1. 原型
prototype
每个函数都有一个 prototype 属性(PS:prototype 是函数才会有的属性噢),比如:
function Car() {}
Car.prototype.color = 'black';
var car1 = new Car();
var car2 = new Car();
console.log(car1.color) // black
console.log(car2.color) // black
那【Car】这个函数的 prototype 属性到底指向的是什么,又为什么需要这样的设计?我们可以把它打印出来看看:
可以看出,prototype 属性指向了一个对象,这个对象就是我们常说的原型。这个对象在 JS 设计之初,是专门设计用来存储对象共享的属性。例如上面那个例子,car1 和 car2 两个对象,它们通过 prototype 属性拥有了完全相同的 color (如果修改了 prototype 的color,所有 Car 对象的 color 都会被修改)。
因此,我们可以得出一个构造函数和实例原型的关系图:
那么,我们该怎么表示实例原型与实例对象,也就是 car1 和 Car.prototype 之间的关系呢?这时候我们就要讲到第二个属性:proto
proto
在 JS 所有对象中(除了null)都具有的一个属性,叫 proto,这个属性会指向该对象的原型。
console.log(car1.__proto__ === Car.prototype); // true
于是更新构造函数和实例原型的关系图如下:
既然实例对象和构造函数都可以指向原型,那么实例原型是否有属性指向构造函数或者实例对象呢?
实例原型没有属性指向实例对象,因为一个构造函数可以生成多个实例。
实例原型有属性 constructor 指向构造函数。
constructor
每个实例原型都有一个 constructor 属性指向关联的构造函数:
console.log(Car === Car.prototype.constructor); // true
于是更新构造函数和实例原型的关系图如下:
综上所述,构造函数、实例原型、和实例对象之间的关系如上图,用代码表示为:
function Car() {}
Car.prototype.color = 'black';
var car1 = new Car();
var car2 = new Car();
console.log(car1.color) // black
console.log(car2.color) // black
console.log(car1.__proto__ == Car.prototype) // true
console.log(Car.prototype.constructor == Car) // true
2. 原型链
原型链是什么?其实理解了原型,原型链不是一个十分复杂的概念。
上面讲到,每个对象都有一个 proto 属性指向实例原型,那么这个实例原型也有 proto 指向它的实例原型,直到指向 null,这些相互关联的原型组成的链状结构就是原型链。
我们可以根据上面的 Car 分析一下它的原型链,先打印一下 Car.prototype:
可以看到,Car 的实例原型的 proto 指向的是 Object 的 prototype,而 Object.prototype 的 proto 又指向了null(null 没有构造函数),这就是它的原型链。因此,更新关系图如下:
参考资料: