該文章來自于最詳盡的 JS 原型與原型鏈終極詳解,沒有「可能是」的學習總結
一:對象的分類
- JS中對象分為兩種,函數對象和普通對象。也稱為Function Object和Object。通過typeof 可以查看具體的對象類型。
- JS中的函數對象,在和new結合的時候,可以創建新的對象。JS提供了兩個最基本的函數對象 Function 和 Object。其他所有的對象都由這兩個函數對象演進而來
- JS中的函數對象,有兩個方法可以創建,一個是通過new Function(),一個是通過
function() {}
聲明。 - JS中的普通對象,也有兩個方法可以創建,一個是通過
new (非Function函數對象/**Object**)
,一個是通過{ }
等各類語法聲明。Array也可視為一個普通對象,可以通過new Array(),也可以通過[] - 另外,還有一個特殊的
Object.create()
方法,可以由一個普通對象創建一個新的普通對象。此時第一個普通對象起到類似函數對象的作用,不同之處在于,新建對象的__proto__指向第二個普通對象自身。因為普通函數沒有prototype屬性(這個可以看完下面的回頭再理解下)。
var o1 = {};
var o2 =new Object();
var o3 = new f1();
var o4 = Object.create(o2)
function f1(){};
var f2 = function(){};
var f3 = new Function('str','console.log(str)');
console.log(typeof Object); //function
console.log(typeof Function); //function
console.log(typeof f1); //function
console.log(typeof f2); //function
console.log(typeof f3); //function
console.log(typeof o1); //object
console.log(typeof o2); //object
console.log(typeof o3); //object
console.log(typeof o4); //object
二:prototype 和 __proto__
- JS中的任意一個對象都包括一個 __proto__屬性。該屬性在ES2015之前,并沒有在標準中明確定義,而是一個大多數瀏覽器默認支持的一個屬性,也稱為[[prototype]]
- JS中的任意一個函數對象都包括了一個prototype屬性。該屬性相當于是當前函數對象的一個特殊實例。一般為普通對象。
- JS中任意一個對象的__proto__屬性,根據new的特性,都指向創建該對象的函數對象的prototype屬性。即
(普通對象).__proto__ == (創建它的函數對象).prototype
(函數對象).__proto__ == Function.prototype // 函數對象都由 new Function()創建
Function和Object都是函數對象,都是通過new Function()
創建,所以
Function.__proto__ == Function.prototype
Object.__proto__ == Function.prototype
- JS中的函數對象的prototype一般都為普通對象即
typeof A.prototype == 'Object' // A
但Function除外,因為Function的新建實例還是一個函數對象,所以
typeof Function.prototype == 'function'
但是該prototype是一個特殊的函數對象。它沒有prototype,另外,它的__proto__指向了Object.prototype,而不是Function的prototype,即
typeof Function.prototype == 'function'
Function.prototype.__proto__ == Object.prototype
- JS中的所有函數對象的prototype都有一個constructor函數,稱為構建函數,該構建函數指向當前函數對象。即
function f1() {}
f1.prototype.constructor == f1
Function.prototype.constructor == Function
-
prototype,__proto__以及constructor的關系圖
image.png
其中的person1.constructor其實是通過原型鏈繼承在Person.prototype中尋得。
三:原型鏈繼承
JS中的原生繼承是通過原型鏈完成的。以下面的代碼為例子
var Person = function(name){
this.name = name;
};
Person.prototype.getName = function(){
return this.name; // tip: 當函數執行時這個 this 指的是誰?
}
var person1 = new person('Mick');
person1.getName(); //Mick
person1.name; //Mick
person1.toString(); // [object Object]
- 當person1執行getName方法的時候,先在person1的自身屬性中尋找。此時自有屬性只有name屬性,未找到,則繼續在person1的__proto__屬性中尋找
- 因為
person1.__proto__ = Person.prototype
。此時在Person.prototype中尋找getName屬性,此時尋找得到,將Person.prototype.getName返回作為person1.getName,進行執行。 - 當person1執行toString方法的時候,在Person.prototype中未能找到,則繼續在Person.prototype.__proto__中尋找。
- Person.prototype作為Person的一個特殊實例,它是普通對象。普通對象都由new Object創建。因此Person.prototype.__proto__ = Object.prototype。因此繼續在Object.prototype中尋找,發現了toString方法,將Object.prototype.toString返回作為person1.toString,進行執行。
- 需要注意,JS中的原型鏈繼承,指的是新建對象和創建函數對象的prototype之間的關系,而不是和創建函數的關系。能夠在原型鏈繼承上傳遞下去的只有原型對象prototype上的函數。比如Object擁有geOwnPropertyNames方法,但Object.prototype卻不包含,所以person1也無法擁有getOwnPropertyNames 方法。同樣的,如果新建一個函數對象Student,讓它繼承Person函數對象,那么new Stuednet()新建的對象也只能繼承Person.prototype.getName,而不能繼承person.name屬性
四:JS中原型鏈繼承鏈路
- Function函數對象
Function.__proto__ == Function.prototype
Function.prototype.__proto__ == Object.prototype
Object.prototype.__proto__ == null
- Object函數對象
Object.__proto__ == Function.prototype
Function.prototype.__proto__ == Object.prototype
Object.prototype.__proto__ == null
- 自定義函數對象
function f1() {}
f1.__proto__ == Function.prototype
Function.prototype.__proto__ == Object.prototype
Object.prototype.__proto__ == null
- 自定義函數對象實例--也是普通對象
function f1() {}
o1 = new f1()
o1.__proto__ = f1.prototype
f1.prototype__proto__ == Object.prototype
Object.prototype.__proto__ == null
- 自定義函數對象繼承--繼承自某一個自定義對象
function f1() {}
function f2() {} // 設定f2繼承自f1,具體如何實現繼承,可以查看另一篇文章
o2 = new f2()
o2.__proto__ = f2.prototype
f2.prototype.__proto__ = f1.prototype
f1.prototype__proto__ == Object.prototype
Object.prototype.__proto__ == null
五:總結
- 所有的普通對象,都會繼承(1)創建它的函數對象的prototype上的屬性(2)函數對象繼承自其它函數對象prototype上的屬性(3)Object函數對象的prototype上的屬性
- 所有的函數對象,都會繼承(1)Function.prototype上的屬性(2)Object函數對象的prototype上的屬性
- 最后所有的對象的原型鏈都會追溯到null標識符上
六:Function和Object自帶屬性
參考鏈接
http://www.lxweimin.com/p/dee9f8b14771
http://www.lxweimin.com/p/652991a67186
http://www.lxweimin.com/p/a4e1e7b6f4f8