在 class-based 的物件導向的世界裡,要出現物件,必須先有類。類之間可以繼承,類再使用 new 操作建立出實體,父子物件之間的繼承體現在父類和子類上。你不能說 物件 a 繼承了物件 b,只能說 class a 繼承了 class b,然後他們各自有乙個例項a、b。
js中實現的是原型繼承。在 prototype-based 的物件導向的世界裡,繼承是物件之間的事情,就像現實世界裡,兒子繼承父親,沒必要非得整出父親類、兒子類。你有乙個物件 a,通過b = object.create(a)
(或者用類似的polyfill)建立出乙個繼承了 a 物件的實體 b。a 物件的屬性和方法甚至後期對 a 的屬性和方法修改,b 物件都直接繼承過來。這裡,我們說,a物件是b物件的原型
。我們發現,這樣的繼承方式不需要類
,繼承完全在物件間完成。
在物件屬性查詢時,如果物件本身存在這個屬性,會優先使用它自己的(也就是概念ownproperty
);如果沒有,就會查詢物件的原型上有沒有這個屬性;如果原型物件也有自己的原型,遞迴查詢,直到原型的根部,沒有原型了停止查詢,如果還是不存在,則返回undefined
。這就是傳說中的原型鏈
,這也是 js 中物件繼承
的體現方式,原型存在的意思。
這裡順帶說一下如何獲取乙個物件的原型。es5 提供了object.getprototypeof(obj)
方法來獲取乙個物件的原型,在 chrome 中也可以使用非標準的obj.__proto__
。
js 在 prototype-based 的物件導向的基礎上,引入了構造器
來模擬 class-based 的模式, 配合new
操作符使用。 構造器和 已有的 prototype 概念如何配合工作呢?
我們知道,js 中的構造器就是乙個普通函式,但是這個函式有乙個特殊的屬性(函式也是物件,所以也有屬性) ————prototype
。此 prototype 用來定義通過構造器構造出來的物件的原型,構造器內部的**用來給物件初始化。
function ctor() {}
console.dir(ctor.prototype);
// 構造器的 prototype 屬性,預設值是
// ctor.prototype.method = function()
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 作為原型來建立乙個新物件
我們稱,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)
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
object.getprototypeof(object) === function.prototype
// function 也是乙個建構函式
object.getprototypeof(function) === function.prototype
// function.prototype 本身是乙個物件,所有物件的原型根部都是 object.prototype
object.getprototypeof(function.prototype) === object.prototype
這也印證了 js 中函式是物件的概念。 js原型物件 原型鏈 繼承
原型物件 只要建立了乙個新函式,就會根據一些特定的規則為該函式建立乙個prototype屬性,這個屬性指向的物件就是該新函式的原型物件。預設情況下,所有原型物件都會自動獲取乙個constructor屬性,這個屬性是乙個指向prototype屬性所在函式的指標 原型物件的優點 可以讓所有物件例項共享它...
JS原型繼承與原型鏈(二)
建構函式的繼承 function animal function cat name,color 一 建構函式繫結 function cat name,color var cat1 new cat 大毛 黃色 alert cat1.species 動物 二 prototype模式 第二種方法更常見,使...
js繼承,原型鏈繼承
1 乙個型別的物件能夠訪問另外乙個型別的屬性和方法 2 類與類之間的關係 類就是眾多例項共有的屬性和方法的乙個抽象 function animal name animal.prototype.say function function dog 把子類的原型指向父類的例項 dog.prototype ...