On Github basarat / this-and-prototype
function Animal(name) { this.name = name; } Animal.prototype.walk = function () { console.log(this.name + ' is walking') }; function Bird() { Animal.apply(this, arguments); } Bird.prototype.__proto__ = Animal.prototype; Bird.prototype.fly = function () { console.log(this.name + ' is flying') }; var animal = new Animal('elephant'); animal.walk(); var bird = new Bird('crow'); bird.walk(); bird.fly();
> "elephant is walking" > "crow is walking" > "crow is flying"
When you do not care about the base class constructor argumentsfunction bar() { console.log(this); } bar();
> window
var foo = {}; function bar() { console.log(this === foo); } foo.bar = bar; foo.bar();
> true
var foo = { bar: 123, bas: function () { console.log('inside this.bar is: ' + this.bar); } } foo.bas();
> "inside this.bar is: 123"
function bar() { console.log('inside this.foo is: ' + this.foo); } var a = { foo: 123, bar: bar }; var b = { foo: 456, bar: bar }; a.bar(); b.bar();
> "inside this.foo is: 123" > "inside this.foo is: 456"
function bar() { console.log('inside this.foo is: ' + this.foo); } var a = { foo: 123 }; a.bar = bar; a.bar();
> "inside this.foo is: 123"
var foo = {}; var bar = {}; console.log(foo.toString == bar.toString);
> true
Are these the same functions? Where are they coming from? (Object.__proto__.toString)var foo = {}; var bar = {}; log(foo.__proto__ == bar.__proto__);
> true
Share the protovar foo = {}; var bar = {}; log(foo.__proto__.toString == bar.__proto__.toString);
> true
var foo = {}; console.log(foo.__proto__); console.log(foo.__proto__.__proto__); console.log('----'); foo.__proto__.bar = 123; console.log(foo.bar); console.log('----'); foo.bar = 456; console.log(foo.bar); console.log('----'); delete foo.bar; console.log(foo.bar);
> {} > null > "----" > 123 > "----" > 456 > "----" > 123
Since proto is shared don't do this!var foo = { __proto__: { __proto__: { __proto__: { bar: 123 } } } }; console.log(foo.bar);
> 123
var objectProto = ({}).__proto__; objectProto.log = function() { console.log(this); }; var foo = { a: 123 }; var bar = { b: 456 }; foo.log(); bar.log();
> {"a":123} > {"b":456}
Now you shouldn't do this deep in your library or you are bound to break other libraries and possibly future JavaScriptfunction Foo() { } console.log(Foo.prototype); // Has a default member console.log(Foo.prototype.constructor === Foo);
> {} > true
Non enumerated constructor propertyfunction Foo() { } // without the new operator console.log(Foo()); // with the new operator console.log(new Foo());
> undefined > {}
function Foo() { this.bar = 123; console.log('Is this global?: '+ (this == window)); } // without the new operator Foo(); console.log(window.bar); // with the new operator var newFoo = new Foo(); console.log(newFoo.bar);
> "Is this global?: true" > 123 > "Is this global?: false" > 123
function Foo() { } var foo = new Foo(); console.log(foo.__proto__ === Foo.prototype);
> true
function Foo() {} Foo.prototype.log = function() { console.log('log'); } var a = new Foo(); var b = new Foo(); a.log(); b.log();
> "log" > "log"
Provides a standard way of creating shared functionsfunction Foo(val) { this.val = val; } Foo.prototype.log = function () { console.log(this.val); } var a = new Foo(1); var b = new Foo(2); a.log(); b.log();
> 1 > 2
Each gets its own `this` and proto (pointing to prototype) is shared we get an efficient class structure. The `this` inside the prototype members is depended upon the calling contextvar foo = { bar: 123, bas: function () { console.log('inside this.bar is: ' + this.bar); } } var bas = foo.bas; bas();
> "inside this.bar is: undefined"
var foo = { bar: 123, bas: function () { console.log('inside this.bar is: ' + this.bar); } } var bas = function () { return foo.bas(); }; bas();
> "inside this.bar is: 123"
var foo = { bar: 123, bas: function () { console.log('inside this.bar is: ' + this.bar); } } var bas = foo.bas.bind(foo); bas();
> "inside this.bar is: 123"
var foo = { bar: 123, }; function bas() { console.log('inside this.bar is: ' + this.bar); } bas.bind(foo)();
> "inside this.bar is: 123"
We use bind because we don't want to mutate the object or the functionvar foo = { bar: 123, }; function bas() { console.log('inside this.bar is: ' + this.bar); } bas.call(foo);
> "inside this.bar is: 123"
var foo = { bar: 123, }; function bas(a, b) { console.log({bar:this.bar, a:a, b:b}); } bas.call(foo, 1, 2);
> {"bar":123,"a":1,"b":2}
Call is needed to create a prototype chain and we will see it nextvar adder = { increment: 1, add: function (value) { return this.increment + value; } } // Want to intercept any call to add e.g. console.log(adder.add(3));
> 4
var adder = { increment: 1, add: function (value) { return this.increment + value; } } var oldAdd = adder.add; adder.add = function(value){ console.log('someone is calling with value: ' + value); return oldAdd.call(adder,value); } // Intercepted! adder.add(3);
> "someone is calling with value: 3"
function foo(a,b,c){ console.log(arguments); } foo('say', 'something'); foo('talk', 'to', 'me');
> {"0":"say","1":"something"} > {"0":"talk","1":"to","2":"me"}
Array like object. Independent of any explicit arguments that you pass in.function foo(a,b,c){ console.log(this); console.log({a:a,b:b,c:c}); } foo.apply({bar:'123'}, ['talk', 'to', 'me']);
> {"bar":"123"} > {"a":"talk","b":"to","c":"me"}
Similar to call except second argument is an array that gets destructured into parameters. First argument is "this" same as callvar adder = { increment: 1, add: function (value) { return this.increment + value; } } var oldAdd = adder.add; adder.add = function(value){ console.log('someone is calling with value: ' + value); return oldAdd.apply(adder,arguments); } // Intercepted! adder.add(3);
> "someone is calling with value: 3"
Just apply the arguments on the old function, and guaranteed to not break any codefunction Animal() { } Animal.prototype.walk = function () { console.log('walk') }; function Bird() { } Bird.prototype.__proto__ = Animal.prototype; var animal = new Animal(); animal.walk(); var bird = new Bird(); bird.walk();
> "walk" > "walk"
function Animal() { } Animal.prototype.walk = function () { console.log('walk') }; function Bird() { } Bird.prototype.__proto__ = Animal.prototype; Bird.prototype.fly = function () { console.log('fly') }; var animal = new Animal(); animal.walk(); var bird = new Bird(); bird.walk(); bird.fly();
> "walk" > "walk" > "fly"
function Animal(name) { this.name = name; } Animal.prototype.walk = function () { console.log(this.name + ' is walking') }; function Bird(name) { } Bird.prototype.__proto__ = Animal.prototype; Bird.prototype.fly = function () { console.log(this.name + ' is flying') }; var animal = new Animal('elephant'); animal.walk(); var bird = new Bird('crow'); bird.walk(); bird.fly();
> "elephant is walking" > "undefined is walking" > "undefined is flying"
Does anybody see the error in this?function Animal(name) { this.name = name; } Animal.prototype.walk = function () { console.log(this.name + ' is walking') }; function Bird(name) { Animal.call(this, name); } Bird.prototype.__proto__ = Animal.prototype; Bird.prototype.fly = function () { console.log(this.name + ' is flying') }; var animal = new Animal('elephant'); animal.walk(); var bird = new Bird('crow'); bird.walk(); bird.fly();
> "elephant is walking" > "crow is walking" > "crow is flying"
function Animal(name) { this.name = name; } Animal.prototype.walk = function () { console.log(this.name + ' is walking') }; function Bird() { Animal.apply(this, arguments); } Bird.prototype.__proto__ = Animal.prototype; Bird.prototype.fly = function () { console.log(this.name + ' is flying') }; var animal = new Animal('elephant'); animal.walk(); var bird = new Bird('crow'); bird.walk(); bird.fly();
> "elephant is walking" > "crow is walking" > "crow is flying"
When you do not care about the base class constructor argumentsthis
prototype
Base.call(this,...args)
Child.prototype.__proto__ = Base.prototype
function Animal() {} Animal.prototype.walk = function () { console.log('walk') }; function Bird() {} Bird.prototype.__proto__ = Animal.prototype; var animal = new Animal(); var bird = new Bird(); console.log(animal.constructor == Animal); console.log(bird.constructor == Bird);
> true > true
Since proto points to prototype and prototype and member constructorfunction Animal() {} function Bird() {} Bird.prototype.__proto__ = Animal.prototype; var animal = new Animal(); // how deep console.log(animal.__proto__.__proto__.__proto__); // tests console.log(animal.__proto__ == Animal.prototype || animal.__proto__.__proto__ == Animal.prototype); console.log(animal.__proto__ == Bird.prototype || animal.__proto__.__proto__ == Bird.prototype); // Would be even longer for bird
> null > true > false
function Animal() {} function Bird() {} Bird.prototype.__proto__ = Animal.prototype; var animal = new Animal(); var bird = new Bird(); console.log(animal instanceof Animal); console.log(animal instanceof Bird); console.log(bird instanceof Bird); console.log(bird instanceof Bird);
> true > false > true > true
function func(){} var arr = []; var str = ''; var bool = true; var num = 123; console.log(func.__proto__ == Function.prototype); console.log(arr.__proto__ == Array.prototype); console.log(str.__proto__ == String.prototype); console.log(bool.__proto__ == Boolean.prototype); console.log(num.__proto__ == Number.prototype);
> true > true > true > true > true
var arr = []; console.log(arr.__proto__ == Array.prototype); console.log(arr.__proto__.__proto__ == Object.prototype); console.log(arr instanceof Array); console.log(arr instanceof Object);
> true > true > true > true
function Animal() {} Animal.prototype.walk = function () { console.log('walk') }; function Bird() {} Bird.prototype.__proto__ = Animal.prototype; var bird = new Bird(); bird.walk();
> "walk"
Anything you already know that can take a prototype and move it to __proto__?function Animal() {} Animal.prototype.walk = function () { console.log('walk') }; function Bird() {} function tmp(){}; tmp.prototype = Animal.prototype; Bird.prototype = new tmp(); var bird = new Bird(); bird.walk();
> "walk"
Deletes our reference to constructorfunction Animal() {} Animal.prototype.walk = function () { console.log('walk') }; function Bird() {} function tmp(){}; tmp.prototype = Animal.prototype; Bird.prototype = new tmp(); Bird.prototype.constructor = Bird; var bird = new Bird(); bird.walk();
> "walk"
function Animal() {} Animal.prototype.walk = function () { console.log('walk') }; function Bird() {} Bird.prototype = Object.create(Animal.prototype); var bird = new Bird(); bird.walk();
> "walk"
function Animal() {} Animal.prototype.walk = function () { console.log('walk') }; function Bird() {} Bird.prototype = Object.create(Animal.prototype, { constructor: { value: Bird, enumerable: false, writable: true, configurable: true } }); var bird = new Bird(); bird.walk();
> "walk"