js原型链与new关键词

162 阅读2分钟

介绍

js的原型链看上去比较难理解,但实际上可以把它当作一个链表,链表上的每个节点就是一个js的对象,节点的指针就是对象的隐式原型__proto__, 它们的连接方式就是对象的隐式原型__proto__等于构造函数的显式原型对象prototype。而这个链表的尾部就是null。我们经常使用的new关键词其实就是把构造函数的显示原型对象prototype赋给一个新对象的隐式原型,相当于在这个链表头部上加了一个节点,所以这个新对象能获得原型链上的原型方法,然后再使用call方法获取构造函数本身的属性,最后这个对象就成为了这个构造函数的实例对象。

一、原型链

  1. 只有函数才有prototype(显式属性)
  2. 所有对象都有__proto__((隐式属性)
  3. A是a的构造函数则 a.__proto __= A.prototype,a里找不到的属性会去A的prototype里找
  4. A.prototype是对象,里面有constructor这个属性
  5. 根据a.__proto __= A.prototype,a.__proto __ 也是对象,所有对象都没有prototype,只有__proto __
  6. instanceof 就是通过原型链判断的

牢记以上几点规则,再来看图

v2-bb0d6ad52869b8a51c11a55bcea60da9_1440w.png

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`);

以上代码按步骤解释即:

  1. 创建空对象
  2. 将构造函数的prototye赋值给这个空对象的__proto__
  3. 使用call方法将Fn的this改为obj,obj继承到Fn的属性
  4. 返回obj对象