摘要:創(chuàng)建對(duì)象在之前說(shuō)過(guò)通過(guò)構(gòu)造函數(shù)或者對(duì)象字面量的方式可以創(chuàng)建對(duì)象。寄生構(gòu)造函數(shù)模式寄生構(gòu)造函數(shù)模式的基本思想是創(chuàng)建一個(gè)函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對(duì)象的代碼,然后在返回新創(chuàng)建的對(duì)象。
在之前說(shuō)過(guò)通過(guò)Object構(gòu)造函數(shù)或者對(duì)象字面量的方式可以創(chuàng)建對(duì)象。但是這些方式有一個(gè)明顯的問(wèn)題,使用同一個(gè)接口創(chuàng)建很多對(duì)象會(huì)產(chǎn)生大量的重復(fù)代碼。例如:
//如果你要?jiǎng)?chuàng)建三個(gè)對(duì)象,通過(guò)Object構(gòu)造函數(shù)你就要new三個(gè)Object構(gòu)造函數(shù) var obj1 = new Object() var obj2 = new Object() var obj3 = new Object()
工廠模式抽象的創(chuàng)建了具體對(duì)象的過(guò)程,通過(guò)用函數(shù)封裝以特定接口創(chuàng)建對(duì)象。
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("Nicholas", 29, "software enginner") var person2 = createPerson("greg", 27, "doctor")
如上,通過(guò)調(diào)用createPerson函數(shù),然后傳入特定的參數(shù)可以創(chuàng)建特定的對(duì)象,雖然工廠模式解決了創(chuàng)建多個(gè)相似對(duì)象的問(wèn)題,但是沒(méi)有解決對(duì)象識(shí)別的問(wèn)題(即怎樣知道一個(gè)對(duì)象的類型)。
ECMAScript中的構(gòu)造函數(shù)可以用來(lái)創(chuàng)建特定類型的對(duì)象,像Object和Array這樣的原生構(gòu)造函數(shù)在運(yùn)行時(shí)會(huì)自動(dòng)出現(xiàn)在執(zhí)行環(huán)境中。這樣我們也可以創(chuàng)建自定義的構(gòu)造函數(shù),從而定義自定義對(duì)象類型的屬性和方法。
function Person(name, age, job){ this.name = name this.age = age this.job = job this.sayName = function() { alert(this.name) } } var person1 = new Person("Nicholas", 29, "software enginner") var person2 = new Person("Grey", 27, "Doctor")
如上例子,Person構(gòu)造函數(shù)取代了之前的createPerson工廠函數(shù)。與之前的工廠函數(shù)創(chuàng)建對(duì)象的方法有以下幾點(diǎn)區(qū)別:
1. 沒(méi)有顯示的創(chuàng)建對(duì)象 2. 直接將屬性和方法賦給了this對(duì)象 3. 沒(méi)有return語(yǔ)句
還有一點(diǎn)可以看到,我們是通過(guò)new操作符創(chuàng)建對(duì)象,那么使用new操作符發(fā)生了什么呢?
1. 創(chuàng)建一個(gè)新的對(duì)象 2. 將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this指向了這個(gè)新對(duì)象) 3. 執(zhí)行構(gòu)造函數(shù)中的代碼 4. 返回一個(gè)新對(duì)象
在前面例子的最后,person1和person2分別保存著Person的一個(gè)不同實(shí)例,這兩個(gè)對(duì)象都有一個(gè)constructor(構(gòu)造函數(shù))屬性,該屬性指向Person。
console.log(person1.constructor) //? Person(name, age, job){ // this.name = name // this.age = age // this.job = job // this.sayName = function() { // alert(this.name) // } //} person1.constructor == Person //true person2.constructor == Person //true
對(duì)象的constructor屬性最初是用來(lái)標(biāo)識(shí)對(duì)象類型的。但是,提到檢測(cè)對(duì)象類型,還是instranceof操作符更可靠點(diǎn)。我們?cè)诶又袆?chuàng)建的所有對(duì)象既是Object的實(shí)例,同時(shí)也是Person的實(shí)例。
console.log(person1 instanceof Object) //true console.log(person1 instanceof Person) // true console.log(person2 instanceof Object) //true console.log(person2 instanceof Person) // true
創(chuàng)建自定義的構(gòu)造函數(shù)意味著可以將它的實(shí)例標(biāo)識(shí)為一種特定的類型,這正是構(gòu)造函數(shù)模式勝過(guò)工廠模式的地方。
構(gòu)造函數(shù)也是函數(shù),與普通函數(shù)之間的區(qū)別就是構(gòu)造函數(shù)和普通函數(shù)調(diào)用的方式不一樣。
function Person(name, age, job){ this.name = name this.age = age this.job = job this.sayName = function() { alert(this.name) } } //當(dāng)作構(gòu)造函數(shù)使用 var person = new Person("Nicholas", 29, "software engineer") //作為普通函數(shù)使用 Person("gray", 29, "doctor") window.sayName() //gray //再另一個(gè)對(duì)象的作用域中調(diào)用 var o = new Object() Person.call(o, "kir", 25, "nurse") o.sayName() //"nurse"
構(gòu)造函數(shù)創(chuàng)建對(duì)象的缺點(diǎn):構(gòu)造函數(shù)方式創(chuàng)建對(duì)象相對(duì)于工廠函數(shù)的優(yōu)勢(shì)就是可以將它的實(shí)例作為一種特定類型。但是通過(guò)構(gòu)造函數(shù)方式創(chuàng)建的對(duì)象,每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一次。在之前的例子上person1和person2都有一個(gè)名為sayName的方法,但是那兩個(gè)方法不是同一個(gè)Function實(shí)例。改寫下之前的構(gòu)造函數(shù)方式就會(huì)很清楚:
function Person(name, age, job) { this.name = name this.age = age this.job = job this.sayName = new Function("alert(this.name)") }
從改寫的例子上可以看到,每個(gè)Person實(shí)例都包含一個(gè)不同的Function實(shí)例。然而創(chuàng)建兩個(gè)完成相同功能的Function實(shí)例是沒(méi)有必要的。況且有this對(duì)象的存在,根本不用在執(zhí)行代碼前就把函數(shù)綁定到特定對(duì)象上面,因此可以像下面這樣把函數(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) }
如上,通過(guò)Person構(gòu)造函數(shù)創(chuàng)建的對(duì)象都共享全局作用域中的sayName函數(shù)。但是這樣的寫法總感覺(jué)有點(diǎn)不合適,如果對(duì)象有很多方法,那么就要在全局作用域中創(chuàng)建很多函數(shù)。接下來(lái)介紹一個(gè)原型模式可以解決這個(gè)問(wèn)題。
我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性時(shí)一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。
通過(guò)字面意思了解,prototype就是通過(guò)調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象實(shí)例的原型對(duì)象,使用原型對(duì)象的好處是可以讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。
function Person() {} Person.prototype.name = "Nicholas" Person.prototype.age = 29 Person.prototype.job = "software engineer" Person.prototype.sayName = function() { alert(this.name) } var person1 = new Person() person1.sayName() //"Nicholas" var person2 = new Person() person2.sayName() //"Nicholas" console.log(person1.sayName == person2.sayName) //true
如上可以看到,創(chuàng)建的對(duì)象的這些屬性和方法是由所有實(shí)例共享的,換句話說(shuō),person1和person2訪問(wèn)的都是同一組屬性和同一個(gè)sayName函數(shù)。
首先我們理解下什么是原型對(duì)象?
當(dāng)我們創(chuàng)建任意一個(gè)新函數(shù)的時(shí)候,就會(huì)根據(jù)特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。默認(rèn)情況下,原型對(duì)象會(huì)有一個(gè)constructor屬性,該屬性指向prototype屬性所在函數(shù)的指針。
function Person() {} //Person.prototype.constructor 指向的就是Person構(gòu)造函數(shù), 通過(guò)這個(gè)構(gòu)造函數(shù)我們可以繼續(xù)為原型對(duì)象添加屬性和方法
創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對(duì)象默認(rèn)有constructor屬性,至于其他方法都是從Object繼承而來(lái)。當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例后,該實(shí)例內(nèi)部將包含一個(gè)指針(內(nèi)部屬性)指向構(gòu)造函數(shù)的原型對(duì)象。通常我們是用(__proto__)來(lái)表示的。這個(gè)連接是存在于對(duì)象實(shí)例和構(gòu)造函數(shù)原型對(duì)象之間的,而不是對(duì)象實(shí)例和構(gòu)造函數(shù)之間。
isPrototypeOf( )
我們可以通過(guò)isPrototypeOf( )方法來(lái)判斷對(duì)象實(shí)例的__proto__是否指向?qū)?yīng)的原型對(duì)象。
Person.prototype.isPrototypeOf(person1) //true Person.prototype.isPrototypeOf(person2) //true
Object.getPrototypeOf( )
該方法會(huì)返回傳入的實(shí)例對(duì)象的原型對(duì)象的值。
Object.getPrototypeOf(person1) == Person.prototype //true Object.getPrototypeOf(person1).name = "Nicholas"
每當(dāng)代碼讀取某個(gè)對(duì)象屬性的時(shí)候就會(huì)執(zhí)行一次搜索。搜索是首先從對(duì)象實(shí)例本身開(kāi)始,如果在實(shí)例中找到了具有給定名字的屬性,則返回該屬性的值,如果沒(méi)有找到則繼續(xù)搜索指針指向的原型對(duì)象,在原型對(duì)象中找具有給定名字的屬性。
function Person() { this.name = "bob" } Person.prototype.name = "jim" var p = new Person() console.log(p.name) // "bob"
可以看到上面輸出的"bob"因?yàn)樵诿總€(gè)實(shí)例對(duì)象上面有具有一個(gè)name為bob的屬性。
雖然我們可以通過(guò)對(duì)象實(shí)例訪問(wèn)到原型對(duì)象中的值,但是我們并不能通過(guò)實(shí)例對(duì)象修改原型對(duì)象中的值。
hasOwnProperty()
通過(guò)hasOwnProperty()方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中。該方法是從Object繼承來(lái)的。當(dāng)屬性存在于對(duì)象實(shí)例中時(shí)返回true。
function Person() {} Person.prototype.name = "Nicholas" Person.prototype.age = 29 Person.prototype.job = "soft ware" var p1 = new Person() var p2 = new Person() console.log(p1.hasOwnProperty("name")) //false p1.name = "bob" console.log(p1.hasOwnProperty("name")) //true
通過(guò)使用hasOwnProperty()方法,什么時(shí)候訪問(wèn)的是實(shí)例屬性,什么時(shí)候訪問(wèn)的是原型屬性就可以一清二楚了。
原型對(duì)象與in操作符
有兩種方式使用in操作符,多帶帶使用和在for-in循環(huán)中使用。in操作符會(huì)在通過(guò)對(duì)象能夠訪問(wèn)給定屬性時(shí)返回true。(不管在對(duì)象實(shí)例上還是在原型對(duì)象上的屬性)
function Person() {} Person.prototype.name = "Nicholas" Person.prototype.age = 29 Person.job = "software engineer" var p1 = new Person() console.log("name" in p1) //true
要取得對(duì)象上所有可枚舉的實(shí)例屬性,可以使用Object.keys()方法,這個(gè)方法接受一個(gè)對(duì)象作為參數(shù),返回一個(gè)包含所有可枚舉屬性的字符串?dāng)?shù)組。
function P() { this.a = "a" } P.prototype.b = "b" var o = new P() console.log(Object.keys(o)) //["a"]
Object.getOwnPropertyNames()
如果你想獲得所有的實(shí)例屬性,不管是否可枚舉的,可以通過(guò)Object.getOwnPropertyNames().
原型的動(dòng)態(tài)性
由于在原型中查找值的過(guò)程是一次搜索,因此我們隊(duì)原型對(duì)象所做的任何修改都能夠立即從實(shí)例上反映出來(lái),即使是先創(chuàng)建實(shí)例對(duì)象后修改原型也是一樣。因?yàn)閷?shí)例與原型對(duì)象之間的連接只不過(guò)是一個(gè)指針,而非一個(gè)副本。
注意,我們可以隨時(shí)為原型對(duì)象添加屬性和方法,但是如果重寫了整個(gè)原型對(duì)象,那么情況就不一樣了。
function Person() {} var friend = new Person() Person.prototype = { constructor: Person, name: "nike", sayName: function() { alert(this.name) } } friend.sayName() //error
重寫了原型對(duì)象切斷了現(xiàn)有原型和任何之前已經(jīng)存在的對(duì)象實(shí)例之間的關(guān)系,它們引用的任然是之前最初的原型。
通過(guò)使用這兩種結(jié)合的方式創(chuàng)建對(duì)象是很常見(jiàn)的方式,構(gòu)造函數(shù)模式用于定義實(shí)例屬性,而原型模式用于定義方法和共享屬性。結(jié)果每個(gè)實(shí)例都會(huì)有自己的一份實(shí)例屬性的副本,但同時(shí)又共享著對(duì)方法的引用,最大限度的節(jié)約了內(nèi)存。
function Person(name, age, job) { this.name = name this.age = age this.job = job this.friends = ["shelby", "count"] } Person.prototype = { constructor: Person, sayName: function() { alert(this.name) } }
動(dòng)態(tài)原型模式把所有的信息都封裝在了構(gòu)造函數(shù)中,而通過(guò)在構(gòu)造函數(shù)中初始化原型,又保持了同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn)??梢酝ㄟ^(guò)檢查某個(gè)應(yīng)該存在的方法是否有效,來(lái)決定是否需要初始化原型。
function Person(name, age, job) { this.name = name this.age = age this.job = job if(typeof this.sayName != "function") { Person.prototype.sayName = function() { alert(this.name) } } }
寄生構(gòu)造函數(shù)模式的基本思想是創(chuàng)建一個(gè)函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對(duì)象的代碼,然后在返回新創(chuàng)建的對(duì)象。但從表面上看,這個(gè)函數(shù)又很像是典型的構(gòu)造函數(shù)。
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("Nicholas", 29, "software engineer") friend.sayName() //"Nicholas"
如上可以看到,Person函數(shù)創(chuàng)建了一個(gè)新對(duì)象,并初始化該對(duì)象的屬性和方法,然后返回該對(duì)象。其實(shí)這個(gè)方式和工廠模式是幾乎一樣的,構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。這里通過(guò)return語(yǔ)句,重寫了構(gòu)造函數(shù)的返回值。
//工廠模式, 可以看到寄生構(gòu)造函數(shù)模式和工廠模式不同的地方就是使用new操作符 function Person(name, age, job) { var o = new Object() o.name = name o.age = age o.job = job o.sayName = function() { alert(this.name) } } var person1 = Person("Nicholas", 29, "software enginner")
那么這個(gè)方法的使用場(chǎng)景是什么呢?(其實(shí)我也沒(méi)弄明白)這個(gè)模式可以在特殊的情況下來(lái)為對(duì)象創(chuàng)建構(gòu)造函數(shù)。假設(shè)我們想創(chuàng)建一個(gè)具有額外方法的特殊數(shù)組。由于不能直接修改Array構(gòu)造函數(shù),因此可以使用這個(gè)模式。
function SpecialArray() { var values = new Array() values.push.apply(values, arguments) values.toPipedString = function() { return this.join("|") } return values } var c = new SpecialArray("red", "blue", "green") console.log(c.toPipedString()) // "red|blue|green"
關(guān)于寄生構(gòu)造函數(shù)模式,返回的對(duì)象與構(gòu)造函數(shù)或者和構(gòu)造函數(shù)的原型屬性沒(méi)有關(guān)系,也就是說(shuō)構(gòu)造函數(shù)返回的對(duì)象與在構(gòu)造函數(shù)外部創(chuàng)建的對(duì)象沒(méi)什么不同。所以通過(guò)instanceof操作符來(lái)確定對(duì)象類型是沒(méi)有意義的。
穩(wěn)妥對(duì)象指的是沒(méi)有公共屬性,而且方法也不引用this對(duì)象。穩(wěn)妥對(duì)象醉適合在一些安全的環(huán)境中(禁止使用this和new),或者在防止數(shù)據(jù)被其他應(yīng)用程序改動(dòng)時(shí)使用。
function Person(name, age, jbo) { var o = new Object() o.sayName = function() { alert(name) } return o }
注意,這里看著也很像工廠函數(shù),但是和工廠函數(shù)有區(qū)別的就是沒(méi)有使用this。這里只有通過(guò)sayName方法來(lái)訪問(wèn)name。
var friend = Person("Nicholas", 29, "software Enigneer") friend.sayName()
這樣,變量friend中保存了一個(gè)穩(wěn)妥對(duì)象,除了調(diào)用sayName方法外,沒(méi)有其他的方式可以訪問(wèn)其數(shù)據(jù)成員。即使可以通過(guò)其他辦法給這個(gè)對(duì)象添加方法或數(shù)據(jù)成員,但也沒(méi)有辦法進(jìn)行訪問(wèn),穩(wěn)妥構(gòu)造函函數(shù)模式提供的這種安全性,令他非常適合在某些安全執(zhí)行環(huán)境。
總結(jié):關(guān)于創(chuàng)建對(duì)象的幾種方式基本上都在這里了,個(gè)人覺(jué)得常用的還是構(gòu)造函數(shù)模式和原型模式的結(jié)合版本。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/89093.html
摘要:構(gòu)造函數(shù)通常首字母大寫,用于區(qū)分普通函數(shù)。這種關(guān)系常被稱為原型鏈,它解釋了為何一個(gè)對(duì)象會(huì)擁有定義在其他對(duì)象中的屬性和方法。中所有的對(duì)象,都有一個(gè)屬性,指向?qū)嵗龑?duì)象的構(gòu)造函數(shù)原型由于是個(gè)非標(biāo)準(zhǔn)屬性,因此只有和兩個(gè)瀏覽器支持,標(biāo)準(zhǔn)方法是。 從這篇文章開(kāi)始,復(fù)習(xí) MDN 中級(jí)教程 的內(nèi)容了,在初級(jí)教程中,我和大家分享了一些比較簡(jiǎn)單基礎(chǔ)的知識(shí)點(diǎn),并放在我的 【Cute-JavaScript】系...
摘要:本文是重溫基礎(chǔ)系列文章的第十四篇。元,是指程序本身。有理解不到位,還請(qǐng)指點(diǎn),具體詳細(xì)的介紹,可以查看維基百科元編程。攔截,返回一個(gè)布爾值。 本文是 重溫基礎(chǔ) 系列文章的第十四篇。 這是第一個(gè)基礎(chǔ)系列的最后一篇,后面會(huì)開(kāi)始復(fù)習(xí)一些中級(jí)的知識(shí)了,歡迎持續(xù)關(guān)注呀! 接下來(lái)會(huì)統(tǒng)一整理到我的【Cute-JavaScript】的JavaScript基礎(chǔ)系列中。 今日感受:獨(dú)樂(lè)樂(lè)不如眾樂(lè)樂(lè)...
摘要:系列目錄復(fù)習(xí)資料資料整理個(gè)人整理重溫基礎(chǔ)篇重溫基礎(chǔ)對(duì)象介紹重溫基礎(chǔ)對(duì)象介紹重溫基礎(chǔ)介紹重溫基礎(chǔ)相等性判斷本章節(jié)復(fù)習(xí)的是中的關(guān)于閉包,這個(gè)小哥哥呀,看看。這里隨著閉包函數(shù)的結(jié)束,執(zhí)行環(huán)境銷毀,變量回收。 本文是 重溫基礎(chǔ) 系列文章的第十九篇。今日感受:將混亂的事情找出之間的聯(lián)系,也是種能力。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理) 【重溫基礎(chǔ)】...
摘要:內(nèi)存泄露內(nèi)存泄露概念在計(jì)算機(jī)科學(xué)中,內(nèi)存泄漏指由于疏忽或錯(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存。判斷內(nèi)存泄漏,以字段為準(zhǔn)。 本文是 重溫基礎(chǔ) 系列文章的第二十二篇。 今日感受:優(yōu)化學(xué)習(xí)方法。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理) 【重溫基礎(chǔ)】1-14篇 【重溫基礎(chǔ)】15.JS對(duì)象介紹 【重溫基礎(chǔ)】16.JSON對(duì)象介紹 【重溫基礎(chǔ)】1...
摘要:本文是重溫基礎(chǔ)系列文章的第七篇。系列目錄復(fù)習(xí)資料資料整理個(gè)人整理重溫基礎(chǔ)語(yǔ)法和數(shù)據(jù)類型重溫基礎(chǔ)流程控制和錯(cuò)誤處理重溫基礎(chǔ)循環(huán)和迭代重溫基礎(chǔ)函數(shù)重溫基礎(chǔ)表達(dá)式和運(yùn)算符重溫基礎(chǔ)數(shù)字本章節(jié)復(fù)習(xí)的是中的時(shí)間對(duì)象,一些處理的方法。 本文是 重溫基礎(chǔ) 系列文章的第七篇。今日感受:做好自律。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理) 【重溫基礎(chǔ)】1.語(yǔ)法和數(shù)據(jù)類型...
摘要:本文是重溫基礎(chǔ)系列文章的第十二篇。注意對(duì)象的名稱,對(duì)大小寫敏感。基礎(chǔ)用法第一個(gè)參數(shù)是目標(biāo)對(duì)象,后面參數(shù)都是源對(duì)象。用途遍歷對(duì)象屬性。用途將對(duì)象轉(zhuǎn)為真正的結(jié)構(gòu)。使用場(chǎng)景取出參數(shù)對(duì)象所有可遍歷屬性,拷貝到當(dāng)前對(duì)象中。類似方法合并兩個(gè)對(duì)象。 本文是 重溫基礎(chǔ) 系列文章的第十二篇。 今日感受:需要總結(jié)下2018。 這幾天,重重的感冒發(fā)燒,在家休息好幾天,傷···。 系列目錄: 【復(fù)習(xí)資料...
閱讀 2006·2021-09-22 16:05
閱讀 9310·2021-09-22 15:03
閱讀 2885·2019-08-30 15:53
閱讀 1702·2019-08-29 11:15
閱讀 911·2019-08-26 13:52
閱讀 2353·2019-08-26 11:32
閱讀 1806·2019-08-26 10:38
閱讀 2567·2019-08-23 17:19