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();
};
  1. Constructor function

  2. .prototype method

  3. .constructor method

  4. 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

Made with Slides.com