介绍
js的原型链看上去比较难理解,但实际上可以把它当作一个链表,链表上的每个节点就是一个js的对象,节点的指针就是对象的隐式原型__proto__, 它们的连接方式就是对象的隐式原型__proto__等于构造函数的显式原型对象prototype。而这个链表的尾部就是null。我们经常使用的new关键词其实就是把构造函数的显示原型对象prototype赋给一个新对象的隐式原型,相当于在这个链表头部上加了一个节点,所以这个新对象能获得原型链上的原型方法,然后再使用call方法获取构造函数本身的属性,最后这个对象就成为了这个构造函数的实例对象。
一、原型链
- 只有函数才有prototype(显式属性)
- 所有对象都有__proto__((隐式属性)
- A是a的构造函数则 a.__proto __= A.prototype,a里找不到的属性会去A的prototype里找
- A.prototype是对象,里面有constructor这个属性
- 根据a.__proto __= A.prototype,a.__proto __ 也是对象,所有对象都没有prototype,只有__proto __
- instanceof 就是通过原型链判断的
牢记以上几点规则,再来看图
let f1 = new Foo();
f1.__proto__ === Foo.prototype; // 规则3
Foo.prototype.constructor === Foo; // 规则4
Foo.prototype.__proto__ === Object.prototype; // Foo.prototype是对象,所有对象的隐形属性__proto__最终都指向Object方法的原型。
Object.prototype.constructor === Object;
Object.prototype.__proto__ === null; // 以上===结果都为true
根据以上列出的几点规则,我们列出了图中f1的原型链,最终指向了null。 如果我们按照以上方式去列出o1的原型链,也会发现最终也是走到null。
Foo()函数本身其实也是一个对象,那它也有__proto__属性。Foo()函数的构造函数实际上是Function(), 根据a.__proto __ === A.protoType,所以有:
Foo.__proto__ === Function.prototype;
Function.prototype.__proto__ === Object.prototype; // 还是指向了Object
..... // 原型链末端最终还是null
二、new关键词
假如我们有构造函数Fn。
function Fn(name){
this.name = name;
}
const f = new Fn(`chuck`);
以上通过new关键词去创建Fn实例可以等效于以下代码:
function(name){
let obj = {};
obj.__proto__ = Fn.prototype; // 这也是为什么`A是a的构造函数则
a.__proto__= A.prototype;
Fn.call(obj, name);
return obj;
}(`chuck`);
以上代码按步骤解释即:
- 创建空对象
- 将构造函数的prototye赋值给这个空对象的__proto__
- 使用call方法将Fn的this改为obj,obj继承到Fn的属性
- 返回obj对象