37
JS Level Up: Prototypes Take your JavaScript to the next level

JS Level Up: Prototypes

Embed Size (px)

Citation preview

JS Level Up: Prototypes Take your JavaScript to the next level

JavaScript has a very simple type system

http://bit.ly/1QZ4QVX

JavaScript has a very simple type system

http://bit.ly/1QZ4QVX

1. Primitive Types

2. Object Types

Primitive Types

http://bit.ly/1QZ4QVX

1. String

2. Number

3. Boolean

4. Undefined

5. Null

Object Types

http://bit.ly/1QZ4QVX

1. Object

2. Array

3. Function

* Some like to say everything is an object

Everything in JavaScript can act like an object

Every object in JavaScript links to a prototype object

console.log( “”.__proto__ === String.prototype ); // true!

That, if you’re wondering, is pronounced “dunder proto"

Constructor Functions leverage this link to create reusable interfaces (not UI)

Constructor Functions

• Constructor functions are used to create new objects.

• Very useful when creating multiple instances of a particular object

var list = new Array( 1, 2, 3 );

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

var list = new Array( 1, 2, 3 );

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

Where did that push method come from?

var list = new Array( 1, 2, 3 );

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

Where did that push method come from?

It’s inherited from Array.prototype!

var list = new Array( 1, 2,

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

new Array( 1, 2, 3 )

var list = new Array( 1, 2,

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

new Array( 1, 2, 3 )

A function is a block of code that is defined once but can be invoked any number of times.

var list = new Array( 1, 2,

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

new Array( 1, 2, 3 )

Each function has a special thiskeyword, the functions invocation context

var list = new Array( 1, 2,

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

new Array( 1, 2, 3 )

Functions used to initialize a new object are called Constructor Functions

var list = new Array( 1, 2,

list.push( 4 );

// list === [ 1, 2, 3, 4 ]

new Array( 1, 2, 3 )

All functions - because they are objects - havea prototype property that is a link to the prototype object

So let’s look at an example

function Person ( name ) {this.name = name;

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = new Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

Constructor Functions

function Person ( name ) {this.name = name;

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = new Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

• Common convention is to uppercase the first character of the function name of a constructor function

• The context of this within the constructor function is the newly created object

• No return statement required

Constructor Functions

function Person ( name ) {this.name = name;

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = new Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

• We can add custom functionality to the prototype object of our function

• This added functionality is inherited by each instance of our constructor we create

• The context of methods added to the prototype object are the created instance itself

Constructor Functions

function Person ( name ) {this.name = name;

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = new Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

• A new object instance is created by invoking a constructor function with the new operator

• Here, we create vernon and assign is the specified values as its properties

• So, the value of vernon.name would be “Vernon”

• We can now create as many “Person’s” as we want, and they will all be able to sayHi!

Constructor Functions

function Person ( name ) {this.name = name; // this === window

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

• Invoking a constructor function without the new operator has unintended side effects

• The context of the newly created object in the case of a missing new, is window

Constructor Functions

function Person ( name ) {if ( !( this instanceof Person ) ) {

return new Person( name );} this.name = name;

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = new Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

• We can protect ourselves from these side effects by using the instanceof operator

• instanceof tests whether an object has the prototype property of a constructor in its prototype chain

• Here, we test the context of our constructor and return the proper invocation if needed

function Person ( name ) {this.name = name;

}

Person.prototype.sayHi = function () {console.log( “Hi! I’m “, this.name );

}

var vernon = new Person( “Vernon” );

vernon.sayHi(); // Hi! I’m Vernon

Person.prototype.sayBye = function () {console.log( “Bye!” );

}

vernon.sayBye(); // Bye!

function Person ( name ) {this.name = name;

}

Person.prototype.sayHi = function () {console.log(

}

var vernon = new Person(

vernon.sayHi(); // Hi! I’m Vernon

Personconsole

}

vernon

Prototypal Inheritance

Prototypal Inheritance

• All inheritance in JavaScript is through the prototype object

• Object.prototype does not inherit any properties

• All built-in constructors inherit from Object.prototype

• Best understood by looking at an example

http://bit.ly/1MNdeGU

Creates a new object with the specified prototype object and properties

Prototypal Inheritance | Object.create()

var obj = {}; // Inherits from Object.prototypeobj.foo = "bar"; // has an own property of foo

var obj2 = Object.create( obj ); // Inherits from obj and Object.prototypeobj2.bar = "baz"; // has an own property of bar

var obj3 = Object.create( obj2 ); // Inherits from obj, obj2 and Object.prototypeobj3.baz = "thud"; // has an own property of baz

console.log( obj3.foo ); // barconsole.log( obj3.bar ); // bazconsole.log( obj3.hasOwnProperty( “foo” ) ); // false

var obj = {}; // Inherits from Object.prototypeobj.foo = "bar"; // has an own property of foo

var obj2 = Object.createobj2.bar = "baz"; // has an own property of bar

var obj3 = Object.createobj3.baz = "thud"; // has an own property of baz

console.log( obj3.foo ); // barconsole.log( obj3.bar ); // bazconsole.log( obj3.hasOwnProperty(

This inheritance lookup is done against the prototype chain

Prototypal Inheritance

var obj = {}; // Inherits from Object.prototypeobj.foo = "bar"; // has an own property of foo

var obj2 = Object.createobj2.bar = "baz"; // has an own property of bar

var obj3 = Object.createobj3.baz = "thud"; // has an own property of baz

console.log( obj3.foo ); // barconsole.log( obj3.bar ); // bazconsole.log( obj3.hasOwnProperty(

We can leverage this to create scalable objects

Prototypal Inheritance

var obj = {}; // Inherits from Object.prototypeobj.foo = "bar"; // has an own property of foo

var obj2 = Object.createobj2.bar = "baz"; // has an own property of bar

var obj3 = Object.createobj3.baz = "thud"; // has an own property of baz

console.log( obj3.foo ); // barconsole.log( obj3.bar ); // bazconsole.log( obj3.hasOwnProperty(

Let’s look at another example

Prototypal Inheritance

var User = function( details ) { details = details || {}; this.firstname = details.firstname; this.lastname = details.lastname;

};

Let’s create a User constructor function

var User = function( details ) { details = details || {}; this.firstname = details.firstname; this.lastname = details.lastname;

};

User.prototype.logName = function() {console.log( this.firstname + " " + this.lastname );

};

Now, we’ll add a logName method to the prototype

var User = function( details ) { details = details || {}; this.firstname = details.firstname; this.lastname = details.lastname;

};

User.prototype.logName = function() {console.log( this.firstname + " " + this.lastname );

};

var john = new User( { firstname: "John", lastname: "Doe" } );

Finally, let’s create an instance of a User

var User = function( details ) { … };

User.prototype.logName = function() { … };

var john = new User( { firstname: "John", lastname: "Doe" } );

var Admin = function( details ) { this.firstname = details.firstname; this.lastname = details.lastname; this.fullaccess = true;};

Now, let’s add an Admin constructor function

var User = function( details ) { … };

User.prototype.logName = function() { … };

var john = new User( { firstname: "John", lastname: "Doe" } );

var Admin = function( details ) { this.firstname = details.firstname; this.lastname = details.lastname; this.fullaccess = true;};

Admin.prototype = new User();Admin.prototype.constructor = Admin;

We can inherit functionality from our User function

var User = function( details ) { … };

User.prototype.logName = function() { … };

var john = new User( { firstname: "John", lastname: "Doe" } );

var Admin = function( details ) { this.firstname = details.firstname; this.lastname = details.lastname; this.fullaccess = true;};

Admin.prototype = new User();Admin.prototype.constructor = Admin;

var jane = new Admin( { firstname: "Jane", lastname: "Doe" } );

jane.logName(); // Jane Doe

Our new Admin instance has access to logName

Inheritance and the prototype chain

• ECMAScript 6 introduces a new set of keywords implementing “classes” in JavaScript. It’s important to note that these classes are still prototype based.

• Object.create is another method of inheritance

• Do not extend native prototypes unless you are back porting newer features of JavaScript to older engines

http://bit.ly/1MNdeGU

Thanks for listening! @vernonk