乐趣区

prototype-VS-proto-VS-prototype-chain

这篇文章讲解 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 类型的属性,对象并没有 prototype 属性
3: 对象 dog 的__proto__的内容就是其构造函数 Dog()的 prototype 的内容

我们可以看到 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"]

在这种情况下,当我们改变其中一个对象的属性,其他所有的实例对象都会受影响。

退出移动版