看完很快,稍加思索还得一会哈哈
什么是原型:
每个函数都会创建一个 prototype 属性,这个属性是一个对象。而这个对象就是通过调用构造函数创建的对象的原型。
为什么要使用原型:
使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。
//例如
function Person() {}
Person.prototype.name = "Nicholas";
let person1 = new Person();
person1.sayName(); // "Nicholas"
注意的是,我们的Person是构造函数(函数)。而Person.prototype
是原型对象(对象)。所谓原型就是通过函数的prototype属性来获取原型对象。
构造函数.prototype -> 原型对象
所有原型对象自动获得一个名为 constructor
的属性,指回与之关联的构造函数。所以可以得到关系
原型对象.constructor => 构造函数
首先我们要明白构造函数、原型对象和实例是 3 个完全不同的对象。实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有:如下所示
person1 = new Person(),
person2 = new Person();
/**
* 构造函数、原型对象和实例
* 是 3 个完全不同的对象:
*/
console.log(person1 !== Person); // true
console.log(person1 !== Person.prototype); // true
console.log(Person.prototype !== Person); // true
每次调用构造函数创建一个新实例,这个实例的内部[[Prototype]]指针
就会被赋值为构造函数的原型对象。
但 Firefox、Safari 和 Chrome会在每个对象上暴露__proto__
属性,通过这个属性可以访问对象的原型。
实例对象通过这个可以直接指向原型对象,如下
/**
* 实例通过__proto__链接到原型对象,
* 它实际上指向隐藏特性[[Prototype]]
*
* 构造函数通过 prototype 属性链接到原型对象
*
* 实例与构造函数没有直接联系,与原型对象有直接联系
*/
console.log(person1.__proto__ === Person.prototype); // true
conosle.log(person1.__proto__.constructor === Person); // true
可以观察到实例对象person1通过__proto__
可以得到原型对象,然后通过原型对象的constructor
属性又可以得到构造函数。
实例对象.
__proto__
-> 原型对象
在前面,我们已经讲了原型对象也是一个对象,既然是对象,我们知道object是个祖宗类。所以我们原型对象的__proto__
属性指向的就是object的原型对象object.prototype
。
原型对象.proto -> object.prototype
/**
* 正常的原型链都会终止于 Object 的原型对象
* 补充一点,Object 原型的原型是 null
*/
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Person.prototype.__proto__.constructor === Object); // true
console.log(Person.prototype.__proto__.__proto__ === null); // true