首先是對(duì)“對(duì)象”的理解:
對(duì)象是一組沒(méi)有特定屬性的值,對(duì)象的每一個(gè)屬性或方法都有一個(gè)名字,而每一個(gè)名字都映射到一個(gè)值,其中值可以是數(shù)據(jù)或函數(shù)。每一個(gè)對(duì)象都是基于一個(gè)引用類型創(chuàng)建的,這個(gè)引用類型可以是原生類型,也可以是開(kāi)發(fā)人員自定義的類型?!叱?/pre>(好的,這里說(shuō)的比較不容易理解)
(不急,接下來(lái)再看)
JavaScript中,一切都是對(duì)象,函數(shù)也是對(duì)象,數(shù)組也是對(duì)象,但是數(shù)組是對(duì)象的子集,而對(duì)于函數(shù)來(lái)說(shuō),函數(shù)與對(duì)象之間有一種“雞生蛋蛋生雞”的關(guān)系。所有的對(duì)象都是由Object繼承而來(lái),而Object對(duì)象卻是一個(gè)函數(shù)。對(duì)象都是由函數(shù)來(lái)創(chuàng)建的。
比如,在控制臺(tái)中
輸入 typeof Object 結(jié)果是"function",
輸入 typeof Function 結(jié)果還是"function".(好的,是不是更懵逼了,不急,現(xiàn)在先看一下怎么創(chuàng)建對(duì)象以及各種方法的孰優(yōu)孰劣)
創(chuàng)建對(duì)象 0. 最基本的模式:var box=new Object(); //創(chuàng)建一個(gè) Object 對(duì)象 box.name="Lee"; //創(chuàng)建一個(gè) name 屬性并賦值 box.age= 100; //創(chuàng)建一個(gè) age 屬性并賦值 box.run= function(){ //創(chuàng)建一個(gè)run()方法并返回值 return this.name + this.age; }; console.log(box.run()); //輸出屬性和方法的值優(yōu)缺點(diǎn):
優(yōu)點(diǎn):簡(jiǎn)單
缺點(diǎn):產(chǎn)生大量代碼,封裝性差
1.工廠模式:fuction creatPerson(name,age,job){ var o = new Object(); //創(chuàng)建對(duì)象 o.name = name; //添加屬性 o.age = age; o.job = job; o.sayName = function(){ //添加方法 console.log(this.name); } return o; //返回對(duì)象引用 } var person1 = creatPerson("Nicholas",29,"engineer");//實(shí)例化 var person2 = creatPerson("Mike",28,"teacher");優(yōu)缺點(diǎn):
優(yōu)點(diǎn):解決了創(chuàng)建多個(gè)相似對(duì)象的問(wèn)題.
缺點(diǎn):但卻沒(méi)有解決對(duì)象識(shí)別問(wèn)題,即怎樣知道一個(gè)對(duì)象的類型。也就是,因?yàn)楦緹o(wú)法搞清楚他們到底是哪個(gè)對(duì)象的實(shí)例(這個(gè)可以和下面的構(gòu)造函數(shù)模式作對(duì)比)
2.構(gòu)造函數(shù)模式function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); } } //或 function Person(name,age,job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName;//注意這里不要寫括號(hào),要實(shí)現(xiàn)引用地址一致 } function sayName(){ console.log(this.name); }//這里是在外面寫一個(gè),但這種方法會(huì)有作用域問(wèn)題,十分不推薦 var person1 = new Person("Nicholas",29,"Engineer");//一般這樣實(shí)例化 var o = new Object;; Person.call(o,"Kkresten",25,"Nurse");//對(duì)象冒充法實(shí)例化區(qū)別:
沒(méi)有寫出new Object,但是后臺(tái)會(huì)自動(dòng) var obj = new Object,而this就相當(dāng)于obj
沒(méi)有 renturn 語(yǔ)句,在后臺(tái)返回
規(guī)范
函數(shù)名和實(shí)例化構(gòu)造名相同且大寫(非強(qiáng)制,主要是為了和普通函數(shù)區(qū)分開(kāi)來(lái))
通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象,必須使用 new 運(yùn)算符
優(yōu)缺點(diǎn)
優(yōu)點(diǎn): 創(chuàng)建自定義的構(gòu)造類型意味著將來(lái)可以將它的實(shí)例標(biāo)識(shí)為一種特定的類型,(可以用instanceof 來(lái)驗(yàn)證),即可識(shí)別(這里就可以和上面工廠模式作對(duì)比了,這也是比工廠模式更強(qiáng)的地方)
缺點(diǎn):每個(gè)方法都要在每個(gè)實(shí)例上創(chuàng)建一遍,大可不必(當(dāng)函數(shù)在內(nèi)部時(shí)),
全局作用域中定義的函數(shù)只能被某個(gè)對(duì)象調(diào)用,這讓全局作用域有點(diǎn)名不副實(shí),而且,如果對(duì)象需要定義很多方法,那么就要定義很多個(gè)全局函數(shù),于是,這個(gè)自定義的引用類型就絲毫沒(méi)有封可言了(函數(shù)定義在外部時(shí))。(所以函數(shù)在內(nèi)部和外部都有缺點(diǎn))
(好的,學(xué)到這里,你已經(jīng)大體掌握了怎么創(chuàng)建一個(gè)對(duì)象,接下來(lái)將開(kāi)始學(xué)習(xí)創(chuàng)建對(duì)象高大上的方法和概念)
3. 原型模式我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,(屬性值是對(duì)象)而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。:prototype 通過(guò) 調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象的原型對(duì)象。使用原型的好處可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。也就是說(shuō),不必在構(gòu)造函數(shù)中定義對(duì)象信息,而是可以直接將這些信息 添加到原型中?!叱?/pre>(好的,又回到了懵逼的狀態(tài)了,不急,先通過(guò)例子和圖來(lái)了解一下先)
function Person(){//構(gòu)造函數(shù) } Person.prototype.name = "Nicholas";//添加原型屬性 Person.prototype.age = 29; Person.prototype.job = "software Engineer"; Person.prototype.sayName = function() {//添加原型方法 console.log(this.name); } var person1 = new Person(); //實(shí)例化 person1.sayName(); //“Nicholas” var person2 = new Person(); //實(shí)例化 person2.sayName();//"Nicholas"如下圖:
而我自己畫(huà)了個(gè)圖來(lái)加深一下認(rèn)識(shí)(結(jié)合高程里的那段話)
(是不是有點(diǎn)懂了,接下來(lái)再逐個(gè)仔細(xì)分析)1.對(duì)于[[Prototype]]
每一個(gè)對(duì)象都有一個(gè)這樣的隱藏屬性,它引用了創(chuàng)建這個(gè)對(duì)象的函數(shù)的prototype原型對(duì)象,我們來(lái)看一張圖:
注意:函數(shù)也是對(duì)象,自然它也有__proto__。
在控制臺(tái)中,我們發(fā)現(xiàn):即函數(shù)的__proto__是函數(shù)類型。(也就說(shuō)函數(shù)的原型對(duì)象是函數(shù),而函數(shù)也是對(duì)象,所以函數(shù)的原型還是對(duì)象)(這里聽(tīng)著有點(diǎn)繞,但是可以先跳過(guò))
還要注意一個(gè)特例,如下圖:
這里,一切對(duì)象繼承自O(shè)bject,而我們又知道Object.prototype是它的原型對(duì)象,是一個(gè)對(duì)象,但是這個(gè)對(duì)象的__proto__卻為null,是否說(shuō)明構(gòu)建Object對(duì)象的函數(shù)沒(méi)有原型對(duì)象,因?yàn)?strong>對(duì)象都是由函數(shù)創(chuàng)建的
(對(duì)于函數(shù)與對(duì)象的關(guān)系和涉及到的原型鏈的相關(guān)知識(shí),還挺大挺深的,將多帶帶作為一個(gè)話題來(lái)討論。如果這里有點(diǎn)看得暈,可以先只是知道prototype是什么就可以了)
注意: __proto__這個(gè)指針沒(méi)有標(biāo)準(zhǔn)的方法訪問(wèn),IE 瀏覽器在腳本訪問(wèn)[[Prototype]]會(huì)不能識(shí)別,火狐和谷歌瀏覽器及其他某些瀏覽器均能識(shí)別。雖然可以輸出,但無(wú)法獲取內(nèi)部信息。([[Prototype]] 也可寫為_(kāi)_proto__)雖然無(wú)法訪問(wèn)到,但是可以通過(guò): Object.isPrototypeOf(person1)判斷這個(gè)實(shí)例對(duì)象是否指向它的原型對(duì)象 ;而我們也知道Person.prototype就是Object類型,即一個(gè)原型對(duì)象
//承接上面的代碼 Person.prototype.isPrototypeOf(person1);//true Person.prototype.isPrototypeOf(person2);//true2.對(duì)于原型模式的執(zhí)行流程:
①先檢查這個(gè)對(duì)象自身有無(wú)這個(gè)屬性;如果有,直接使用它。
②如果無(wú)法在對(duì)象自身找到需要的屬性,就會(huì)繼續(xù)訪問(wèn)對(duì)象的[[Prototype]]鏈,找到則直接使用,不再查找下去;如果一直找不到,最后就會(huì)返回undefined
3.可以通過(guò) hasOwnProperty()方法檢測(cè)屬性是否存在實(shí)例中,也可以通過(guò) in 來(lái)判斷 實(shí)例或原型中是否存在屬性;可以通過(guò)Object.keys()方法或Object.getOwnPropertyNames()來(lái)得到實(shí)例屬性,具體見(jiàn)高程。
4.優(yōu)缺點(diǎn):每添加一個(gè)屬性和方法就要敲一遍Person.prototype,而且視覺(jué)上說(shuō)封裝性不夠好。當(dāng)然優(yōu)點(diǎn)就是解決了上面構(gòu)造函數(shù)的問(wèn)題。
5.更簡(jiǎn)單的原型模式
function Person(){ } Person.prototype = { //將 Person.prototype 設(shè)置為等于一個(gè)以對(duì)象字面量形式創(chuàng)建的新對(duì)象 name : "Nicholas", age: 29, job: "software Engineer", sayName : function() { console.log(this.name); } } //(但constructor屬性不再指向Person了,而是指向Object構(gòu)造函數(shù)) //但可以這樣手動(dòng)設(shè)置: function Person(){ } Person.prototype = { constructor : Person,//手動(dòng)設(shè)置 name : "Nicholas", age: 29, job: "software Engineer", sayName : function() { console.log(this.name); } } //因?yàn)榘瓷厦娴姆绞綍?huì)導(dǎo)致它的[[Enumerable]]特性被設(shè)置為true,所以還可以像下面這樣 function Person(){ } Person.prototype = { name : "Nicholas", age: 29, job: "software Engineer", sayName : function() { console.log(this.name); } } Object.definePrototype(Person.prototype,"constructor"),{ enumerable : false; value : Person } }6.原型的動(dòng)態(tài)性:
//承接上面的Person構(gòu)造函數(shù) var friend = new Person(); Person.prototype.sayhi = function(){ alert("hi"); }; friend.sayhi(); //"hi"沒(méi)有問(wèn)題,雖然是在實(shí)例之后添加的屬性,但是根據(jù)原型模式的搜索機(jī)制,會(huì)找到原型中的這個(gè)方法,原因:實(shí)例與原型是松散連接的//但是:如果是這樣: function Person(){ } var friend = new Person(); Person.prototype = { name : "Nicholas", age: 29, job: "software Engineer", sayName : function() { console.log(this.name); } } friend.sayName();//Uncaught TypeError: friend.sayName is not a function,雖然有將重寫的原型的指針指向Person原型對(duì)象,但是很實(shí)際上卻如下圖:6.優(yōu)缺點(diǎn):
優(yōu)點(diǎn):解決了構(gòu)造函數(shù)出現(xiàn)的問(wèn)題(強(qiáng)大的類型識(shí)別)
缺點(diǎn):共享了引用類型的值,這個(gè)就是很少有人多帶帶使用原型模式的原因。比如下面:
function Person(){ } Person.prototype = { constructor : Person, name : "Nicholas", age: 29, job: "software Engineer", friend:["Mike","Jeny"], sayName : function() { console.log(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.friend.push("Van"); console.log(person1.friend);//"Mike,Jeny,Van" console.log(person2.friend);//"Mike,Jeny,Van"4.組合使用構(gòu)造函數(shù)和原型模式function Perosn(name,age,job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby","Court"]; } Person.prototype = { constructor : Person, sayName : function(){ console.log(this.name); } } var person1 = new Person("Nicholas",29," Engineer");優(yōu)缺點(diǎn):
優(yōu)點(diǎn):解決了引用類型實(shí)例共享的問(wèn)題
缺點(diǎn):封裝性不夠好
## 5.動(dòng)態(tài)原型模式 ##
function Person(name,age,job){ //屬性 this.name = name; this.age = age; this.job = job; //方法 if(typeof this.sayname != "function"){ Person.prototype.sayname = function(){ console.log(this.name);//只有在sayName方法不存在的情況下才會(huì)被添加到原型中 } } //這段代碼在初次調(diào)用構(gòu)造函數(shù)時(shí)才會(huì)執(zhí)行,此后,原型已經(jīng)初始化 var friend = new Person("Nicholas",29,"Engineer");優(yōu)缺點(diǎn):
優(yōu)點(diǎn):既得到了封裝,又實(shí)現(xiàn)了原型方法共享,并且屬性都保持獨(dú)立??梢哉f(shuō)是非常完美了,其實(shí)說(shuō)白了這種方法就是解決上面構(gòu)造函數(shù)的方法不需要每次都創(chuàng)建一遍的問(wèn)題。
缺點(diǎn):不能使用對(duì)象字面量重寫原型,會(huì)使之前定義的原型對(duì)象的方法失效。
(好了,學(xué)到這里,大概常用的創(chuàng)建對(duì)象的方法就已經(jīng)掌握了,接下來(lái)還有兩種不常用的方法可以了解一下)
6.寄生構(gòu)造函數(shù)模式function Person(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ console.log(this.name); } return o; } var friend = new Person("Nicholas",29,"Software Engineer"); function SpecialArray(){ //創(chuàng)建數(shù)組 var values = new Array(); //用push方法初始化數(shù)組的值 values.push.apply(values,arguments); //添加方法 values.toPipedString = function(){ return this.join("|"); } //返回?cái)?shù)組 return values; } var colors = new SpecialArray("red","blue","green"); console.log(colors.toPipedString()); //"red|blue|green"優(yōu)缺點(diǎn):
構(gòu)造函數(shù)返回的對(duì)象與在構(gòu)造函數(shù)外部創(chuàng)建的對(duì)象沒(méi)有什么不同,為此不能依賴instanceof操作符來(lái)確定對(duì)象的類型:
console.log(friend instanceof Person) // false因此,可以使用其他模式的情況下不使用此類型
7.穩(wěn)妥構(gòu)造函數(shù)模式function Person(name,age,job){ //創(chuàng)建要返回的對(duì)象 var o = new Object(); //可以在這里定義私有變量和函數(shù) //添加方法 o.sayName = function(){ console.log(name); } //返回對(duì)象 return o; } var friend = Person("Nicholas",29,"Software Engineer"); friend.sayName();區(qū)別:
不引用this的對(duì)象
不使用new操作符
優(yōu)點(diǎn):安全
(好了,js對(duì)象的創(chuàng)建就大概有這幾種方法,其實(shí)最常用的貌似還是構(gòu)造函數(shù)的模式,但是原型相關(guān)的東西也是必須要掌握的)
最后,歡迎大家圍觀指正!
參考:《javascript高級(jí)程序設(shè)計(jì)》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/95166.html
摘要:要用作原型的對(duì)象。函數(shù)對(duì)象可以創(chuàng)建普通對(duì)象,這個(gè)我們上面講過(guò)了回顧一下這是一個(gè)自定義構(gòu)造函數(shù)普通對(duì)象沒(méi)法創(chuàng)建函數(shù)對(duì)象,凡是通過(guò)創(chuàng)建的對(duì)象都是函數(shù)對(duì)象,其他都是普通對(duì)象通常通過(guò)創(chuàng)建,可以通過(guò)來(lái)判斷。 關(guān)于js的原型和原型鏈,有人覺(jué)得這是很頭疼的一塊知識(shí)點(diǎn),其實(shí)不然,它很基礎(chǔ),不信,往下看要了解原型和原型鏈,我們得先從對(duì)象說(shuō)起 創(chuàng)建對(duì)象 創(chuàng)建對(duì)象的三種方式: 對(duì)象直接量 通過(guò)對(duì)象直接量創(chuàng)建...
摘要:所以覺(jué)得把這個(gè)執(zhí)行的詳細(xì)過(guò)程整理一下,幫助更好的理解。類似的語(yǔ)法報(bào)錯(cuò)的如下圖所示三預(yù)編譯階段代碼塊通過(guò)語(yǔ)法分析階段之后,語(yǔ)法都正確的下回進(jìn)入預(yù)編譯階段。另開(kāi)出新文章詳細(xì)分析,主要介紹執(zhí)行階段中的同步任務(wù)執(zhí)行和異步任務(wù)執(zhí)行機(jī)制事件循環(huán)。 一、概述 js是一種非常靈活的語(yǔ)言,理解js引擎的執(zhí)行過(guò)程對(duì)于我們學(xué)習(xí)js是非常有必要的??戳撕芏噙@方便文章,大多數(shù)是講的是事件循環(huán)(event loo...
摘要:所以覺(jué)得把這個(gè)執(zhí)行的詳細(xì)過(guò)程整理一下,幫助更好的理解。類似的語(yǔ)法報(bào)錯(cuò)的如下圖所示三預(yù)編譯階段代碼塊通過(guò)語(yǔ)法分析階段之后,語(yǔ)法都正確的下回進(jìn)入預(yù)編譯階段。另開(kāi)出新文章詳細(xì)分析,主要介紹執(zhí)行階段中的同步任務(wù)執(zhí)行和異步任務(wù)執(zhí)行機(jī)制事件循環(huán)。 一、概述 js是一種非常靈活的語(yǔ)言,理解js引擎的執(zhí)行過(guò)程對(duì)于我們學(xué)習(xí)js是非常有必要的。看了很多這方便文章,大多數(shù)是講的是事件循環(huán)(event loo...
摘要:在基于原型的面向?qū)ο蠓绞街?,?duì)象則是依靠構(gòu)造函數(shù)和原型構(gòu)造出來(lái)的。來(lái)看下面的例子優(yōu)點(diǎn)與單純使用構(gòu)造函數(shù)不一樣,原型對(duì)象中的方法不會(huì)在實(shí)例中重新創(chuàng)建一次,節(jié)約內(nèi)存。 我們所熟知的面向?qū)ο笳Z(yǔ)言如 C++、Java 都有類的的概念,類是實(shí)例的類型模板,比如Student表示學(xué)生這種類型,而不表示任何具體的某個(gè)學(xué)生,而實(shí)例就是根據(jù)這個(gè)類型創(chuàng)建的一個(gè)具體的對(duì)象,比如zhangsan、lisi,由...
摘要:對(duì)象創(chuàng)建的三種方式字面量創(chuàng)建方式系統(tǒng)內(nèi)置構(gòu)造函數(shù)方式自定義構(gòu)造函數(shù)構(gòu)造函數(shù)原型實(shí)例之間的關(guān)系實(shí)例是由構(gòu)造函數(shù)實(shí)例化創(chuàng)建的,每個(gè)函數(shù)在被創(chuàng)建的時(shí)候,都會(huì)默認(rèn)有一個(gè)對(duì)象。 JS 對(duì)象創(chuàng)建的三種方式 //字面量創(chuàng)建方式 var person= { name:jack?。? //系統(tǒng)內(nèi)置構(gòu)造函數(shù)方式 var person= new Object(); person.name = jack; ...
摘要:執(zhí)行上下文作用域鏈和內(nèi)部機(jī)制一執(zhí)行上下文執(zhí)行上下文是代碼的執(zhí)行環(huán)境,它包括的值變量對(duì)象和函數(shù)。創(chuàng)建作用域鏈一旦可變對(duì)象創(chuàng)建完,引擎就開(kāi)始初始化作用域鏈。 執(zhí)行上下文、作用域鏈和JS內(nèi)部機(jī)制(Execution context, Scope chain and JavaScript internals) 一、執(zhí)行上下文 執(zhí)行上下文(Execution context EC)是js代碼的執(zhí)...
閱讀 707·2021-11-18 10:02
閱讀 2243·2021-11-15 18:13
閱讀 3165·2021-11-15 11:38
閱讀 2956·2021-09-22 15:55
閱讀 3680·2021-08-09 13:43
閱讀 2450·2021-07-25 14:19
閱讀 2459·2019-08-30 14:15
閱讀 3453·2019-08-30 14:15