原型包括三個獨立但相關的訪問器。這三個單詞都是對單詞prototype做了一些變化。 C.prototype用於建立由new C()創建的對象的原型 Object.getPrototypeOf(obj)是ES5中用來獲取obj對象的原型對象的標準方法 obj.__proto__是獲取obj對象... ...
原型包括三個獨立但相關的訪問器。這三個單詞都是對單詞prototype做了一些變化。
-
C.prototype用於建立由new C()創建的對象的原型
-
Object.getPrototypeOf(obj)是ES5中用來獲取obj對象的原型對象的標準方法
-
obj.__proto__是獲取obj對象的原型對象的非標準方法
一個例子
要理解這些訪問器,我們拿一個典型的js數據類型作例子。
假設User構造函數需要通過new操作符來調用。它需要兩個參數,即姓名和密碼的哈希值,並將它們存儲在創建的對象中。
代碼如下:
function User(name,pwd){ this.name=name; this.pwd=pwd; } User.prototype.toString=function(){ return '[User '+this.name+']'; } User.prototype.checkPwd=function(pwd){ return hash(pwd)===this.pwd; } var u=new User('cedrusweng','$sdf99kaslf7');
根據上面的這個代碼我們可以畫出下麵這個圖,以表示各種關係。
我們每次在使用
function Fn(){ }
相當於調用下麵這樣
var fn=new Function([參數,]"函數體");
其中Function是一個構造函數,它的原型對象中包含我們之前講到過的call、apply、bind等屬性和方法。fn是Function的一個實例對象。
所以這裡的可以得到第一部分的圖
下麵再就構造函數User和它的原型對象之間的關係畫出一個圖
User函數帶有一個預設的prototype的屬性,其包含一個開始幾乎為空的對象。上面的例子中添加了兩個方法到原型對象中。當使用new操作符創建User的實例時,產生的對象u得到了自動分配的原型對象,該原型對象被存儲在User.prototype中。
註意:new操作符調用構造函數,會產生一個新的對象實例,這個對象是以構造函數為模板,創建一份私有的性屬性和方法,所有的實例都會繼承原型對象。當訪問u.name時,u對象會首先在它的私有屬性中進行搜索,如果有則會返回,如果沒有,則會查找對象的原型對象。當訪問u.checkPwd時,私有屬性和方法不存在時,會返回存儲在User.prototype中的方法。
原型有關的方法
首先,構造函數的prototype屬性用來設置新實例的原型關係。
其次,ES5中的函數Object.getPrototypeOf()可以用於檢索現有對象的原型。
如上面的例子,可以使用下麵代碼來檢測對象u的原型對象。
Object.getPrototypeOf(u)===User.prototype;//true
最後,一些環境提供了非標準的方法檢索對象的原型,即特殊的__proto__屬性。這可作為在不支持ES5的Object.getPrototypeOf方法的環境中的一個相容方法。在這些環境中可以使用下麵代碼完成檢測
u.__proto__===User.prototype;//true
最後的說明
js程式員往往將User描述為一個類,儘管它跟一個函數差不多。js中的類本質上是一個構造函數與一個用於類(User.prototype)實例間共用方法的原型對象的結合。
下麵是一個User類的概念圖。
User函數給該類提供了一個公共的構造函數,而User.prototype是實例之間共用方法的一個內部實現。User和u的變通用法都不需要直接訪問原型對象。
提示
-
C.prototype屬性是new C()創建的對象的原型
-
Object.getPrototypeOf(obj)是ES5中檢索對象原型的標準函數
-
obj.__proto__是檢索對象原型的非標準方法
-
類是由一個構造函數和一個關聯的原型組成的一種設計模式
附錄一:函數聲明
函數使用
var fn1=function(){};
var fn2=new Function('');
function fn3(){
}
這3種方式的函數,前面兩種都是屬性函數表達式,最後一種是函數聲明語法。函數實際上是對象,每個函數都是Function類型的實例,而且都與其他引用類型一樣具有屬性和方法。
具體可以看下圖來看對應的關係圖
函數表達式與函數聲明的區別
看到上面都是產生了函數的實例,但解析器會率先讀取函數聲明,並使其在執行任何代碼之前可用;函數表達式,則必須等到解析器執行到它所在的代碼行,才會真正被解釋。
可以根據函數表達式的特點,按條件給變數賦不同的函數表達式。
function createFn(a) var fn; if(a){ fn=function(){console.log(1)} }else{ fn=function(){console.log(2)} } return fn; } var fn1=createFn(true); var fn2=createFn(); fn1();//1 fn2();//2