JavaScript: • What kinds of values exist? • How are values categorized? Structure: 1. Basics: primitives vs. objects, typeof, instanceof. 2. Advanced: typeof and instanceof are not always enough. 3. WTFs: weird aspects of categorization. 4. Best practices 2
everything is an object. Quoting the ECMAScript 5.1 specification: • A primitive value is a member of one of the following built-in types: Undefined, Null, Boolean, Number, and String; • an object is a member of the remaining built-in type Object 5
by reference: unique identity, only equal to itself. > var obj1 = {}; > var obj2 = {}; > obj1 === obj2 false > obj1 === obj1 true Primitive value – compared by value: equal to all values with same content. > var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true 6
object property: • Not accessible from the language, • but influence how it works. • Names are written in [[double square brackets]]. Example: [[Extensible]] – can properties be added? • Check: Object.isExtensible(obj) • Set to false: Object.preventExtensions(obj) 10
between objects. • Object inherits properties of prototype. • 㱺 prototype chain of objects 2. Instance prototype: Value of Point.prototype. • Shared [[Prototype]] of all instances of Point 11 dist ··· function() {···} Point.prototype prototype Point x 0 [[Prototype]] y 7 x 8 [[Prototype]] y 5 p1 p2 2 1 1
(non-inherited) property of all objects. 17 Value [[Class]] arguments 'Arguments' Instances of built-in constructors 'Boolean', 'Number', 'String', 'Array', 'Function', 'RegExp', 'Date', 'Error' Global values 'JSON', 'Math' All other objects 'Object'
var str = Object.prototype.toString.call(x); var match = /^\[object (.*)\]$/.exec(str); if (match) { return match[1]; } else { return str; } } Use case: whenever instanceof doesn’t work (explained later). 19
'undefined' null 'object' (bug) boolean value 'boolean' number value 'number' string value 'string' function 'function' (has property [[Call]]) all other values 'object'
determine whether a value is an array. • Original goal: allow JSON.stringify() to safely detect arrays. • But: generally useful when crossing frames. function isArray(value) { var str = Object.prototype.toString.call(value); return str === '[object Array]'; } 26
{} > typeof Object.prototype 'object' > getClass(Object.prototype) 'Object' But it’s not an instance of Object: > Object.prototype instanceof Object false Why? Can’t be its own prototype: > Object.getPrototypeOf(Object.prototype) null 29
undefined > typeof Function.prototype // has [[Call]] 'function' > getClass(Function.prototype) 'Function' It’s not an instance of Function (can’t be its own prototype): > Function.prototype instanceof Function false 30
property [[PrimitiveValue]]: • Boolean • Number • String • Date Things to note: • Instance prototypes also have [[PrimitiveValue]]. • Method valueOf() returns value of [[PrimitiveValue]]. 35
ECMAScript 5.1 specification: • A Date object contains a Number indicating a particular instant in time to within a millisecond. • Such a Number is called a time value. • A time value may also be NaN, indicating that the Date object does not represent a specific instant of time. • Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC. 39
'Date' It’s an invalid date, wrapping NaN: > Date.prototype Invalid Date > Date.prototype.valueOf() NaN Compare: > new Date(NaN) Invalid Date > new Date(NaN).valueOf() NaN 40
you can. For example, via polymorphism. Don’t do this: function bla(x) { if (x instanceof Foo) { ··· } else if (x instanceof Bar) { ··· } else ··· } Do this: Foo.prototype.bla = function () { ··· }; Bar.prototype.bla = function () { ··· }; ··· 42
avoid categorization: • Use typeof and instanceof • Normally ignore [[Class]] and Array.isArray() Careful with typeof: • 'function' versus 'object' • typeof null 43
“Categorizing values in JavaScript” • Using objects as constructors, not functions: “Prototypes as classes – an introduction to JavaScript inheritance” • “Values” – chapter in “Speaking JavaScript” 47
to their constructor: > function Foo() { } > Foo.prototype.constructor === Foo true > RegExp.prototype.constructor === RegExp true Inherited by instance: find out who created it. > new Foo().constructor [Function: Foo] > /abc/.constructor [Function: RegExp] 50