摘要:類類的概念應該是面向對象語言的一個特色,但是并不像,等高級語言那樣擁有正式的類,而是多數通過構造器以及原型方式來仿造實現。因此,出現了構造函數方式,它的關鍵在于構造器概念的引入。于是,這就產生了構造函數原型法的類構造方法。
工廠模式類 Class 類的概念應該是面向對象語言的一個特色,但是JavaScript并不像Java,C++等高級語言那樣擁有正式的類,而是多數通過構造器以及原型方式來仿造實現。在討論構造器和原型方法前,我可以看看一種叫做工廠方式的仿造方法。
這種方式顯然可以實現class的功能,但是外形上怎么也無法說它是個class以及class實例的創建過程。因此,出現了“構造函數方式”,它的關鍵在于構造器(Constructor)概念的引入。
構造器(Constructor)
這個看起來有點類的樣子了吧(先不提那個難看的外置function)?我們發現,那個constructor其實就是一個簡單的function,它與“工廠方式”中的createCar()區別就在于:
1、方法名大寫
2、沒有了空對象的創建和返回
3、使用this做引用。
那原來的那個空對象的創建以及返回的步驟去哪了呢?這兩個步驟,現在都由創建實例時的“new”實現了。“new”這個操作符負責創建一個空對象,然后將那個叫做構造器的function添加到實例對象中并觸發它,這樣這個function實際上就是這個對象的一個method,function中的this指向的便是這個對象,最后將這個對象返回。根據如上分析,我們可以把這個過程簡單分解為如下代碼:
構造函數方式雖然與高級面向對象語言中的類創建方式已經很接近(使用new創建),但是貌似那個游離在類之外的function material()其實卻是個相當有礙觀瞻的瑕疵。我們應該想一種辦法讓這個方法與類掛鉤,讓它成為類的一個屬性,不是全局的。于是,這就產生了“構造函數+原型法”的類構造方法。
構造函數 + 原型從上面的構造函數模式創建對象的例子上可以看到,每創建一個對象實例,每個對象中都有material()這個成員方法,這樣看起來是不是會浪費內存空間,降低執行效率。所以JavaScript中提供了原型的方法可以解決這個問題。
原型:proto
在JavaScript中每個函數都有一個原型屬性,即prototype,當調用構造函數進行創建對象的時候,所有該構造函數原型的屬性在創建的對象上都可用。按照這樣的想法多個CreateCar都可以共享一個原型material.
在構造函數+原型法中,我們對于類的method期待得到的效果是:
1. 僅是類的method而不是全局的。
2. 只在類被定義時創建一個method實例,然后被所有類的實例共用。
由這兩個目標,我們很容易想到高級面向對象語言Java的private static變量的特點。JavaScript沒有為我們提供這么簡單的符號來實現這個復雜功能,但是卻有一個屬性可以幫我們仿造出這種效果:prototype。我們先來看幾段prototype的使用代碼。
> function Car(){ } > > Car.prototype.material = "steel"; > > var car1 = new Car(); var car2 = new Car(); > > document.write(car1.material); //prints "steel" > document.write(car2.material); //prints "steel" > > //car1.prototype.material = "iron" //compile error:car1.prototype is > undefined car1.material = "iron"; > > document.write(car1.material); //prints "iron" > document.write(car2.material); //prints "steel" > document.write(Car.prototype.material); //prints "steel" > > Car.prototype.material = "wood"; var car3 = new Car(); > document.write(car1.material); //prints "iron" > document.write(car2.material ); //prints "wood" > document.write(car3.material ); //prints "wood" > document.write(Car.prototype.material); //prints "wood"
分析該段代碼前,需要明確兩個概念:對象的直屬屬性和繼承屬性。直接在構造函數中通過this.someproperty = xxx這種形式定義的someproperty屬性叫做對象的直屬屬性,而通過如上第4行代碼那樣Car.prototype.material = "steel";這種形式定義的material屬性叫做繼承屬性。由上面這段代碼,我們可以總結出prototype屬性的如下特點:
prototype是function下的屬性(其實任意object都擁有該屬性,function是對象的一種)
prototype屬性的值是一個對象,因此可任意添加子屬性(line 4)
類的實例可以直接通過"."來直接獲取prototype下的任意子屬性(line 9)
所有以此function作為構造函數創建的類實例共用prototype中的屬性及值(ling 9,10)
類的實例沒有prototype屬性(line 12)
可以直接通過 "實例.屬性 = xxx" 的方式修改繼承屬性,修改后的值將覆蓋繼承自prototype的屬性,但此修改不影響prototype本身,也不影響其它類實例(line 15,16,17)
繼承屬性修改后,該屬性就成為類實例的直屬屬性
可以直接修改prototype的屬性值,此改變將作用于此類下的所有實例,但無法改變直屬屬性值(極晚綁定line 21-24)
PS:對象實例在讀取某屬性時,如果在本身的直屬屬性中沒有查找到該屬性,那么就會去查找function下的prototype的屬性。
Tip:我們可以通過hasOwnProperty方法來判斷某屬性是直屬于對象還是繼承自它的prototype屬性 car1.hasOwnProperty("material"); // true car2.hasOwnProperty("material"); // false "material" in car2;// true
構造函數+原型方式代碼如下
這個跟高級面向對象語言中的class的樣子更~加類似了吧?上述寫法只是在“語義”上達到了對類屬性和方法的封裝,很多面向對象思想的完美主義者希望在“視覺”上也達到封裝,因此就產生了“動態原型法”,請看下面的代碼:
function Car(color, title){ this.color = color; this.title = title; if (typeof Car._initialized == "undefined") { Car.prototype.start = function(){ alert("Bang!!!"); }; Car.prototype.material = "steel"; Car._initialized = true; } }
我們看,其實Car.prototype的屬性定義是可以被放進Car function的定義之中的,這樣就達到了“視覺”封裝。但是我們沒有單純的move,我們需要加一個條件,讓這些賦值操作只執行一次,而不是每次創建對象實例的時候都執行,造成內存空間的浪費。添加_initialized 屬性的目的就在于此。
對于所有用字面量創建的對象而言,其prototype對象均為Object.prototype(作為一個特殊對象,Object.prototype沒有原型對象):
var x = {a:18, b:28}; console.log(x.__proto__);//Object {}
而對于所有用new操作符創建的對象而言,其prototype對象均為constructor函數的prototype屬性:
var x = {a:18, b:28}; function Test(c){ this.c = c; } Test.prototype = x; var t = new Test(38); console.log(t);//Object {c=38, a=18, b=28} console.log(t.__proto__);//Object {a=18, b=28} console.log(t.__proto__.__proto__);//Object {}
總結出來一句話就是:用構造函數方式定義對象的所有非函數屬性,用原型方式定義對象的函數屬性。
整理于:
http://dbear.iteye.com/blog/613745
http://www.cnblogs.com/tomxu/archive/2012/02/21/2352994.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85657.html
摘要:是完全的面向對象語言,它們通過類的形式組織函數和變量,使之不能脫離對象存在。而在基于原型的面向對象方式中,對象則是依靠構造器利用原型構造出來的。 JavaScript 函數式腳本語言特性以及其看似隨意的編寫風格,導致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向對象的語言,或者只是部分具備一些面向對象的特征。本文將回歸面向對象本意,從對語言感悟的角度闡述為什...
摘要:作用域鏈的作用就是做標示符解析。事件循環還有個明顯的特點單線程。早期都是用作開發,單線程可以比較好當規避同步問題,降低了開發門檻。單線程需要解決的是效率問題,里的解決思想是異步非阻塞。 0、前言 本人在大學時非常癡迷java,認為java就是世界上最好的語言,偶爾在項目中會用到一些javascript,但基本沒放在眼里。較全面的接觸javascript是在實習的時候,通過這次的了解發現...
摘要:構造函數和實例都通過屬性指向了原形。代碼示例是構造函數的實例的屬性與的屬性保存的值相等,即他們指向同一個對象原形。 講清楚之javascript原型 標簽: javascript javascript 中原形是一個比較難于理解的概念。javascript 權威指南在原形這一章也花了大量的篇幅進行介紹,也許你已經讀過javascript 權威指南,或者已經是讀第N篇了,然而這篇文章的目...
摘要:深入理解原型與繼承看過不少書籍,不少文章,對于原型與繼承的說明基本上讓人不明覺厲,特別是對于習慣了面向對象編程的人來說更難理解,這里我就給大家說說我的理解。 深入理解:JavaScript原型與繼承 看過不少書籍,不少文章,對于原型與繼承的說明基本上讓人不明覺厲,特別是對于習慣了面向對象編程的人來說更難理解,這里我就給大家說說我的理解。 首先JavaScript是一門基于原型編程的語言...
摘要:當談到語言與其他編程語言相比時,你可能會聽到一些令人困惑東西,其中之一是工廠函數和構造函數。好的,讓我們用構造函數做同樣的實驗。當我們使用工廠函數創建對象時,它的指向,而當從構造函數創建對象時,它指向它的構造函數原型對象。 showImg(https://segmentfault.com/img/bVbr58T?w=1600&h=900); 當談到JavaScript語言與其他編程語言...
閱讀 3154·2021-11-22 14:45
閱讀 3300·2019-08-29 13:11
閱讀 2306·2019-08-29 12:31
閱讀 922·2019-08-29 11:21
閱讀 2991·2019-08-29 11:09
閱讀 3617·2019-08-28 18:11
閱讀 1420·2019-08-26 13:58
閱讀 1273·2019-08-26 13:27