摘要:如果按照字面意思來理解,那么就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象實(shí)例的原型對(duì)象。構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。
面向?qū)ο?/b>
對(duì)象:是無序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或者函數(shù)。new運(yùn)算符
創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象的實(shí)例。
當(dāng)代碼 new Foo(...) 執(zhí)行時(shí),會(huì)發(fā)生以下事情:
一個(gè)繼承自Foo.prototype的新對(duì)象被創(chuàng)建
使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Foo ,并將 this 綁定到新創(chuàng)建的對(duì)象。
屬性類型ECMAScript 中有兩種屬性:數(shù)據(jù)屬性和訪問器屬性。
數(shù)據(jù)屬性
數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫入值。數(shù)據(jù)屬性有 4 個(gè)描述其行為的特性。
Configurable:表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。像前面例子中那樣直接在對(duì)象上定義的屬性,它們的這個(gè)特性默認(rèn)值為 true。
Enumerable:表示能否通過 for-in 循環(huán)返回屬性。像前面例子中那樣直接在對(duì)象上定義的屬性,它們的這個(gè)特性默認(rèn)值為 true。
Writable:表示能否修改屬性的值
Value:包含這個(gè)屬性的數(shù)據(jù)值。讀取屬性值的時(shí)候,從這個(gè)位置讀;寫入屬性值的時(shí)候,把新值保存在這個(gè)位置。這個(gè)特性的默認(rèn)值為 undefined。
要修改屬性默認(rèn)的特性,必須使用 ECMAScript 5 的 Object.defineProperty()方法。這個(gè)方法 接收三個(gè)參數(shù):屬性所在的對(duì)象、屬性的名字和一個(gè)描述符對(duì)象。
var person = {}; Object.defineProperty(person, "name", { writable: false, value: "Nicholas" }); alert(person.name); //"Nicholas" person.name = "Greg"; alert(person.name); //"Nicholas"
把 configurable 設(shè)置為 false,表示不能從對(duì)象中刪除屬性。如果對(duì)這個(gè)屬性調(diào)用 delete,則 在非嚴(yán)格模式下什么也不會(huì)發(fā)生,而在嚴(yán)格模式下會(huì)導(dǎo)致錯(cuò)誤。而且,一旦把屬性定義為不可配置的, 就不能再把它變回可配置了。此時(shí),再調(diào)用 Object.defineProperty()方法修改除 writable 之外 的特性,都會(huì)導(dǎo)致錯(cuò)誤:
var person = {}; Object.defineProperty(person, "name", { configurable: false, value: "Nicholas" }); //拋出錯(cuò)誤 Object.defineProperty(person, "name", { configurable: true, value: "Nicholas" });
訪問器屬性
訪問器屬性不包含數(shù)據(jù)值;它們包含一對(duì)兒 getter 和 setter 函數(shù)(不過,這兩個(gè)函數(shù)都不是必需的)。 在讀取訪問器屬性時(shí),會(huì)調(diào)用 getter 函數(shù),這個(gè)函數(shù)負(fù)責(zé)返回有效的值;在寫入訪問器屬性時(shí),會(huì)調(diào)用 setter 函數(shù)并傳入新值,這個(gè)函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。訪問器屬性有如下 4 個(gè)特性。
Configurable:表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特 性,或者能否把屬性修改為數(shù)據(jù)屬性。對(duì)于直接在對(duì)象上定義的屬性,這個(gè)特性的默認(rèn)值為 true。
Enumerable:表示能否通過 for-in 循環(huán)返回屬性。對(duì)于直接在對(duì)象上定義的屬性,這 5 個(gè)特性的默認(rèn)值為 true
Get:在讀取屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值為 undefined
Set:在寫入屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值為 undefined
訪問器屬性不能直接定義,必須使用 Object.defineProperty()來定義
var book = { _year: 2004, edition: 1 }; Object.defineProperty(book, "year", { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } }); book.year = 2005; alert(book.edition); //2
不一定非要同時(shí)指定 getter 和 setter。只指定 getter 意味著屬性是不能寫,嘗試寫入屬性會(huì)被忽略。 在嚴(yán)格模式下,嘗試寫入只指定了 getter 函數(shù)的屬性會(huì)拋出錯(cuò)誤。類似地,只指定 setter 函數(shù)的屬性也不能讀,否則在非嚴(yán)格模式下會(huì)返回 undefined,而在嚴(yán)格模式下會(huì)拋出錯(cuò)誤。
創(chuàng)建對(duì)象 工廠模式用函數(shù)來封裝以特定接口創(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("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
函數(shù) createPerson()能夠根據(jù)接受的參數(shù)來構(gòu)建一個(gè)包含所有必要信息的 Person 對(duì)象??梢詿o數(shù)次地調(diào)用這個(gè)函數(shù),而每次它都會(huì)返回一個(gè)包含三個(gè)屬性一個(gè)方法的對(duì)象。
工廠模式雖然解決了創(chuàng)建多個(gè)相似對(duì)象的問題,但卻沒有解決對(duì)象識(shí)別的問題(即怎樣知道一個(gè)對(duì)象的類型)。隨著 JavaScript 的發(fā)展,又一個(gè)新模式出現(xiàn)了。
構(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("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
好處:
沒有顯式地創(chuàng)建對(duì)象;
直接將屬性和方法賦給了 this 對(duì)象;
沒有 return 語句。
person1 和 person2 分別保存著 Person 的一個(gè)不同的實(shí)例。這兩個(gè)對(duì)象都有一個(gè) constructor(構(gòu)造函數(shù))屬性,該屬性指向 Person,如下所示
alert(person1.constructor == Person); //true alert(person2.constructor == Person); //true
對(duì)象的 constructor 屬性最初是用來標(biāo)識(shí)對(duì)象類型的。但是,提到檢測(cè)對(duì)象類型,還是 instan- ceof 操作符要更可靠一些。我們?cè)谶@個(gè)例子中創(chuàng)建的所有對(duì)象既是 Object 的實(shí)例,同時(shí)也是 Person 的實(shí)例,這一點(diǎn)通過 instanceof 操作符可以得到驗(yàn)證。
alert(person1 instanceof Object); //true alert(person1 instanceof Person); //true alert(person2 instanceof Object); //true alert(person2 instanceof Person); //true
將構(gòu)造函數(shù)當(dāng)作函數(shù)
構(gòu)造函數(shù)與其他函數(shù)的唯一區(qū)別,就在于調(diào)用它們的方式不同。不過,構(gòu)造函數(shù)畢竟也是函數(shù),不 存在定義構(gòu)造函數(shù)的特殊語法。任何函數(shù),只要通過 new 操作符來調(diào)用,那它就可以作為構(gòu)造函數(shù);而 任何函數(shù),如果不通過 new 操作符來調(diào)用,那它跟普通函數(shù)也不會(huì)有什么兩樣。
// 當(dāng)作構(gòu)造函數(shù)使用 var person = new Person("Nicholas", 29, "Software Engineer"); person.sayName(); //"Nicholas" // 作為普通函數(shù)調(diào)用 Person("Greg", 27, "Doctor"); // 添加到window window.sayName(); //"Greg" // 在另一個(gè)對(duì)象的作用域中調(diào)用 var o = new Object(); Person.call(o, "Kristen", 25, "Nurse"); o.sayName(); //"Kristen"
這個(gè)例子中的前兩行代碼展示了構(gòu)造函數(shù)的典型用法,即使用 new 操作符來創(chuàng)建一個(gè)新對(duì)象。
接下來的兩行代碼展示了不使用new 操作符調(diào)用 Person()會(huì)出現(xiàn)什么結(jié)果:屬性和方法都被添加給 window 對(duì)象了。當(dāng)在全局作用域中調(diào)用一個(gè)函數(shù)時(shí),this對(duì)象總是指向Global 對(duì)象(在瀏覽器中就是window對(duì)象)。因此,在調(diào)用完函數(shù)之后,可以通過 window 對(duì)象來調(diào)用 sayName()方法,并且還返回了"Greg"。
最后,也可以使用call()(或者apply())在某個(gè)特殊對(duì)象的作用域中調(diào)用Person()函數(shù)。這里是在對(duì)象o的作用域中調(diào)用的,因此調(diào)用后o就擁有了所有屬性和sayName()方法。
構(gòu)造函數(shù)的問題
使用構(gòu)造函數(shù)的主要問題,就是每個(gè)方法都要在每個(gè) 實(shí)例上重新創(chuàng)建一遍。在前面的例子中,person1 和 person2 都有一個(gè)名為 sayName()的方法,但那 兩個(gè)方法不是同一個(gè) Function 的實(shí)例。
以這種方式創(chuàng)建函數(shù),會(huì)導(dǎo)致不同的作用域鏈和標(biāo)識(shí)符解析,但創(chuàng)建 Function 新實(shí)例的機(jī)制仍然是相同的。因此,不同實(shí)例上的同名函數(shù)是不相等的,以下代碼可以證明這一點(diǎn)
alert(person1.sayName == person2.sayName); //false原型模式
我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè) prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象, 而這個(gè)對(duì)象的用途是包含可以由特定類型的所有實(shí)例共享的屬性和方法。如果按照字面意思來理解,那 么 prototype 就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象實(shí)例的原型對(duì)象。使用原型對(duì)象的好處是可以 讓所有對(duì)象實(shí)例共享它所包含的屬性和方法。換句話說,不必在構(gòu)造函數(shù)中定義對(duì)象實(shí)例的信息,而是 可以將這些信息直接添加到原型對(duì)象中,如下面的例子所示。
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" alert(person1.sayName == person2.sayName); //true
理解原型對(duì)象
無論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè) prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。在默認(rèn)情況下,所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè) constructor (構(gòu)造函數(shù))屬性,這個(gè)屬性包含一個(gè)指向 prototype 屬性所在函數(shù)的指針。就拿前面的例子來說, Person.prototype.constructor 指向 Person。而通過這個(gè)構(gòu)造函數(shù),我們還可繼續(xù)為原型對(duì)象添加其他屬性和方法。
創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對(duì)象默認(rèn)只會(huì)取得 constructor 屬性;至于其他方法,則都是從 Object 繼承而來的。當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例后,該實(shí)例的內(nèi)部將包含一個(gè)指針(內(nèi)部 屬性),指向構(gòu)造函數(shù)的原型對(duì)象。ECMA-262 第 5 版中管這個(gè)指針叫[[Prototype]]。雖然在腳本中 沒有標(biāo)準(zhǔn)的方式訪問[[Prototype]],但 Firefox、Safari 和 Chrome 在每個(gè)對(duì)象上都支持一個(gè)屬性 __proto__;而在其他實(shí)現(xiàn)中,這個(gè)屬性對(duì)腳本則是完全不可見的。不過,要明確的真正重要的一點(diǎn)就 是,這個(gè)連接存在于實(shí)例與構(gòu)造函數(shù)的原型對(duì)象之間,而不是存在于實(shí)例與構(gòu)造函數(shù)之間。
使用 hasOwnProperty()方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中。這個(gè)方法(不 要忘了它是從 Object 繼承來的)只在給定屬性存在于對(duì)象實(shí)例中時(shí),才會(huì)返回 true。
有兩種方式使用 in 操作符:多帶帶使用和在 for-in 循環(huán)中使用。在多帶帶使用時(shí),in 操作符會(huì)在通 過對(duì)象能夠訪問給定屬性時(shí)返回 true,無論該屬性存在于實(shí)例中還是原型中。
由于 in 操作符只要通過對(duì)象能夠訪問到屬性就返回 true,hasOwnProperty()只在屬性存在于 實(shí)例中時(shí)才返回 true,因此只要 in 操作符返回 true 而 hasOwnProperty()返回 false,就可以確 定屬性是原型中的屬性。
原型對(duì)象的問題
所有實(shí)例在默認(rèn)情況下都將取得相同的屬性值
function Person() {} Person.prototype = { constructor: Person, name : "Nicholas", age : 29, job : "Software Engineer", friends : ["Shelby", "Court"], sayName : function () { alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Court,Van" alert(person2.friends); //"Shelby,Court,Van" alert(person1.friends === person2.friends); //true
Person.prototype對(duì)象有一個(gè)名為friends的屬性,該屬性包含一個(gè)字符串?dāng)?shù)組。然后, 創(chuàng)建了 Person 的兩個(gè)實(shí)例。接著,修改了 person1.friends 引用的數(shù)組,向數(shù)組中添加了一個(gè)字符 串。由于 friends 數(shù)組存在于 Person.prototype 而非 person1 中,所以剛剛提到的修改也會(huì)通過 person2.friends(與 person1.friends 指向同一個(gè)數(shù)組)反映出來。
組合使用構(gòu)造函數(shù)模式和原型模式構(gòu)造函數(shù)模式用于定義實(shí)例屬性,而原型模式用于定義方法和共享的屬性。結(jié)果,每個(gè)實(shí)例都會(huì)有自己的一份實(shí)例屬性的副本,但同時(shí)又共享著對(duì)方法的引用,最大限度地節(jié)省了內(nèi)存。另外,這種混成模式還支持向構(gòu)造函數(shù)傳遞參數(shù);可謂是集兩種模式之長(zhǎng)
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true動(dòng)態(tài)原型模式
通過檢查某個(gè)應(yīng)該存在的方法是否有效,來決定是否需要初始化原型。
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); }; } } var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName();寄生構(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("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"
在這個(gè)例子中,Person 函數(shù)創(chuàng)建了一個(gè)新對(duì)象,并以相應(yīng)的屬性和方法初始化該對(duì)象,然后又返 回了這個(gè)對(duì)象。除了使用 new 操作符并把使用的包裝函數(shù)叫做構(gòu)造函數(shù)之外,這個(gè)模式跟工廠模式其實(shí) 是一模一樣的。構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。而通過在構(gòu)造函數(shù)的末尾添加一個(gè) return 語句,可以重寫調(diào)用構(gòu)造函數(shù)時(shí)返回的值。
這個(gè)模式可以在特殊的情況下用來為對(duì)象創(chuàng)建構(gòu)造函數(shù)。假設(shè)我們想創(chuàng)建一個(gè)具有額外方法的特殊數(shù)組。由于不能直接修改 Array 構(gòu)造函數(shù),因此可以使用這個(gè)模式。
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("red", "blue", "green"); alert(colors.toPipedString()); //"red|blue|green"
需要說明:首先,返回的對(duì)象與構(gòu)造函數(shù)或者與構(gòu)造函數(shù)的原型屬性之間沒有關(guān)系;也就是說,構(gòu)造函數(shù)返回的對(duì)象與在構(gòu)造函數(shù)外部創(chuàng)建的對(duì)象沒有什么不同。為此,不能依賴 instanceof 操作符來確定對(duì)象類型。由于存在上述問題,我們建議在可以使用其他模式的情況下,不要使用這種模式。
穩(wěn)妥構(gòu)造函數(shù)模式所謂穩(wěn)妥對(duì)象,指的是沒有公共屬性,而且其方法也不引用 this 的對(duì)象。穩(wěn)妥對(duì)象最適合在 一些安全的環(huán)境中(這些環(huán)境中會(huì)禁止使用 this 和 new),或者在防止數(shù)據(jù)被其他應(yīng)用程序(如 Mashup 程序)改動(dòng)時(shí)使用。穩(wěn)妥構(gòu)造函數(shù)遵循與寄生構(gòu)造函數(shù)類似的模式,但有兩點(diǎn)不同:一是新創(chuàng)建對(duì)象的 實(shí)例方法不引用 this;二是不使用 new 操作符調(diào)用構(gòu)造函數(shù)。按照穩(wěn)妥構(gòu)造函數(shù)的要求,可以將前面 的 Person 構(gòu)造函數(shù)重寫如下。
function Person(name, age, job){ //創(chuàng)建要返回的對(duì)象 var o = new Object(); //可以在這里定義私有變量和函數(shù) //添加方法 o.sayName = function(){ alert(name); }; //返回對(duì)象 return o; }
在以這種模式創(chuàng)建的對(duì)象中,除了使用 sayName()方法之外,沒有其他辦法訪問 name 的值。 可以像下面使用穩(wěn)妥的 Person 構(gòu)造函數(shù)。
即使有其他代碼會(huì)給這個(gè)對(duì)象添加方法或數(shù)據(jù)成員,但也不可能有別的辦法訪問傳 入到構(gòu)造函數(shù)中的原始數(shù)據(jù)。穩(wěn)妥構(gòu)造函數(shù)模式提供的這種安全性,使得它非常適合在某些安全執(zhí)行環(huán) 境——例如,ADsafe(www.adsafe.org)和 Caja(http://code.google.com/p/goog... )提供的環(huán)境—— 下使用。
繼承 原型鏈構(gòu)造函數(shù)、原型和實(shí)例的關(guān)系:每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針。
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
問題:包含引用類型值的原型屬性會(huì)被所有實(shí)例共享;在創(chuàng)建子類型的實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)
借用構(gòu)造函數(shù)在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù);函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對(duì)象, 因此通過使用 apply()和 call()方法也可以在(將來)新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù),如下所示
function SuperType() { this.colors = ["red", "blue", "green"] } function SubType(){ //繼承了 SuperType SuperType.call(this); } 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"
代碼中“借調(diào)”了超類型的構(gòu)造函數(shù)。通過使用 call()方法(或 apply()方法 也可以),我們實(shí)際上是在(未來將要)新創(chuàng)建的 SubType 實(shí)例的環(huán)境下調(diào)用了 SuperType 構(gòu)造函數(shù)。 這樣一來,就會(huì)在新 SubType 對(duì)象上執(zhí)行 SuperType()函數(shù)中定義的所有對(duì)象初始化代碼。結(jié)果, SubType 的每個(gè)實(shí)例就都會(huì)具有自己的 colors 屬性的副本了。
優(yōu)勢(shì):相對(duì)于原型鏈而言,借用構(gòu)造函數(shù)有一個(gè)很大的優(yōu)勢(shì),即可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。
問題:如果僅僅是借用構(gòu)造函數(shù),那么無法避免構(gòu)造函數(shù)模式存在的問題——方法都在構(gòu)造函數(shù)中定義,因此函數(shù)復(fù)用就無從談起了。而且,在超類型的原型中定義的方法,對(duì)子類型而言也是不可見的,結(jié) 果所有類型都只能使用構(gòu)造函數(shù)模式。考慮到這些問題,借用構(gòu)造函數(shù)的技術(shù)也是很少多帶帶使用的。
組合繼承思路:使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); } function SubType(name, age){ //繼承屬性 SuperType.call(this, name); this.age = age; } //繼承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27原型式繼承
借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象,同時(shí)還不必因此創(chuàng)建自定義類型。
function object(o){ function F(){} F.prototype = o return new F()
在object()函數(shù)內(nèi)部,先創(chuàng)建了一個(gè)臨時(shí)性的構(gòu)造函數(shù),然后將傳入的對(duì)象作為這個(gè)構(gòu)造函數(shù)的原型,最后返回了這個(gè)臨時(shí)類型的一個(gè)新實(shí)例。從本質(zhì)上講,object()對(duì)傳入其中的對(duì)象執(zhí)行了一次淺復(fù)制。e.g.
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); // 這相當(dāng)于創(chuàng)建了 person 對(duì)象的兩個(gè)副本。 alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
ECMAScript 5 通過新增 Object.create()方法規(guī)范化了原型式繼承。這個(gè)方法接收兩個(gè)參數(shù):一 個(gè)用作新對(duì)象原型的對(duì)象和(可選的)一個(gè)為新對(duì)象定義額外屬性的對(duì)象。在傳入一個(gè)參數(shù)的情況下, Object.create()與 object()方法的行為相同。
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"寄生式繼承
寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,即創(chuàng)建一個(gè)僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強(qiáng)對(duì)象,最后再像真地是它做了所有工作一樣返回對(duì)象。以下代碼示范了寄生式繼承模式
function createAnother(original){ var clone = object(original) //通過調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象 clone.sayHi = function() { //以某種方式來增強(qiáng)這個(gè)對(duì)象 alert("hi") } return clone; //返回這個(gè)對(duì)象 }寄生組合式繼承
組合繼承最大的 問題就是無論什么情況下,都會(huì)調(diào)用兩次超類型構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型的時(shí)候,另一次是 在子類型構(gòu)造函數(shù)內(nèi)部。沒錯(cuò),子類型最終會(huì)包含超類型對(duì)象的全部實(shí)例屬性,但我們不得不在調(diào)用子 類型構(gòu)造函數(shù)時(shí)重寫這些屬性。
寄生組合式繼承的基本模式如下所示。
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //創(chuàng)建超類型原型的一個(gè)副本 prototype.constructor = subType; //為創(chuàng)建的副本添加 constructor 屬性,從而彌補(bǔ)因重寫原型而失去的默認(rèn)的 constructor 屬性 subType.prototype = prototype; //將新創(chuàng)建的對(duì)象(即副本)賦值給子類型的原型 }
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94626.html
摘要:更形象的我們還可以將面向?qū)ο罄斫鉃橐环N宗教信仰。這就導(dǎo)致面向?qū)ο蠼痰某绦騿T們?cè)趯憰r(shí)就很難受。所以為了滿足信仰面向?qū)ο蠼痰男枨笸ㄟ^構(gòu)造函數(shù)的形式模擬了偽類。這個(gè)套路的核心就是類那么里沒有類所以其實(shí)是通過構(gòu)造函數(shù)來模擬的偽類。 JS面向?qū)ο笾?【概述】 在學(xué)習(xí)JS的面向?qū)ο笾?我們應(yīng)該先自問這樣幾個(gè)問題: 面向?qū)ο笫鞘裁匆馑? 學(xué)習(xí)面向?qū)ο蟮暮诵氖鞘裁? 為什么要學(xué)習(xí)面向?qū)ο?(它的...
摘要:是完全的面向?qū)ο笳Z言,它們通過類的形式組織函數(shù)和變量,使之不能脫離對(duì)象存在。而在基于原型的面向?qū)ο蠓绞街?,?duì)象則是依靠構(gòu)造器利用原型構(gòu)造出來的。 JavaScript 函數(shù)式腳本語言特性以及其看似隨意的編寫風(fēng)格,導(dǎo)致長(zhǎng)期以來人們對(duì)這一門語言的誤解,即認(rèn)為 JavaScript 不是一門面向?qū)ο蟮恼Z言,或者只是部分具備一些面向?qū)ο蟮奶卣?。本文將回歸面向?qū)ο蟊疽?,從?duì)語言感悟的角度闡述為什...
摘要:面向過程函數(shù)式編程面向?qū)ο缶幊痰诙€(gè)并不是大家理解的那樣,我們先說舉個(gè)現(xiàn)實(shí)例子就明白了。多說一句函數(shù)是編程是非常強(qiáng)大也是我最喜歡的,以后再說,我們先說面向?qū)ο缶幊獭? 概述 當(dāng)大家已經(jīng)把js的語言基礎(chǔ)理解了,然后能夠?qū)懗鲆恍┖?jiǎn)單的例子了,這個(gè)時(shí)候基本上達(dá)到了一年工作經(jīng)驗(yàn)的水平,而自己能夠獨(dú)立的寫一些小功能,完成一些小效果,或者臨摹修改一些比較復(fù)雜的插件的時(shí)候差不多就是兩年工作經(jīng)驗(yàn)的水平,...
摘要:對(duì)象重新認(rèn)識(shí)面向?qū)ο竺嫦驅(qū)ο髲脑O(shè)計(jì)模式上看,對(duì)象是計(jì)算機(jī)抽象現(xiàn)實(shí)世界的一種方式。除了字面式聲明方式之外,允許通過構(gòu)造器創(chuàng)建對(duì)象。每個(gè)構(gòu)造器實(shí)際上是一個(gè)函數(shù)對(duì)象該函數(shù)對(duì)象含有一個(gè)屬性用于實(shí)現(xiàn)基于原型的繼承和共享屬性。 title: JS對(duì)象(1)重新認(rèn)識(shí)面向?qū)ο? date: 2016-10-05 tags: JavaScript 0x00 面向?qū)ο?從設(shè)計(jì)模式上看,對(duì)象是...
摘要:自己的理解的第一個(gè)參數(shù)就是的值如果沒用默認(rèn)是那個(gè)調(diào)用函數(shù)的當(dāng)前的對(duì)象在全局作用域中就是被隱藏的所以不寫且在全局作用于調(diào)用函數(shù)的時(shí)候就是可以使用或者自己指定的指向 JS面向?qū)ο笠?MVC的面向?qū)ο蠓庋b MDNjavascript面向?qū)ο?面向?qū)ο?Object-Oriented) showImg(https://segmentfault.com/img/remote/1460000016...
閱讀 2992·2021-11-16 11:45
閱讀 5194·2021-09-22 10:57
閱讀 1779·2021-09-08 09:36
閱讀 1608·2021-09-02 15:40
閱讀 2519·2021-07-26 23:38
閱讀 1208·2019-08-30 15:55
閱讀 935·2019-08-30 15:54
閱讀 1225·2019-08-29 14:06