JS对象继承与原型链

对象继承 VS 类继承

在 class-based 的面向对象的世界里,要出现对象,必须先有类。类之间能够继承,类再使用 new 操做建立出实体,父子对象之间的继承体如今父类和子类上。你不能说 对象 a 继承了对象 b,只能说 class A 继承了 class B,而后他们各自有一个实例a、b。app

JS中实现的是原型继承。在 prototype-based 的面向对象的世界里,继承是对象之间的事情,就像现实世界里,儿子继承父亲,不必非得整出父亲类、儿子类。你有一个对象 a,经过 b = Object.create(a) (或者用相似的polyfill)建立出一个继承了 a 对象的实体 b。a 对象的属性和方法甚至后期对 a 的属性和方法修改,b 对象都直接继承过来。这里,咱们说,a对象是b对象的原型。咱们发现,这样的继承方式不须要,继承彻底在对象间完成。函数

原型继承的工做机制

在对象属性查找时,若是对象自己存在这个属性,会优先使用它本身的(也就是概念 ownProperty);若是没有,就会查找对象的原型上有没有这个属性;若是原型对象也有本身的原型,递归查找,直到原型的根部,没有原型了中止查找,若是仍是不存在,则返回 undefined。这就是传说中的 原型链,这也是 JS 中对象继承的体现方式,原型存在的意思。this

这里顺带说一下如何获取一个对象的原型。ES5 提供了 Object.getPrototypeOf(obj) 方法来获取一个对象的原型,在 Chrome 中也可使用非标准的 obj.__proto__prototype

JS 在 prototype-based 的面向对象的基础上,引入了 构造器 来模拟 class-based 的模式, 配合 new 操做符使用。 构造器和 已有的 prototype 概念如何配合工做呢?翻译

咱们知道,JS 中的构造器就是一个普通函数,可是这个函数有一个特殊的属性(函数也是对象,因此也有属性) ———— prototype。此 prototype 用来定义经过构造器构造出来的对象的原型,构造器内部的代码用来给对象初始化。code

function Ctor() {}
console.dir(Ctor.prototype);
// 构造器的 prototype 属性,默认值是
// { constructor: Ctor    }

Ctor.prototype.method = function() {
    console.log(1)
}

instance = new Ctor();

instance.constructor // Ctor
instance.method() // console.log(1)

instance 是如何得到 Ctor 的 prototype 属性上的数据的呢?好,还记得 JS 中的继承都是对象之间的继承吗?咱们翻译一下 new 操做符到底干了什么。对象

instance = new Ctor() // 等价于
instance = Object.create(Ctor.prototype) // 用 Ctor 的 prototype 做为原型来建立一个新对象
Ctor.apply(instance) // 执行构造器用来初始化,构造器中的 this 指向 instance

咱们称, instance 的原型是 Ctor.prototype, instance 是 Ctor 构造出来的 (new 出来的).继承

为了让 instance.constructor 能正确指向 instance 的构造器,一个构造器默认的 prototype 上已经存在 constructor 属性,而且指向构造器自己了。在咱们覆盖构造器的 prototype 属性时,记得要把 prototype.constructor 属性定义了,让它指回到构造器,不然构造出来的 instance 的 constructor 属性就出问题了。因此咱们能够看出,instance.constructor 实际上是不是 instance 本身的属性,是原型链上定义的。递归

这里千万不要把 Ctor.prototype 误理解为是 Ctor 的原型。Ctor 的原型是 Object.getPrototypeOf(Ctor)(非标准写法:Ctor.__proto__),它是 Function.prototype, 由于 Ctor 是一个函数对象,全部函数都构造自 Function,原型是 Function.prototype。Ctor.prototype 是 Ctor 构造出来的实例的原型,不是 Ctor 的原型。原型链

Object & Function 鸡生蛋蛋生鸡

有代码以下:

Object instanceof Function // true
Function instanceof Object // true
// what???

咱们来挖掘一下 instanceof 操做符底层逻辑:

instance instanceof Ctor // 等价于

function instanceOf(instance, prototype) {
    var proto = Object.getPrototype(instance); // 取对象原型
    if( proto === null) return false; // 空
    if( proto === prototype) return true; // 原型匹配
    return instanceOf(proto, prototype); // 递归检查原型的原型
}

instance(instance, Ctor.prototype);

JS 中的继承终归是原型的继承,因此 class-based 中的 instanceof 概念最终也须要映射到 prototype 上。可是 JS 中的构造器名称有一个特殊之处,这个名称既表示了构造器这个函数,又表示了 class-based 概念中的 的概念, 而函数自己又是一种特殊的对象。

Object instanceof Function 之因此为 true,咱们是把 Object 当作构造器看待,它是一个函数,它是 Function 的实例,因此同时这里咱们把 Function 看成类型来看待,它是全部 function 的类。

Function instanceof Object 之因此为 true,咱们是把 Function 看成对象看待,它虽然是一个构造函数,可是它也是对象,它是 Object 的实例,因此同时咱们又把 Object 看成类型来看待,它是全部对象的类。

从原型角度:

// Object 是一个构造函数,它的原型是 Function.prototype
// Function.prototype 是全部函数的原型,call, apply 就挂在这里
Object.getPrototypeOf(Object) === Function.prototype 

// Function 也是一个构造函数
Object.getPrototypeOf(Function) === Function.prototype
// Function.prototype 自己是一个对象,全部对象的原型根部都是 Object.prototype
Object.getPrototypeOf(Function.prototype) === Object.prototype

这也印证了 JS 中函数是对象的概念。

相关文章
相关标签/搜索