Creating JavaScript Objects

A Visual Excursion

Into Prototype Chains
Object.prototype
.hasOwnProperty()
.toString()

Function
.arguments
.name
Object
.create()
.keys()
Function.prototype
.call()
.apply()




.prototype
.constructor


.prototype
.constructor

Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
fido
build data object
lassie
build data object
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor

.prototype
.constructor
Creating Objects
{ }, constructor functions, factory functions
Object.create(), class
Relating Objects
Prototype chains
Inheritance - traditional and modern
Instantiation with new
Composition with Object.assign()
TC39
Brendan Eich
@brendaneich
Douglas Crockford
crockford.com
Kyle Simpson
@getify
Eric Elliott
@_ericelliott
Mattias Johansson
@mpjme
Influences





The Origins of JavaScript
ES1 (1997)
2 object creation approaches:
Manual
Object literal
{ }
Automated
constructor functions
& new
also:
factory functions
(discuss later)
The Origins of JavaScript
2 object creation approaches:
Manual
Object literal
{ }
Automated
constructor functions
& new
The Origins of JavaScript
Object Literal (manual)
var fido = {
name: 'Fido',
favoriteThings: ['food', 'fetch'],
bark: function () {...},
howl: function () {...}
};
Constructor Function & new (automated)
function Dog(name, faves) {
this.name = name;
this.favoriteThings = faves;
this.bark = function () {...};
this.howl = function () {...};
};
var faves = ['food', 'fetch']
var fido = new Dog('Fido', faves);
var lassie = new Dog('Lassie', faves);
function Dog(name, faves) {
this.name = name;
this.favoriteThings = faves;
};
Dog.prototype.bark = function () {...};
Dog.prototype.howl = function () {...};
var faves = ['food', 'fetch'];
var fido = new Dog('Fido', faves);
var lassie = new Dog('Lassie', faves);
Constructor Function & new (automated)
But what about inheritance?

