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