摘要:創(chuàng)建對(duì)象兩個(gè)基本方法創(chuàng)建對(duì)象最基本的兩個(gè)方法是構(gòu)造函數(shù)和對(duì)象字面量。當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新的實(shí)例對(duì)象后,該實(shí)例內(nèi)部會(huì)有一個(gè)指針指向構(gòu)造函數(shù)的原型對(duì)象。碼農(nóng)構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。
前言:本文主要總結(jié)一下javascript創(chuàng)建對(duì)象的方法、原型、原型鏈和繼承,但是先從創(chuàng)建對(duì)象的幾種方法開(kāi)始,延伸到原型模式創(chuàng)建對(duì)象以及其它模式。繼承本來(lái)想一塊寫了,發(fā)現(xiàn)太多內(nèi)容了,放到下里總結(jié)。
1.創(chuàng)建對(duì)象 (1)兩個(gè)基本方法創(chuàng)建對(duì)象最基本的兩個(gè)方法是:Object構(gòu)造函數(shù)和對(duì)象字面量。
//Object構(gòu)造函數(shù)方式 var person = new Object(); person.name = "Jack"; person.age = 12; person.sayName = function(){ alert(this.name); }; //字面量方式 var person = { name: "Jack", age: 14, job: "碼農(nóng)", sayName: function(){ alert(this.name); } };(2)工廠模式
上述兩個(gè)基本方法的缺點(diǎn)是:使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量的復(fù)制代碼。針對(duì)這個(gè)缺點(diǎn),看下面
原理是用函數(shù)來(lái)封裝以特定接口創(chuàng)建對(duì)象的細(xì)節(jié)
function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Jack",15,"碼農(nóng)"); var person2 = createPerson("rose",12,"程序媛");
函數(shù)createPerson能接收參數(shù)構(gòu)建一個(gè)包含所有屬性的對(duì)象,并且可以用很少的代碼不斷的創(chuàng)建多個(gè)對(duì)象,但是由于它被函數(shù)所封裝,暴露的接口不能有效的識(shí)別對(duì)象的類型(即你不知道是Object還是自定義的什么對(duì)象)。
(3)構(gòu)造函數(shù)模式function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Jack",15,"碼農(nóng)"); //滿滿的java復(fù)古風(fēng) var person2 = new Person("Rose",15,"程序媛");
與工廠模式相比,構(gòu)造函數(shù)模式用Person()函數(shù)代替了createPerson()函數(shù),并且沒(méi)有顯示的創(chuàng)建對(duì)象,直接把屬性和方法賦值給了this對(duì)象。
要?jiǎng)?chuàng)建Person的實(shí)例,必須使用new關(guān)鍵字。
person1和person2都是Person的實(shí)例,這兩個(gè)對(duì)象都有一個(gè)constructor(構(gòu)造函數(shù))屬性,該屬性指向Person。 person1.constructor == Person; //true
person1即是Person的實(shí)例又是Object的實(shí)例,后面繼承原型鏈會(huì)總結(jié)。
(3).1構(gòu)造函數(shù)的使用//當(dāng)做構(gòu)造函數(shù)使用 var person1 = new Person("Jack",15,"碼農(nóng)"); person1.sayName(); //"Jack" //當(dāng)做普通函數(shù)使用 Person("Jack",16,"碼農(nóng)"); //注意:此處添加到了window window.sayName(); //"Jack" //在另一個(gè)對(duì)象的作用域中調(diào)用 var o = new Object(); Person.call(o,"Jack",12,"碼農(nóng)"); o.sayName(); //"Jack"
第一種當(dāng)做構(gòu)造函數(shù)使用就不多說(shuō)了
當(dāng)在全局作用域中調(diào)用Person("Jack",16,"碼農(nóng)");時(shí),this對(duì)象總是指向Global對(duì)象(瀏覽器中是window對(duì)象)。因此在執(zhí)行完這句代碼后,可以通過(guò)window對(duì)象來(lái)調(diào)用sayName()方法,并且返回“Jack”。
最后也可以使用call()或者apply()在某個(gè)特殊對(duì)象的作用域中調(diào)用Person()函數(shù)
(3).2存在的問(wèn)題在(3)構(gòu)造函數(shù)模式的代碼中,對(duì)象的方法sayName的功能都一樣,就是alert當(dāng)前對(duì)象的name。當(dāng)實(shí)例化Person之后,每個(gè)實(shí)例(person1和person2)都有一個(gè)名為sayName的方法,但是兩個(gè)方法不是同一個(gè)Function實(shí)例。不要忘了,js中函數(shù)是對(duì)象,所以每個(gè)實(shí)例都包含一個(gè)不同的Function實(shí)例,然而創(chuàng)建兩個(gè)功能完全一樣的Function實(shí)例是完全沒(méi)有必要的。因此可以把函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外。
如下代碼:
function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName(){ alert(this.name); } //實(shí)例化對(duì)象 var person1 = new Person("Jack",15,"碼農(nóng)"); //滿滿的java復(fù)古風(fēng) var person2 = new Person("Rose",15,"程序媛");
但是這樣依然存在問(wèn)題:
為了讓Person的實(shí)例化對(duì)象共享在全局作用域中定義的同一個(gè)sayName()函數(shù),我們把函數(shù)sayName()定義在全局作用域中,并通過(guò)指針sayName指向構(gòu)造函數(shù),所以在全局作用域中的sayName()只能被特定對(duì)象調(diào)用,全局作用域名不符實(shí),且污染全局變量。
并且如果對(duì)象需要很多種方法,那么就要定義很多全局函數(shù),對(duì)于對(duì)象就沒(méi)有封裝性,并且污染全局。
2.原型 (1)原型模式創(chuàng)建對(duì)象js不同于強(qiáng)類型語(yǔ)言的java,java創(chuàng)建對(duì)象的過(guò)程是由類(抽象)到類的實(shí)例的過(guò)程,是一個(gè)從抽象到具體的過(guò)程。
javascript則不同,其用原型創(chuàng)建對(duì)象是一個(gè)具體到具體的過(guò)程,即以一個(gè)實(shí)際的實(shí)例為藍(lán)本(原型),去創(chuàng)建另一個(gè)實(shí)例對(duì)象。
所以用原型模式創(chuàng)建對(duì)象有兩種方式:
1.Object.create()方法
Object.create:它接收兩個(gè)參數(shù),第一個(gè)是用作新對(duì)象原型的對(duì)象(即原型),一個(gè)是為新對(duì)象定義額外屬性的對(duì)象(可選,不常用)。
var Person = { name:"Jack", job:"碼農(nóng)" }; //傳遞一個(gè)參數(shù) var anotherPerson = Object.create(Person); anotherPerson.name //"Jack" //傳遞兩個(gè)參數(shù) var yetPerson = Object.create(Person,{name:{value:"Rose"}}); yetPerson.name; //Rose
2.構(gòu)造函數(shù)方法創(chuàng)建對(duì)象
任何一個(gè)函數(shù)都有一個(gè)prototype屬性(是一個(gè)指針),指向通過(guò)構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象的原型對(duì)象,原型對(duì)象可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。
因此不必在構(gòu)造函數(shù)中定義對(duì)象實(shí)例的信息,而是將這些屬性和方法直接添加到原型對(duì)象中,從而被實(shí)例對(duì)象多繼承(繼承后面總結(jié))
//第一步:用構(gòu)造函數(shù)創(chuàng)建一個(gè)空對(duì)象 function Person(){ } //第二步:給原型對(duì)象設(shè)置屬性和方法 Person.prototype.name = "Jack"; Person.prototype.age = 20; Person.prototype.job = "碼農(nóng)"; Person.prototype.sayName = function(){ alert(this.name); }; //第三步:實(shí)例化對(duì)象后,便可繼承原型對(duì)象的方法和屬性 var person1 = new Person(); person1.sayName(); //Jack var person2 = new Person(); person2.sayName(); //Jack alert(person1.sayName == person2.sayName); //true
person1和person2說(shuō)訪問(wèn)的是同一組屬性和同一個(gè)sayName()函數(shù)。
(2)理解原型對(duì)象只要?jiǎng)?chuàng)建一個(gè)函數(shù),就會(huì)為該函數(shù)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。
所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性,這個(gè)屬性包含一個(gè)指向prototype屬性所在函數(shù)的指針。
當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新的實(shí)例對(duì)象后,該實(shí)例內(nèi)部會(huì)有一個(gè)指針([prototype]/_proto_),指向構(gòu)造函數(shù)的原型對(duì)象。如下圖:
上圖中 :
Person.prototype指向了原型對(duì)象,而Person.prototype.construstor又指回了Person。
注意觀察原型對(duì)象,除了包含constructor屬性之外,還包括后來(lái)添加的其它屬性,這就是為什么每個(gè)實(shí)例化后的對(duì)象,雖然都不包含屬性和方法,但是都包含一個(gè)內(nèi)部屬性指向了Person.prototype,能獲得原型中的屬性和方法。
(3)判斷一個(gè)實(shí)例對(duì)象的原型這個(gè)方法叫:Object.getPrototypeOf(),如下例子:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Jack"
這個(gè)方法可以很方便的取得一個(gè)對(duì)象的原型
還可以利用這個(gè)方法取得原型對(duì)象中的name屬性的值。
(3)搜索屬性的過(guò)程當(dāng)我們?cè)趧?chuàng)建實(shí)例化的對(duì)象之后,調(diào)用這個(gè)實(shí)例化的對(duì)象的屬性時(shí),會(huì)先后執(zhí)行兩次搜索。
第一次搜索實(shí)例person1有name屬性嗎?沒(méi)有進(jìn)行第二次搜索
第二次搜索person1的原型有name屬性嗎?有就返回。
因此進(jìn)行一次思考,如果對(duì)實(shí)例進(jìn)行屬性重寫和方法覆蓋之后,訪問(wèn)實(shí)例對(duì)象的屬性和方法會(huì)顯示哪個(gè)?實(shí)例對(duì)象的還是對(duì)象原型的?
function Person(){ } Person.prototype.name = "Jack"; Person.prototype.age = 20; Person.prototype.job = "碼農(nóng)"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Rose"; alert(person1.name); //Rose alert(person2.name); //Jack
當(dāng)為對(duì)象實(shí)例添加一個(gè)屬性時(shí),這個(gè)屬性就會(huì)屏蔽原型對(duì)象中保存的同名屬性。
但是這個(gè)屬性只會(huì)阻止我們?cè)L問(wèn)原型中的那個(gè)屬性,而不會(huì)修改那個(gè)屬性
3.使用delete操作符可以刪除實(shí)例屬性,從而重新訪問(wèn)原型中的屬性。
function Person(){ } Person.prototype.name = "Jack"; Person.prototype.age = 20; Person.prototype.job = "碼農(nóng)"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Rose"; alert(person1.name); //Rose --來(lái)自實(shí)例 alert(person2.name); //Jack --來(lái)自原型 delete person1.name; alert(person1.name); //Jack --來(lái)自原型(4)判斷訪問(wèn)的到底是對(duì)象還是原型屬性
hasOwnProperty()可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是原型中,只有在給定屬性存在于對(duì)象實(shí)例中,才會(huì)返回true。
person1.hasOwnProperty("name"); //假設(shè)name存在于原型,返回false
in操作符會(huì)在通過(guò)對(duì)象能夠訪問(wèn)給定屬性時(shí)返回true,無(wú)論該屬性是存在于實(shí)例中還是原型中
"name" in person1 //true
所以通過(guò)這兩個(gè)可以封裝一個(gè)hasPrototypeProperty()函數(shù)確定屬性是不是原型中的屬性。
function hasPrototypeProperty(object,name){ return !object.hasOwnProperty(name) && (name in object); }(5)更簡(jiǎn)單的原型語(yǔ)法
前面每次添加一個(gè)屬性和方法都要寫一次Person.prototype,為了簡(jiǎn)便可以直接這樣
function Person(){ } Person.prototype = { name:"Jack", age:20, job:"碼農(nóng)", sayName:function(){ alert("this.name"); } };
上述代碼直接將Person.prototype設(shè)置為等于一個(gè)以對(duì)象字面量形式創(chuàng)建的新對(duì)象
上述這么做時(shí):constructor屬性就不再指向Person了。
本質(zhì)上完全重寫了默認(rèn)的prototype對(duì)象,因此constructor屬性也就變成了新對(duì)象的constructor屬性(指向Object構(gòu)造函數(shù))。
因此如果constructor值很重要,可以在Person.prototype中設(shè)置回適當(dāng)?shù)闹担?br>如上例中可以添加:constructor:Person,
(6)原型的動(dòng)態(tài)性我們對(duì)原型對(duì)象所做的任何修改都會(huì)立即從實(shí)例上反映出來(lái)-即使先創(chuàng)建實(shí)例對(duì)象后修改原型也如此
var friend = new Person(); Person.prototype.sayHi = function(){ alert("Hi"); }; friend.sayHi(); //"Hi"
盡管可以隨時(shí)為原型添加屬性和方法,并且修改能立即在實(shí)例對(duì)象中體現(xiàn)出來(lái),但是如果重寫整個(gè)原型對(duì)象,就不一樣了。看下面例子:
function Person(){ } var friend = new Person(); Person.prototype = { constructor:Person, name:"Jack", age:20, sayName:function(){ alert(this.name); } }; friend.sayName(); //error
上述代碼先創(chuàng)建了一個(gè)Person實(shí)例,然后又重寫了其原型對(duì)象,在調(diào)用friend.sayName()時(shí)發(fā)生錯(cuò)誤。
因?yàn)閒riend指向的原型中不包含以該名字命名的屬性。關(guān)系如下圖:
省略了為構(gòu)造函數(shù)初始化參數(shù)這一環(huán)節(jié),結(jié)果是所有實(shí)例都取得相同的屬性,但問(wèn)題不大,可以為實(shí)例對(duì)象重寫屬性來(lái)解決。
2.但是,對(duì)于包含引用類型值的屬性來(lái)說(shuō),問(wèn)題就比較突出了,因?yàn)橐妙愋椭?,屬性名只是一個(gè)指針,在實(shí)例中重寫該屬性并沒(méi)有作用。指針始終指向原來(lái)的。
如下例子:
function Person(){} Person.prototype = { constructor:Person, name:"Jack", job:"碼農(nóng)", friends:["路人甲","路人乙","路人丙"], }; var person1 = new Person(); var person2 = new Person(); person1.friends.push("路人丁"); alert(person1.friends); //["路人甲","路人乙","路人丙","路人丁"] alert(person2.friends); //["路人甲","路人乙","路人丙","路人丁"] alert(person1.friends === person2.friends); //true
上面這個(gè),假如每個(gè)實(shí)例對(duì)象的引用值屬性不一樣,則無(wú)法修改。
3.組合使用構(gòu)造函數(shù)和原型模式構(gòu)造函數(shù)模式用于定義實(shí)例屬性
原型模式用于定義方法和共享的屬性
如下代碼:
function Person(name,age,job){ this.name = name; this.job = job; this.age = age; this.friends = ["路人甲","路人乙"]; } Person.prototype = { constructor:Person, sayName: function(){ alert(this.name); } } var person1 = new Person("Jack", 20, "碼農(nóng)"); var person2 = new Person("Rose", 20, "程序媛"); person1.friends.push("路人丁"); alert(person1.friends); //["路人甲","路人乙","路人丁"] alert(person2.friends); //["路人甲","路人乙"] alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true4.寄生構(gòu)造函數(shù)模式
該模式基本思想是創(chuàng)建一個(gè)函數(shù),該函數(shù)作用僅僅是封裝創(chuàng)建對(duì)象的代碼,然后返回新創(chuàng)建的對(duì)象。
function Person(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var friend = new Person("Jack", 16, "碼農(nóng)"); friend.sayName(); //Jack
構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。
通過(guò)在構(gòu)造函數(shù)末尾添加一個(gè)return語(yǔ)句,可以重寫調(diào)用構(gòu)造函數(shù)時(shí)返回的值。
這個(gè)方法的用處是:可以創(chuàng)建一個(gè)額外方法的特殊的數(shù)組(因?yàn)樵鷮?duì)象Array的構(gòu)造函數(shù)不能直接修改)
function SpecialArray(){ //創(chuàng)建數(shù)組 var values = new Array(); //添加值 values.push.apply(values,arguments); //添加方法 values.toPipedString = function(){ return this.join("|"); }; //返回?cái)?shù)組 return values; } var colors = new SpecialArray("black","red","blue"); alert(colors.toPipedString());
本來(lái)想接著寫繼承的,發(fā)現(xiàn)實(shí)在太多了,分成兩篇吧。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79979.html
摘要:當(dāng)調(diào)用的構(gòu)造函數(shù)時(shí),在函數(shù)內(nèi)部又會(huì)調(diào)用的構(gòu)造函數(shù),又在新對(duì)象上創(chuàng)建了實(shí)例屬性和,于是這兩個(gè)屬性就屏蔽了原型中的同名屬性。 前言:這次對(duì)上篇收個(gè)尾,主要總結(jié)一下javascript的繼承。 1.原型鏈 js中原型鏈?zhǔn)菍?shí)現(xiàn)繼承的主要方法。基本思想是:利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。我們來(lái)簡(jiǎn)單回顧一下以前的內(nèi)容: 每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象 每個(gè)原型對(duì)象都包含一個(gè)指...
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:執(zhí)行構(gòu)造函數(shù)的一步說(shuō)明對(duì)象可以通過(guò)函數(shù)來(lái)創(chuàng)建。是最頂級(jí)的構(gòu)造函數(shù),對(duì)象里面,就有好幾個(gè)其他屬性。構(gòu)造函數(shù)與普通函數(shù)并沒(méi)有區(qū)別,只是調(diào)用方式不同。 主要問(wèn)題:1、構(gòu)造函數(shù)和普通函數(shù)有區(qū)別么?什么區(qū)別?2、prototype和__proto__有什么不同?3、instanceof的作用機(jī)制,為什么有限制?4、ES6的相關(guān)方法,Class繼承原理? 三、對(duì)象與原型 (一)、數(shù)據(jù)類型 Js...
閱讀 2659·2021-11-11 16:55
閱讀 697·2021-09-04 16:40
閱讀 3093·2019-08-30 15:54
閱讀 2635·2019-08-30 15:54
閱讀 2424·2019-08-30 15:46
閱讀 418·2019-08-30 15:43
閱讀 3241·2019-08-30 11:11
閱讀 2995·2019-08-28 18:17