function Animal() {...}
Animal.prototype.poop = function () {...};
function Dog() {
Animal.call(this);
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {...};
var fido = new Dog(...);
var lassie = new Dog(...);
Classical Inheritance Pattern
Modern JavaScript Inheritance
The Evolution
Modern JavaScript Inheritance
Constructor functions spawned some abstractions
Object.create()
ES5 (2009)
class, extends
ES6 (2015)
Built-in JavaScript Objects
Array
Object
Function
ES5
(Properties of the global object)
Built-in JavaScript Objects
Array
ArrayBuffer
DataView
EvalError
Float32Array
Float64Array
Int8Array
ReferenceError
Set
Symbol
SyntaxError
TypeError
Uint8Array
Date
Math
Object
Function
String
ES5
ES6
Int16Array
Int32Array
Map
Proxy
Promise
RangeError
Uint8ClampedArray
Uint16Array
Uint32Array
URIError
WeakMap
WeakSet
Number
JSON
Reflect
Boolean
Error
RegExp
(Properties of the global object)
Laying the Foundation
All objects ultimately
delegate to Object.prototype
Laying the Foundation
All objects ultimately
delegate to Object.prototype
(except Object.prototype itself)
All objects ultimately
delegate to Object.prototype
Laying the Foundation
Inheritance vs. Delegation
Inheritance:
COPY properties FROM
Delegation:
LINK properties TO

Some COPY mechanisms:
-
factory functions,
-
Object.assign(),
-
new
Inheritance:
COPY properties FROM
Delegation:
LINK properties TO

Some LINK mechanisms:
-
{ },
-
Object.create(),
-
new
Inheritance vs. Delegation
All objects ultimately
delegate to Object.prototype
Object.prototype
.hasOwnProperty()
.isPrototypeOf()
.propertyIsEnumerable()
.toLocalString()
.toString()
.valueOf()
.__defineGetter__()
.__defineSetter__()
.__lookupGetter__()
.__lookupSetter__()
get __proto__()
set __proto__()
.constructor()

The base Object.prototype
Object.prototype
.hasOwnProperty()
.isPrototypeOf()
.propertyIsEnumerable()
.toLocalString()
.toString()
.valueOf()
.__defineGetter__()
.__defineSetter__()
.__lookupGetter__()
.__lookupSetter__()
get __proto__()
set __proto__()
.constructor()
Object.prototype
.hasOwnProperty()
.toString()
Object.prototype
.hasOwnProperty()
.toString()
Creating an Array
Intro to Diagrams
Object.prototype
.hasOwnProperty()
.toString()
Function.prototype
.apply()
.bind()
.call()
.toString()
Array.prototype
.concat()
.filter()
.forEach()
.join()
.map()
.pop()
.push()
.shift()
...
String.prototype
.charAt()
.match()
.replace()
.split()
.slice()
.substr()
.toUpperCase()
.toLowerCase()
...



.constructor()
.arguments
.length
.name
.__proto__
.constructor()
.length
.__proto__
.constructor()
.length
.__proto__
Object.prototype
.hasOwnProperty()
.toString()
Function.prototype
.apply()
.bind()
.call()
.toString()
Array.prototype
.concat()
.filter()
.forEach()
.join()
.map()
.pop()
.push()
.shift()
...


.constructor()
.arguments
.length
.name
.__proto__
.constructor()
.length
.__proto__
Object.prototype
.hasOwnProperty()
.toString()


Function.prototype
.apply()
.bind()
Array.prototype
.forEach()
.push()
Object.prototype
.hasOwnProperty()
.toString()
Function.prototype
.apply()
.bind()
Array.prototype
.forEach()
.push()


Array
.arguments
.length

myArr
[0]
[1]
.length

var myArr = new Array('a', 'b');
var myArr = ['a', 'b'];
is equivalent to
.prototype
.constructor
Object.prototype
.hasOwnProperty()
.toString()
Array.prototype
.forEach()
.push()

myArr
[0]
[1]
.length

var myArr = ['a', 'b'];
Object.prototype
.hasOwnProperty()
.toString()
Array.prototype
.forEach()
.push()
myArr
[0]
[1]
.length






Object.prototype
.hasOwnProperty()
.toString()
Array.prototype
.forEach()
.push()
myArr
[0]
[1]
.length






Object.prototype
.hasOwnProperty()
.toString()
Array.prototype
.forEach()
.push()
myArr
[0]
[1]
.length







Navigating the Prototype Chain
.__proto__
.prototype
.constructor
Object.prototype
.hasOwnProperty()
.toString()
Function.prototype
.apply()
.bind()
.call()
.toString()
Array.prototype
.concat()
.filter()
.forEach()
.join()
.map()
.pop()
.push()
.shift()
...
String.prototype
.charAt()
.match()
.replace()
.split()
.slice()
.substr()
.toUpperCase()
.toLowerCase()
...



.constructor()
.arguments
.length
.name
.__proto__
.constructor()
.length
.__proto__
.constructor()
.length
.__proto__
Object.prototype
.hasOwnProperty()
.toString()
Function.prototype
.apply()
.bind()
.call()
.toString()

.constructor()
.arguments
.length
.name
.__proto__
Object.prototype
.hasOwnProperty()
.toString()
Function.prototype
.apply()
.bind()

Function
.arguments
.caller
.length
.name
Object
.create()
.keys()
...
.__proto__
.prototype
.name
...
.__proto__
.prototype


Object.prototype
.hasOwnProperty()
.toString()

Function
.arguments
.name
Object
.create()
.keys()
Function.prototype
.call()
.apply()



.prototype

.prototype

== "prototype chain" == [[Prototype]]
== .__proto__
== Object.getPrototypeOf()
not to be confused with .prototype
Object.prototype
.hasOwnProperty()
.toString()

Function
.arguments
.name
Object
.create()
.keys()
Function.prototype
.call()
.apply()




.prototype
.constructor


.prototype
.constructor

.__proto__ - property of all objects
.prototype - property of all constructor functions
.constructor - property of all prototype objects
( except Object.create(null) )
Object.prototype.constructor === Object // true
Function.prototype.constructor === Function // true
Function.constructor.prototype ===
Function.prototype





Custom Objects
Automation (constructor & new)
Inheritance (classical)
Object.prototype
.hasOwnProperty()
.toString()



Animal
.arguments
.name
Animal.prototype
Object
.create()
.keys()
Function.prototype
.call()
.apply()




.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
Function
.arguments
.name
function Animal() {}
var Animal = function () {};
var Animal = new Function();
is roughly equivalent to
is roughly equivalent to
// Animal.name === "Animal"
// Animal.name === ""
// Animal.name === "anonymous"
Object.prototype
.hasOwnProperty()
.toString()



Animal
.arguments
.name
Animal.prototype
Object
.create()
.keys()
Function.prototype
.call()
.apply()




.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
Function
.arguments
.name
function Animal() {}
Animal.prototype.poop = function () {};
.poop()
Object.prototype
.hasOwnProperty()
.toString()




Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
Object
.create()
.keys()
Function.prototype
.call()
.apply()




.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Function
.arguments
.name
function Dog() {}

Object.prototype
.hasOwnProperty()
.toString()




Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Object
.create()
.keys()
Function.prototype
.call()
.apply()





.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {};
function Dog() {
Animal.call(this);
}
Function
.arguments
.name
function Dog() {}
Dog.prototype
.bark()
Object.prototype
.hasOwnProperty()
.toString()




Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
fido
build data object
lassie
build data object
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor

.prototype
.constructor
Function
.arguments
.name
var fido = new Dog();
var lassie = new Dog():
function Animal() {...}
Animal.prototype.poop = function () {...};
function Dog() {
Animal.call(this);
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {...};
var fido = new Dog(...);
var lassie = new Dog(...);
Classical Inheritance Pattern
Modern JavaScript Inheritance
Constructor functions spawned some abstractions
Object.create()
ES5 (2009)
class, extends
ES6 (2015)
Simple
Easy
Modern JavaScript Inheritance
{ } & Object.create()
Option 1
Object.create()
What does Object.create() do?
What does new do?
Object.create()
1. Create
2. Add
3. Bind
4. Return
an empty object
link up the prototype chain
to the constructor function
if constructor function returns undefined
{ }

this
this
What does new do?
Object.create()
an empty object
link up the prototype chain
to the constructor function
if constructor function returns undefined
What does Object.create() do?
1. Create
2. Add
3. Bind
4. Return
{ }

this
this
Object.create()
What does Object.create() do?
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
1. Create
2. Add
{ }

Object.create()
What does Object.create() abstract away?
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
Constructor function
.prototype method
.constructor method
new operator
Object.create()
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
fido
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
var fido = new Dog();
{ }

this
this
1. Create
2. Add
3. Bind
4. Return
fido
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
fido
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Steps toward the Object.create() abstraction
Step 1: Remove function noise (and related properties)
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
Animal
.poop()
Dog
.arguments
.name
Dog
.bark()
fido
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Steps toward the Object.create() abstraction
Step 1: Remove function noise (and related properties)
Step 2: Chop off the .prototype name
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
animal
.poop()
Dog
.arguments
.name
dog
.bark()
fido
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Steps toward the Object.create() abstraction
Step 1: Remove function noise (and related properties)
Step 2: Chop off the .prototype name
Step 3: Use standard camelCase
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
animal
.poop()
Dog
.arguments
.name
dog
.bark()
fido
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Steps toward the Object.create() abstraction
Step 1: Remove function noise (and related properties)
Step 2: Chop off the .prototype name
Step 3: Use standard camelCase
Step 4: Only use plain objects
Object.prototype
.hasOwnProperty()
.toString()

animal
.poop()
dog
.bark()
fido


var fido = Object.create(dog);
{ }

1. Create
2. Add
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
fido
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor
.prototype
.constructor
Parent
prototype
Child
prototype
Typed
Instance
Parent
"class"
Child
"class"
Using constructor functions
Object.prototype
.hasOwnProperty()
.toString()

animal
.poop()
dog
.bark()
fido


object
object
object
Using Object.create()
var animal = {
poop: function () {...}
};
var dog = _.create(animal, {
bark: function () {...}
};
var fido = _.create(dog);
var lassie = _.create(dog);
function Animal() {...}
Animal.prototype.poop = function () {...};
function Dog() {
Animal.call(this);
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {...};
var fido = new Dog(...);
var lassie = new Dog(...);
Object.create()
Constructor Function
Modern JavaScript Inheritance
class, extends, new
Option 2
function Animal() {...}
Animal.prototype.poop = function () {...};
function Dog() {
Animal.call(this);
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {...};
var fido = new Dog(...);
var lassie = new Dog(...);
class Animal {
poop() {...}
}
class Dog extends Animal {
bark() {...}
}
var fido = new Dog();
var lassie = new Dog();
Class
Constructor Function
Modern JavaScript Inheritance
Constructor functions spawned some abstractions
Object.create()
ES5 (2009)
class, extends
ES6 (2015)
Which approach?
class Animal {
poop() {...}
}
class Dog extends Animal {
bark() {...}
}
var fido = new Dog();
var lassie = new Dog();
class
var animal = {
poop() {...}
};
var dog = _.create(animal, {
bark() {...}
};
var fido = _.create(dog);
var lassie = _.create(dog);
Object.create()
Syntax


class
Object.create()
console.dir()
class
Object.create()

Mental Model

var fido = Object.create(dog);
var fido = new Dog;
2.3 microseconds to create 1 dog
0.23 seconds to create 100,000 dogs
1.6 microseconds to create 1 dog
0.16 seconds to create 100,000 dogs
Factory functions & performance
class
Object.create()
Performance
if (fido instanceof Dog) {
doSomething();
}
if (fido.bark) {
doSomething();
}
if (fido.constructor === Dog) {
doSomething();
}
if (fido.isDog) {
doSomething();
}
class
Object.create()
Polymorphism
-
Similar to other OO languages
-
Huge momentum
-
Frameworks
-
TC39 successor to constructor functions
-
Not as common
-
Handicapped by JS history & evolution
-
Devs did not take the language seriously and did not learn it
class
Object.create()
Prevalence
Polymorphism
Performance
Mental Model
console.dir()
Syntax
Prevalence
Memory
Private Variables
Fixed Taxonomy
Simple
Easy
Object Relation
class, extends & new
Object.create()
Object Creation
class & new
Object Literals
Composition
"Favor composition over inheritance"
-- Gang of Four

Murder Robot Dog Dilemma
Object.prototype
.hasOwnProperty()
.toString()
Animal.prototype
.poop()
Dog.prototype
.bark()
fido


Object.create()

animal
.poop()
dog
.bark()
fido



class
Inheritance (delegation)
animal
.poop()


dog
.bark()

fido
.poop()
.bark()
Object.assign()
Composition

Object.prototype
.hasOwnProperty()
.toString()
Factory Functions
Factory: a function that creates and returns an object
function animal() {
return {
eat: function () {...},
poop: function () {...}
};
}
function animal() {
return {
eat: function () {...},
poop: function () {...}
};
}
function animal(isCarnivore) {
return {
isCarnivore: isCarnivore,
eat: function () {...},
poop: function () {...}
};
}
function animal(isCarnivore) {
return {
isCarnivore: isCarnivore,
eat: function () {...},
poop: function () {...}
};
}
function animal(isCarnivore) {
var privateVariable1 = ...,
privateVariable2 = ...;
return {
isCarnivore: isCarnivore,
eat: function () {...},
poop: function () {...}
};
}
function animal(isCarnivore) {
var privateVariable1 = ...,
privateVariable2 = ...;
return {
isCarnivore: isCarnivore,
eat: function () {...},
poop: function () {...}
};
function privateFunction1() {...}
function privateFunction2() {...}
}
Angular 1 factory
function animal(isCarnivore) {
var privateVariable1 = ...,
privateVariable2 = ...;
return {
isCarnivore: isCarnivore,
eat: function () {...},
poop: function () {...}
};
function privateFunction1() {...}
function privateFunction2() {...}
}
~Revealing Module Pattern
function animal() {
return {
eat: function () {...},
poop: function () {...}
};
}
function dog() {
return {
bark: function () {...},
howl: function () {...}
};
}
var fido = Object.assign({}, animal(), dog());
Polymorphism
Performance
Mental Model
console.dir()
Syntax
Prevalence
Memory
Private Variables
Fixed Taxonomy
Object Relation
class, extends & new
Object.create()
Object.assign()
Object Creation
class & new
Object Literals
Factory Functions
Object.prototype
.hasOwnProperty()
.toString()



Function
.arguments
.name

Animal
.arguments
.name
Animal.prototype
.poop()
Dog
.arguments
.name
Dog.prototype
.bark()
fido
build data object
lassie
build data object
Object
.create()
.keys()
Function.prototype
.call()
.apply()






.prototype
.constructor


.prototype
.constructor
.prototype
.constructor

.prototype
.constructor
"Docendo discimus"
-- Seneca the Younger (4 BC - 65 AD)
"By teaching, we learn"
Modern derivation
Creating JavaScript Objects
By Bob Holben
Creating JavaScript Objects
- 2,807