这篇文章讲解prototype,__proto__和prototype chain三者到底是什么以及三者之间的关系。我们先来看一段代码:
function Dog() {}
Dog.prototype.legsCount = 4;
Dog.prototype.bark = function () {
console.log('wang, wang, wang');
};
let dog = new Dog();
console.log(dog.bark()); //'wang, wang, wang'
console.dir(Dog);
console.dir(dog);
最后两行代码的打印结果为:
我们打印了Dog,它是一个function;我们打印了dog,它是一个object。首先我们看到:
1: prototype本身是一个对象字面量类型的object,__proto__也是一个object
2: prototype是function的属性,__proto__是对象的属性
3: 当我们new一个对象的时候,会把构造函数的prototype赋给这个对象的__proto__。所以他们的内容是一样的。
我们可以看到MDN关于__proto__的解释为:
当实例化一个对象的时候,__proto__用来指向一个作为prototype的对象。也就是说一个对象上的__proto__指向其构造函数的prototype属性。
这也是为什么我们调用dog.bark()可以成功的原因。因为当我们调用dog.bark()的时候,会先在this上去寻找bark()方法,但是我们的构造函数Dog()内部并没有定义bark()方法。这时候,正是因为dog对象有__proto__属性,所以沿着__proto__找到Dog.prototype,从而最终找到了bark()方法。
prototype chain
我们知道JavaScript的继承是基于原型的继承。而每一个对象都有一个__proto__属性,它指向其构造函数的prototype属性,而prototype本身也是一个对象,它也有自己的__proto__属性,而这个__proto__属性又指向。。。。,所以这样层曾上源,就形成了一个类似链表的关系,这个关系就是我们的prototype chain也就是原型链。
所有对象原型链的终点都是Object.prototype, 最终指向null。
原型链的特点有哪些?
1: 实例化对象之后再修改构造函数的prototype属性
function Dog() {}
Dog.prototype.legsCount = 4;
Dog.prototype.bark = function () {
console.log('wang, wang, wang');
};
let dog = new Dog();
console.log(dog.legsCount); //4
//修改prototype
Dog.prototype = {
legsCount: 3
};
console.log(dog.legsCount); //4
以一个新对象的方式重写prototype之后,已经实例化的对象的__proto__并不会改变,因为它不会再重新指向一个新的对象。
以一个新对象的方式重写prototype这种方式也还是危险的。因为prototype作为每个函数的默认属性,它本身还有一个constructor属性prototype.constructor。以一个新对象的方式重写prototype,会抹去prototype.constructor属性。
2: 当原型链上的属性为对象类型时,会造成数据污染
function Dog() {}
Dog.prototype.color = ['black', 'white'];
Dog.prototype.bark = function () {
console.log('wang, wang, wang');
};
let dog1 = new Dog();
let dog2 = new Dog();
console.log(dog1.color);// ["black", "white"]
console.log(dog2.color);// ["black", "white"]
dog1.color.push('red');
console.log(dog1.color);// ["black", "white", "red"]
console.log(dog2.color);// ["black", "white", "red"]
在这种情况下,当我们改变其中一个对象的属性,其他所有的实例对象都会受影响。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。