這篇文章主要介紹了深入理解JavaScript編程中的原型概念,包括prototype屬性的使用等一些相關知識,需要的朋友可以參考下
JavaScript 的原型對象總是讓人糾結。即使是經驗豐富的JavaScript專家甚至其作者,經常對這一概念給出很有限的解釋。我相信問題來自於我們對原型最早的認識。原型總是與new, constructor 以及令人困惑的prototype屬性緊密聯系。事實上,原型是一個相當簡單的概念。為了更好地理解它,我們需要忘記我們所‘學到'的構造原型,然後,追本溯源。
什麼是原型?
原型是一個從其他對象繼承屬性的對象。
是不是任何對象都可以是原型?
是的
那些對象有原型?
每個對象都有一個默認的原型。原型本身就是對象,每一個原型本身也存在一個原型。(只有一個例外,默認的對象原型在每條原型鏈的頂端,其他的原型在原型鏈的後面)
退一步說,什麼又是對象呢?
在JavaScript中一個對象是以鍵值對保存的任意的無序集合,如果它不是原始類(undefined,null,boolean.nuber或string),它就是一個對象。
你可以認為每個對象都有一個prototype. 但當我寫({}).prototype的時候,我卻得到了undefined,你瘋不瘋?
忘記你所掌握的關於prototype屬性的理解 - 這很可能是迷惑的根源. 一個對象真正的prototype是內部[[Prototype]]屬性. ECMA 5介紹了標准的訪問方法,Object.getPrototypeOf(object)。這個最新的實現已被Firefox, Safari, Chrome and IE9所支持. 另外,除了IE,所有的浏覽器都支持非標准的訪問方法__proto__.不然的話,我們只能說對象的構造方法就是它的prototype屬性.
?
1 2 3 4 5 6 7 8 9 10 11 var a = {}; //Opera 或 IE<=8下失敗 Object.getPrototypeOf(a); //[object Object] //IE下失敗 a.__proto__; //[object Object] //所有浏覽器 //(but only if constructor.prototype has not been replaced and fails with Object.create) a.constructor.prototype; //[object Object]很好, false 是原始類型, 為什麼false.__proto__ 會返回一個值呢?
當訪問原始類型的原型(prototype),它會強制轉化為一個對象。
?
1 2 //(works in IE<=8 too, due to double-negative) false.__proto__ === Boolean(false).__proto__; //true我想使用原型實現繼承,我現在該怎麼做?
給一個實例添加原型屬性,幾乎是沒有意義的.除非一種情況,那就是,很有效率的添加屬性直接到實例本身.假設我們已經有了一個對象,要共享已經存在的對象的功能.例如Array,我們可以這樣做
?
1 2 3 4 //fails in IE<=8 var a = {}; a.__proto_ = Array.prototype; a.length; //0但是我們可以看到原型的真正強大在於多個實例共享同一原型。原型對象的屬性只被定義一次就可以被它引用的所有實例所繼承。使用原型對性能和程序可維護性的提高效果是很顯而易見的。那麼這就是構造函數產生的原因嗎?是的,構造函數提供了一個便捷的跨浏覽器機制來實現對實例創建時的公用原型分配。。
在給出一個例子之前,我需要知道constructor.prototype property是干什麼的?
好吧,首先,JavaScript不區分構造函數和其它方法,所以每個方法都有prototype屬性。反而任何不是方法的,都沒有這樣的屬性。
?
1 2 3 4 5 6 7 8 9 10 11 //永遠不是構造函數的方法,無論如何都是有prototype屬性的 Math.max.prototype; //[object Object] //構造函數也有prototype屬性 var A = function(name) { this.name = name; } A.prototype; //[object Object] //Math不是一個方法,所以沒有prototype屬性 Math.prototype; //null現在可以定義: 一個方法的prototype屬性是當這個方法被用作構造函數來創建實例時賦給該實例的prototype的對象。
非常重要的一點是,要理解方法的prototype屬性和實際的prototype沒有任何關系。
?
1 2 3 4 5 6 7 //(在IE中會失敗) var A = function(name) { this.name = name; } A.prototype == A.__proto__; //false A.__proto__ == Function.prototype; //true - A的prototype是它的構造函數的prototype屬性能給個例子不?
以下的代碼,可能你已經看到或用過上百次了,但這裡又把它搬上來了,但可能會有些新意。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //構造器. <em>this</em> 作為新對象返回並且它內部的[[prototype]]屬性將被設置為構造器默認的prototype屬性 var Circle = function(radius) { this.radius = radius; //next line is implicit, added for illustration only //this.__proto__ = Circle.prototype; } //擴充 Circle默認的prototype對象的屬性因此擴充了每個由它新建實例的prototype對象的屬性 Circle.prototype.area = function() { return Math.PI*this.radius*this.radius; } //創建Circle的兩個示例,每個都會使用相同的prototype屬性 var a = new Circle(3), b = new Circle(4); a.area().toFixed(2); //28.27 b.area().toFixed(2); //50.27這很棒。如果我改變了constructor的prototype屬性,即使是已存在的實例對象也可以立刻訪問新的prototype版本嗎?
嗯......不完全是。如果我修改的是現存prototype的屬性後,那麼確實是這種情況,因為對象創建時a.__proto__引用了A.prototype所定義的對象。
?
1 2 3 4 5 6 7 8 9 10 var A = function(name) { this.name = name; } var a = new A('alpha'); a.name; //'alpha' A.prototype.x = 23; a.x; //23但是如果我將prototype屬性用一個新對象代替,a.__proto__ 仍然指向原始對象。
?
1 2 3 4 5 6 7 8 9 10 var A = function(name) { this.name = name; } var a = new A('alpha'); a.name; //'alpha' A.prototype = {x:23}; a.x; //null一個缺省的prototype是什麼樣的?
一個擁有constructor屬性的對象。
?