摘要:創(chuàng)建實例的方式有三種對象字面量表示法操作符跟構(gòu)造函數(shù)中的函數(shù)。下面主要講的是最為復(fù)雜的操作符跟構(gòu)造函數(shù)的創(chuàng)建對象實例的方法。
創(chuàng)建對象 一.創(chuàng)建對象的方法
理解原型對象:
無論什么時候,只要創(chuàng)建了新函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個 prototype屬性,這個屬性指向函數(shù)的原型對象。在默認(rèn)情況下,所有原型對象都會自動獲得一個constructor屬性,這個屬性包含一個指向prototype屬性的所在函數(shù)的指針。(重寫原型屬性會默認(rèn)取消constructor屬性)詳細(xì)可見文章下圖。
創(chuàng)建object實例的方式有三種:對象字面量表示法、new操作符跟object構(gòu)造函數(shù)、(ECMAScrript5中的)object.create()函數(shù)。下面主要講的是最為復(fù)雜的new操作符跟object構(gòu)造函數(shù)的創(chuàng)建對象實例的方法。
1.1工廠模式
function createPerson(name,age,job){ var o=new Object();//創(chuàng)建對象實例 o.name=name;//為對象實例添加屬性 o.age=age; o.job=job; o.sayName=function(){ alert(this.name); } return o;//返回剛創(chuàng)建的對象實例 } var person1= createPerson("NIcho",29,"software engineer");
工廠模式的問題:
工廠模式雖然解決了創(chuàng)建多個相似對象的問題,但是沒有解決對象識別的的問題(即怎樣知道一個對象的類型)。
1.2.構(gòu)造函數(shù)模式
應(yīng)用:創(chuàng)建對象的自有屬性的主要方法,每定義一個函數(shù)就實例化了一個對象.
function Person(name,age,job){
this.name=name; this.age=age; this.job=job; this.sayName=function(){ alert(this.name);
}
//將構(gòu)造的函數(shù)Person實例化,傳入的參數(shù)作為自有屬性,并且繼承自Person.prototype var person1=new Person(NIcho",29,"software engineer); var person2=new Person(Jhon",26,"software engineer);
注意:1.alert(Person.name);//undefined
Person函數(shù)代替了createPerson函數(shù),還有以下不同之處:
(1)沒有顯式創(chuàng)建對象
(2)直接將屬性和方法賦給this對象(這里的this指的就是這個構(gòu)造的函數(shù))
(3)沒有return 語句
我們在這個例子中創(chuàng)建的對象既是object的實例又是person的實例(所有對象均繼承自object。object可以理解為對象的根原型,我們所用到的所有對象方法的操作,如toString 、substring 、splice等都是繼承自object)
alert(person1 instanceOf Object);//true alert(person1 instanceOf Person);//true
構(gòu)造函數(shù)模式的問題:
使用構(gòu)造函數(shù)的主要問題是,每個方法都要在每個實例上創(chuàng)建一次。person1和person2都有一個sayName的方法,但兩個方法不是同一個Function實例(ECMAScript中的函數(shù)是對象,每定義一個函數(shù)就是實例化了一個對象)。
如果沒有自定義向person原型中添加屬性,實例化之后則得到Person自有屬性的一個副本,并且繼承object。(注:person1與person2中sayName()引用位置不同)
圖1構(gòu)造函數(shù)模式下的實例化對象
通過把函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外來解決:
//全局函數(shù) function sayName(){ alert(this.name); }
對應(yīng)的:this.syName:sayName;
1.3原型模式
應(yīng)用:用于定義實例共享的屬性和方法
function Person(){ } Person.prototype.name="Nicho"; Person.prototype.age=29; Person.prototype.jod="software engineer"; Person.prototype.sayName=function(){alert(this.name)}; var person1=new Person(); person1.sayName();//"Nicho" 繼承Person.prototype var person2=new Person(); person2.sayName();//"Nicho"繼承Person.prototype alert(person1.sayName==person2.sayName);//true 引用位置相同
圖2 person1、person2繼承自person.prototype
(注:繼承連接存在與實例與構(gòu)造函數(shù)的原型之間而不是實例與構(gòu)造函數(shù)之間)
關(guān)于原型上的一些函數(shù):
1.檢測obj是否為obj1的原型:obj.isPrototypeOf(obj1)//返回true/false
2.獲得obj1的原型:object.getPrototypeOf(obj1)
3.檢測一個屬性是否存在于實例中(也就是自有屬性)還是原型中(來自于繼承):
Obj1.hasOwnProperty();//如果obj1有自有屬性則返回true,否則返回false
1.4組合使用構(gòu)造函數(shù)模式和原型模式
組合使用構(gòu)造函數(shù)模式和原型模式既可以繼承原型對象的屬性,又可以設(shè)置自由屬性
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } Person.prototype={ constructor:Person, sayName:function(){ alert("Hi"); } } var person1=new Person(“Nicho”,29,”software engineer”); //person1自有屬性:name aga job;原型屬性(來自繼承):constructor sayName
代碼讀取某個對象的某個屬性時的搜索方法:
搜索首先從對象實例開始,如果實例中找到了給定名字的屬性,則返回該屬性的值。如果沒有找到,則繼續(xù)搜索指針指向的原型對象,在原型對象中查找給定名字的屬性。如果在原型對象中找到了該屬性,則返回屬性的值,否則一直向上查找給定名字的屬性,直到object。如果沒有找到則返回undefined。
這就需要我們注意:雖然我們可以通過對象實例訪問原型中的值,但卻不能通過對象實例(注意!!這里說的是通過對象實例而不是在原型中修改屬性)來重寫原型中的值。如果在實例中添加了一個與實例原型中同名的屬性,該屬性將會屏蔽原型中的那個屬性。來看下面的的例子:
function Person(){ } Person.prototype.name="Nicho"; Person.prototype.age=29; Person.prototype.jod="software engineer"; Person.prototype.sayName=function(){alert(this.name)}; var person1=new Person(); var person2=new Person(); person1.name="Greg"; alert(person1.name);//Greg-來自實例 alert(person2.name);//Nicho-來自原型; //person1對屬性 name的修改并不能修改原型上的相應(yīng)屬性,因此person2繼承自原型
1.person1=new Person()與 person3=person1是不同的:前者是實例化一個對象,
后者遵循復(fù)制函數(shù)(對象引用)的原理
2.對對象整體進(jìn)行操作(引用)和對對象中的某個屬性(值)進(jìn)行操作實現(xiàn)原理是不同的
1). 整個對象的復(fù)制即引用相同
var person3=person1;//整個對象的復(fù)制即引用相同(指針指針指針!只是一個索引,實際存儲都不在指針位置下) person3.name=”newName”;//修改屬性名稱的值 alert(person1.name);//newName;//另一個來自此處的引用屬性的值會修改;即復(fù)制對象原理 alert(person1.isPrototypeOf(person3));//false alert(Person.prototype.isPrototypeOf(person3));//true
2).簡單的屬性的復(fù)制,并非引用同一個屬性值.
var person1=new Person("NIcho",29,"software engineer"); var person2=new Person("HIcho",9,"Hardware engineer"); person1.name=person2.name;//簡單的屬性的復(fù)制,并非引用同一個屬性值 person2.name="NEW";//嘗試賦新值 alert(person1.name);//HIcho,說明是值的傳遞而并非引用二.繼承
1.ECMAScript只支持實現(xiàn)繼承,而且實現(xiàn)繼承主要是依靠原型鏈來實現(xiàn)的。
2.其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。(即:原型鏈的構(gòu)建是通過將一個類型的實例賦值給另一個構(gòu)造函數(shù)的原型來實現(xiàn)的)
2.1原型鏈的繼承
function superType(){ this.property=true; } superType.prototype.getSuperValue=function(){ return this.property; }; function subType(){ this.subProperty=false; } //繼承superType subType.prototype=new superType(); subType.prototype.getSubValue=function(){ return this.subProperty; }; var instance=new subType(); alert((instance.getSuperValue)());//true getSuperValue來自superType.prototype alert(instance.property);//true property來自superType實例
實現(xiàn)的本質(zhì)是重寫原型對象,代之以一個新類型的實例。換句話說,原來存在于superType的實例的所有屬性和方法(包括自有屬性和繼承屬性),現(xiàn)在也存在于subType.prototype中了。
圖4 subType繼承了superType
(此時instance.constructor現(xiàn)在指向的是superType,因為subType的原型指向了另一個對象superType的原型,而這個原型對象的constructor指向的是superType)
注意:
1.別忘記默認(rèn)的對象Object.prototype: subType繼承了superType,superType繼承了Object.prototype
確定原型和實例的關(guān)系:
(1)instanceOf;測試實例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù)
alert(instance instanceOf superType);//true alert(instance instanceOf subType);//true
(2)isPrototypeOf:只要原型鏈中出現(xiàn)過的原型都可以說是該原型鏈所派生出來的實例的原型
alert(superType.protoType.isPrototypeOf (instance));//true alert(subType.protoType.isPrototypeOf (instance));//true
原型鏈的問題:
在通過原型來實現(xiàn)繼承時,原型實際上會變成另一個類型的實例。于是,原先的實例屬性也就順理成章地變成了現(xiàn)在的原型屬性了。
2.2解決原型鏈繼承帶來的問題的方法
解決這個問題的技術(shù)是借用構(gòu)造函數(shù),使用最多的繼承模式是組合繼承。此外還有原型式繼承,寄生式繼承,寄生組合繼承。這里主要講借用構(gòu)造函數(shù)和組合繼承。
(1)借用構(gòu)造函數(shù)
//在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型的構(gòu)造函數(shù)
function superType(){ this.colors=["red","blue","green"]; } function subType(){ //繼承了superType superType.call(this);//在新創(chuàng)建的subType實例的環(huán)境下調(diào)用superType函數(shù) //這樣一來,就會在新subType對象上執(zhí)行superType函數(shù)中定義的所有初始化代碼 //結(jié)果subType的每一個實例都會具有自己的color屬性的副本了 } var instance1=new subType(); instance1.colors.push("black"); alert(instance1.colors);//red,blue,green,black var instance2=new subType(); alert(instance2.colors);//red,blue,green
(2)組合繼承
思路是使用原型鏈實現(xiàn)對屬性和方法的繼承,而通過借用函數(shù)來實現(xiàn)對實例屬性的繼承
function superType(name){//超類中定義,為以后繼承該實例屬性(子類的原型)做準(zhǔn)備 this.name=name; this.colors=["red","blue","green"]; } superType.prptotype.sayName=function(){ alert(this.name); }// superType.prptotype新增屬性,位于繼承的更高層 function subtype(name,age){//創(chuàng)建自有屬性 superType.call(this,name); //繼承屬性式的創(chuàng)建 this.age=age;//直接新增 } //繼承方法 subtype.prototype=new superType(); subtype.prototype.constructor=subtype; subtype.prototype.sayAge=function(){//subtype.prototype為自己新增方法 alert(this.age); } var instance1=new subType(“Nicho”,29); instance1.colors.push("black"); alert(instance1.colors);//red,blue,green,black instance1.sayName();//”Nicho” instance1.sayAge();//29 var instance2=new subType(“Greg”,27); alert(instance2.colors);//red,blue,green instance2.sayName();//”Greg” instance2.sayAge();//27
總結(jié):
1.subType的自有屬性: this.age=age
2.subType的繼承屬性:
(1)superType.prptotype.sayName 、subtype.prototype.sayAge
(2)function superType(name){
this.name=name; this.colors=["red","blue","green"]; }
3.
下面讓我們看下一種情況:重寫原型對象(完全重寫而不是簡單的添加原型對象的屬性)
我們知道,在調(diào)用構(gòu)造函數(shù)是會為實例添加一個prototype指針,而把這個原型修改為另一個對象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。請記住:實例中的指針僅指向原型而不指向構(gòu)造函數(shù)。
function Person(){}; var friend=new Person(); Person.prototype={ Constructor:Person, Name:“Nicholas”, Age:29, sayName=function(){ alert(“this.name”); } } friend.sayName();//error
重寫原型對象之前:
重寫原型對象之后:
由圖可見:重寫原型對象切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對象實例之間的聯(lián)系,它們引用的仍是最初的原型
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79098.html
摘要:在節(jié)中,我們學(xué)習(xí)到了通過構(gòu)造函數(shù)創(chuàng)建對象的三個重要步驟,其中的一步是把構(gòu)造函數(shù)的對象設(shè)置為創(chuàng)建對象的原型。利用而不是直接用創(chuàng)建一個實例對象的目的是,減少一次調(diào)用父構(gòu)造函數(shù)的執(zhí)行。 JavaScript語言不像面向?qū)ο蟮木幊陶Z言中有類的概念,所以也就沒有類之間直接的繼承,JavaScript中只有對象,使用函數(shù)模擬類,基于對象之間的原型鏈來實現(xiàn)繼承關(guān)系,ES6的語法中新增了class關(guān)鍵...
摘要:除了以上介紹的幾種對象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點:封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個類繼承(復(fù)制)另一個類的屬性/方法 多態(tài):方...
摘要:并且,在創(chuàng)建子類型的實例時,無法向超類型的構(gòu)造函數(shù)傳遞參數(shù)。借用構(gòu)造函數(shù)經(jīng)典繼承在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。缺點是同樣具有構(gòu)造函數(shù)模式創(chuàng)建對象的固有弊端構(gòu)造函數(shù)中煩人方法函數(shù)對象重復(fù)創(chuàng)建。 創(chuàng)建對象的幾種方式 在邏輯上從低級到高級:工廠模式、構(gòu)造函數(shù)模式、原型模式、組合模式。當(dāng)然還有其他模式,但是這四者邏輯關(guān)系強,總結(jié)起來很有感覺。之所以和繼承一起分析,也是因為邏輯關(guān)系很...
摘要:綜上所述有原型鏈繼承,構(gòu)造函數(shù)繼承經(jīng)典繼承,組合繼承,寄生繼承,寄生組合繼承五種方法,寄生組合式繼承,集寄生式繼承和組合繼承的優(yōu)點于一身是實現(xiàn)基于類型繼承的最有效方法。 一、前言 繼承是面向?qū)ο螅∣OP)語言中的一個最為人津津樂道的概念。許多面對對象(OOP)語言都支持兩種繼承方式::接口繼承 和 實現(xiàn)繼承 。 接口繼承只繼承方法簽名,而實現(xiàn)繼承則繼承實際的方法。由于js中方法沒有簽名...
摘要:由構(gòu)造函數(shù)返回的對象就是表達(dá)式的結(jié)果。如果構(gòu)造函數(shù)沒有顯式返回一個對象,則使用步驟創(chuàng)建的對象。運算符返回一個布爾值,表示對象是否為某個構(gòu)造函數(shù)的實例。 面向?qū)ο?本人能力有限,有誤請斧正 本文旨在復(fù)習(xí)面向?qū)ο?不包含es6) 本文學(xué)習(xí)思維 創(chuàng)建對象的方式,獲取對象屬性 構(gòu)造函數(shù),構(gòu)造函數(shù)的new 做了什么 原型與原型對象 原型鏈 繼承(借用構(gòu)造繼承、原型繼承、組合繼承、寄生組合繼承)...
閱讀 1174·2021-09-27 13:34
閱讀 981·2021-09-13 10:25
閱讀 511·2019-08-30 15:52
閱讀 3450·2019-08-30 13:48
閱讀 648·2019-08-30 11:07
閱讀 2167·2019-08-29 16:23
閱讀 1993·2019-08-29 13:51
閱讀 2328·2019-08-26 17:42