构造函数
我们先使用一个构造函数创建一个对象:
function Penson() {
}
const penson = new Penson();
在这个例子中,Penson 是一个构造函数,我们使用 new 创建一个实例对象 penson。
使用 new 操作符创建构造函数的实例的过程:
- 在内存中创建一个新对象;
- 这个新对象内部的__proto__特性被赋值为构造函数的 prototype 属性;
- 构造函数内部的 this 被赋值为这个新对象(即 this 指向的新对象);
- 执行构造函数内部的代码(给新对象添加属性);
- 如果构造函数返回非空对象,则返回该对象。否则返回刚创建的新对象。
prototype
每个函数都会创建一个 prototype 属性,这个属性是一个对象。
function Penson() {
}
Penson.prototype.name = 'lisi'
const penson1 = new Penson();
const penson2 = new Penson();
console.log(penson1.name); // lisi
console.log(penson2.name); // lisi
实际上这个对象就是通过调用构造函数创建的对象的原型,也就是在这个例子中 penson1 和 penson2 的原型,在它上面定义的属性和方法可以被对象实例共享。
无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性指向原型对象。
__proto__
每一个对象上都有个__proto__属性,这个属性可以访问对象的原型。
function Penson() {
}
const penson = new Penson();
console.log(penson.__proto__ === Penson.prototype); // true
constructor
默认情况下,所有原型对象自动获取一个名为 constructor 的属性,指回与之关联的构造函数。
function Penson() {
}
const penson = new Penson();
console.log(Penson.prototype.constructor === Penson); // true
原型层级
当通过对象访问属性的时候,会先搜索这个实例的本身,如果在这个实例上发现了这个属性,则返回对应的值。如果没有找到这个属性,则会沿着指针进入原型对象,一直查找到最顶层。
function Person() {
}
Person.prototype.name = 'Kevin';
var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy 来自实例
delete person.name;
console.log(person.name) // Kevin 来自原型
那么原型的最顶层是什么呢?
默认情况下,所有引用类型都继承自 Object,任何函数的默认原型都是一个 Object 的实例,这意味着这个实例有一个__proto__指向 Object.prototype。所以原型的最顶层是 Object.prototype。
原型链
原型链其基本思想就是通过原型继承多个应用类型的属性和方法。
构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象(prototype),原型有一个属性(constructor)指回构造函数,而实例内部有个指针(__proto__)指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也是有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。
function SuperType () {
this.name = 'lisi';
}
function SubType () {}
// 继承 SuperType
SubType.prototype = new SuperType();
const instance = new SubType();
console.log(instance.name); // lisi
console.log(instance.__proto__ === SubType.prototype); // true
console.log(SubType.prototype.__proto__ === SuperType.prototype); // true