原型、原型對象、構造函數、原型鏈理解
1. 基本概念:
“原型屬性”也可以叫做“原型”(prototype):所有函數都有prototype,我覺得可以理解為python中的類屬性,不需要通過實例,直接用類(es5就是函數名)可以調用,下面列舉了三種創建函數的方法,函數創建後都有prototype屬性,prototype指向“原型對象”。
// 函數聲明 function F1() { }; // 表達式定義 let F2 = function () { }; // 函數構造 let F3 = new Function(‘n1‘, ‘n2‘, ‘return n1+n2‘); console.info(F1.prototype) //F1 {}console.info(F2.prototype) //F2 {} console.info(F3.prototype) //anonymous {}
原型對象(prototype所指向的對象):這玩意主要就是用來繼承用的,包含實例的方法和屬性。說白了也就是一個對象,用來定義函數對象的屬性、方法,默認情況下它包含一個constructor屬性,如果你重新定義可以覆蓋constructor屬性。
原型對象與構造函數配合一起,就形成一個類了,然後構造函數接收每次初始化對象的初始值,原型對象就提供類模板。而在其他java、python中都寫在class中,當然es6也加入了class;以下的簡單代碼幫助理解上面說的,定義一個動物類,根據構造函數創造不同的動物,如果需要創建特殊動物,可以繼承然後添加一些特殊屬性、方法再創建。---------總結es5的類可以用“構造函數+構造函數的prototype”來定義,類的對象使用“new構造函數”來生成。
// 構造函數 let Animal = function (name) { this.name = name } // 原型對象定義 Animal.prototype.getAnimal = function () { return this.name } // 創建對象,會繼承Animal.prototype let dog = new Animal(‘dog‘) let cat = new Animal(‘cat‘) console.info(dog.getAnimal()) console.info(cat.getAnimal())
上圖展示了構造函數、原型對象、具體對象的屬性、方法及屬性值。
普通對象與函數對象:通過函數對象 new一下可以得到普通對象,把函數對象理解為類,普通對象為實例。如下代碼加強理解,可以看到函數對象new完以後生產的對象是object;函數對象中都有prototype前面已經說過了(為什麽函數對象中會有這個屬性以後讀取更深以後再解答)
// Animal函數對象 let Animal = function (name) { this.name = name } // 原型對象 Animal.prototype.getAnimal = function () { return this.name } // 通過函數對象創建普通對象 let dog = new Animal(‘dog‘) console.info(typeof dog) //object
總結: 函數對象通過new Function()可以得到,Function對象可以構造函數對象。而函數對象又可以new一個普通對象出來。
__proto__:所有對象都有__proto__這個屬性,這個屬性指向對應“函數對象(就理解為類)”的prototype,這也是實現原型鏈的根本,在書中一般都用[[prototype]]。
構造函數和constructor屬性:其實前面的圖已經標註了,概念後續補上。
原型鏈:ES中的繼承主要用原型鏈來實現,記住了這玩意主要用來實現繼承。其基本思想是:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法(js高級程序設計說的),還是用以上的例子來說明
let Animal = function (name) { this.name = name } // 原型對象定義 Animal.prototype.getAnimal = function () { return this.name } // 創建對象,會繼承Animal.prototype let dog = new Animal(‘dog‘) let cat = new Animal(‘cat‘) console.info(dog.valueOf()) console.info(cat.valueOf())
跟上面例子就差了最後console打印部分,我們定義的Animal沒有寫valueof()為什麽可以調用這個方法呢?到底是什麽鬼?這就是原型鏈搞的鬼。我再把上面的圖形完善一下
上圖還有兩個屬性的指向沒有畫,第一、Animal的__proto__指向;第二、Animal.prototype的__proto__指向。
這個圖把Animal.prototype的__proto__指向添加了,
圖形說明:
1. Animal.prototype是個普通對象,ES中Object是所有對象的基礎。所有引用類型都繼承了Object;所有函數的默認原型是都Object的實例,因此Animal.prototype的__proto__指向Object.prototype,這個一定要理解。
2. 我們調用cat.valueOf()解釋器會先找dog實例中有沒有這個方法,沒有就在Animal.prototype中查找,沒有再去Object.prototype中查找。然後就有了cat.valueOf()的運行結果:Animal { name: ‘cat‘ }。
3. Object構造函數的prototype也指向Object.prototype。
原型、原型對象、構造函數、原型鏈理解