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"