深入理解JavaScript編程中的原型概念
JavaScript 的原型對象總是讓人糾結(jié)。即使是經(jīng)驗豐富的JavaScript專家甚至其作者,經(jīng)常對這一概念給出很有限的解釋。我相信問題來自于我們對原型最早的認(rèn)識。原型總是與new, constructor 以及令人困惑的prototype屬性緊密聯(lián)系。事實上,原型是一個相當(dāng)簡單的概念。為了更好地理解它,我們需要忘記我們所‘學(xué)到'的構(gòu)造原型,然后,追本溯源。
什么是原型?
原型是一個從其他對象繼承屬性的對象。
是不是任何對象都可以是原型?
是的
那些對象有原型?
每個對象都有一個默認(rèn)的原型。原型本身就是對象,每一個原型本身也存在一個原型。(只有一個例外,默認(rèn)的對象原型在每條原型鏈的頂端,其他的原型在原型鏈的后面)
退一步說,什么又是對象呢?
在JavaScript中一個對象是以鍵值對保存的任意的無序集合,如果它不是原始類(undefined,null,boolean.nuber或string),它就是一個對象。
你可以認(rèn)為每個對象都有一個prototype. 但當(dāng)我寫({}).prototype的時候,我卻得到了undefined,你瘋不瘋?
忘記你所掌握的關(guān)于prototype屬性的理解 - 這很可能是迷惑的根源. 一個對象真正的prototype是內(nèi)部[[Prototype]]屬性. ECMA 5介紹了標(biāo)準(zhǔn)的訪問方法,Object.getPrototypeOf(object)。這個最新的實現(xiàn)已被Firefox, Safari, Chrome and IE9所支持. 另外,除了IE,所有的瀏覽器都支持非標(biāo)準(zhǔn)的訪問方法__proto__.不然的話,我們只能說對象的構(gòu)造方法就是它的prototype屬性.
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__ 會返回一個值呢?
當(dāng)訪問原始類型的原型(prototype),它會強制轉(zhuǎn)化為一個對象。
//(works in IE<=8 too, due to double-negative) false.__proto__ === Boolean(false).__proto__; //true
我想使用原型實現(xiàn)繼承,我現(xiàn)在該怎么做?
給一個實例添加原型屬性,幾乎是沒有意義的.除非一種情況,那就是,很有效率的添加屬性直接到實例本身.假設(shè)我們已經(jīng)有了一個對象,要共享已經(jīng)存在的對象的功能.例如Array,我們可以這樣做
//fails in IE<=8 var a = {}; a.__proto_ = Array.prototype; a.length; //0
但是我們可以看到原型的真正強大在于多個實例共享同一原型。原型對象的屬性只被定義一次就可以被它引用的所有實例所繼承。使用原型對性能和程序可維護性的提高效果是很顯而易見的。那么這就是構(gòu)造函數(shù)產(chǎn)生的原因嗎?是的,構(gòu)造函數(shù)提供了一個便捷的跨瀏覽器機制來實現(xiàn)對實例創(chuàng)建時的公用原型分配。。
在給出一個例子之前,我需要知道constructor.prototype property是干什么的?
好吧,首先,JavaScript不區(qū)分構(gòu)造函數(shù)和其它方法,所以每個方法都有prototype屬性。反而任何不是方法的,都沒有這樣的屬性。
//永遠不是構(gòu)造函數(shù)的方法,無論如何都是有prototype屬性的 Math.max.prototype; //[object Object] //構(gòu)造函數(shù)也有prototype屬性 var A = function(name) { this.name = name; } A.prototype; //[object Object] //Math不是一個方法,所以沒有prototype屬性 Math.prototype; //null
現(xiàn)在可以定義: 一個方法的prototype屬性是當(dāng)這個方法被用作構(gòu)造函數(shù)來創(chuàng)建實例時賦給該實例的prototype的對象。
非常重要的一點是,要理解方法的prototype屬性和實際的prototype沒有任何關(guān)系。
//(在IE中會失敗) var A = function(name) { this.name = name; } A.prototype == A.__proto__; //false A.__proto__ == Function.prototype; //true - A的prototype是它的構(gòu)造函數(shù)的prototype屬性
能給個例子不?
以下的代碼,可能你已經(jīng)看到或用過上百次了,但這里又把它搬上來了,但可能會有些新意。
//構(gòu)造器. <em>this</em> 作為新對象返回并且它內(nèi)部的[[prototype]]屬性將被設(shè)置為構(gòu)造器默認(rèn)的prototype屬性 var Circle = function(radius) { this.radius = radius; //next line is implicit, added for illustration only //this.__proto__ = Circle.prototype; } //擴充 Circle默認(rèn)的prototype對象的屬性因此擴充了每個由它新建實例的prototype對象的屬性 Circle.prototype.area = function() { return Math.PI*this.radius*this.radius; } //創(chuàng)建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版本嗎?
嗯......不完全是。如果我修改的是現(xiàn)存prototype的屬性后,那么確實是這種情況,因為對象創(chuàng)建時a.__proto__引用了A.prototype所定義的對象。
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__ 仍然指向原始對象。
var A = function(name) { this.name = name; } var a = new A('alpha'); a.name; //'alpha' A.prototype = {x:23}; a.x; //null
一個缺省的prototype是什么樣的?
一個擁有constructor屬性的對象。
var A = function() {}; A.prototype.constructor == A; //true var a = new A(); a.constructor == A; //true (a 的constructor屬性繼承自它的原型)
instanceof與prototype有啥關(guān)系?
如果A的prototype屬性出現(xiàn)在a的原型鏈中,則表達式a instanceof A會返回true。這意味著我們可以欺騙instanceof,讓它失效。
var A = function() {} var a = new A(); a.__proto__ == A.prototype; //true - so instanceof A will return true a instanceof A; //true; //mess around with a's prototype a.__proto__ = Function.prototype; //a's prototype no longer in same prototype chain as A's prototype property a instanceof A; //false
那么我還能利用原型干些其它的什么事兒?
記得我曾經(jīng)說過每一個構(gòu)造器都擁有一個prototype屬性,利用該屬性可以將原型賦值給所有由構(gòu)造器產(chǎn)生的實例?其實這同樣適用于本地構(gòu)造器,例如Function和String。通過擴展(而不是替換)這個屬性,我們可以更新每個指定類型對象的prototype。
String.prototype.times = function(count) { return count < 1 ? '' : new Array(count + 1).join(this); } "hello!".times(3); //"hello!hello!hello!"; "please...".times(6); //"please...please...please...please...please...please..."
告訴我更多關(guān)于繼承與原型是怎么工作的。原型鏈又是什么東東?
因為每個對象和每個原型(本身)都有一個原型,我們可以想象, 一個接一個的對象連接在一起形成一個原型鏈。 原型鏈的終端總是默認(rèn)對象(object)的原型。
a.__proto__ = b; b.__proto__ = c; c.__proto__ = {}; //默認(rèn)對象 {}.__proto__.__proto__; //null
原型繼承機制是內(nèi)在且隱式實現(xiàn)的。當(dāng)對象a要訪問屬性foo時,Javascript會遍歷a的原型鏈(首先從a自身開始),檢查原型鏈的每一個環(huán)節(jié)中存在的foo屬性。如果找到了foo屬性就會將其返回,否則返回undefined值。
直接賦值會咋樣?
當(dāng)直接為對象屬性賦值時,原型繼承機制就玩不轉(zhuǎn)了。a.foo='bar'會直接賦值給a的foo屬性。要想為原型對象的屬性賦值,你需要直接定位原型對象的該屬性。
關(guān)于javascript原型就講全了。我覺得對于原型概念的理解,我把握的還是比較準(zhǔn)確的,但是我的觀點無論如何也不是最后的結(jié)果。請隨便告之我的錯誤之處或提出和我不一致的觀點。
- JavaScript基本概念初級講解論壇貼的學(xué)習(xí)記錄
- javascript,jquery閉包概念分析
- JavaScript內(nèi)核之基本概念
- javascript學(xué)習(xí)筆記(二) js一些基本概念
- JavaScript回調(diào)(callback)函數(shù)概念自我理解及示例
- JavaScript的作用域和塊級作用域概念理解
- JavaScript 基本概念
- javascript中幾個容易混淆的概念總結(jié)
- javascript相關(guān)事件的幾個概念
- 通過實例理解javascript中沒有函數(shù)重載的概念
- 深入剖析JavaScript編程中的對象概念
- 帶領(lǐng)大家學(xué)習(xí)javascript基礎(chǔ)篇(一)之基本概念
相關(guān)文章
深入理解JavaScript系列(25):設(shè)計模式之單例模式詳解
這篇文章主要介紹了深入理解JavaScript系列(25):設(shè)計模式之單例模式詳解,本文給出了多種單例模式的實現(xiàn)方式,需要的朋友可以參考下2015-03-03javascript創(chuàng)建和存儲cookie示例
javascript創(chuàng)建和存儲cookie,cookie是存儲于訪問者的計算機中的變量,下面看一下使用示例吧2014-01-01Javascript中eval函數(shù)的使用方法與示例
JavaScript有許多小竅門來使編程更加容易。其中之一就是eval()函數(shù),這個函數(shù)可以把一個字符串當(dāng)作一個JavaScript表達式一樣去執(zhí)行它。以下是它的說明2007-04-04