在 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 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 中函数是对象的概念。