摘要:就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型對象。創(chuàng)建要返回的對象可以在這里定義私有變量和函數(shù)添加函數(shù)注意寄生構(gòu)造函數(shù)中是返回對象注意寄生構(gòu)造函數(shù)中是注意,在以這種模式創(chuàng)建的對象中,除了使用方法之外,沒有其他辦法訪問的值。
一、工廠模式
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 0; } var person1 = createPerson("Nicholas",29,"Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
抽象了創(chuàng)建具體對象的過程。工廠模式雖然解決了創(chuàng)建多個相似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)
二、構(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");
構(gòu)造函數(shù)模式與工廠模式存在以下幾點不同:
(1)沒有顯示地創(chuàng)建對象;
(2)直接將屬性和方法賦給了this對象;
(3)沒有return語句
要創(chuàng)建Person的新實例,必須使用new操作符,以這種方式調(diào)用構(gòu)造函數(shù)實際上會經(jīng)歷一下4個步驟:
(1)創(chuàng)建一個新得對象;
(2)將構(gòu)造函數(shù)的作用域賦值給新對象(因此this就指向了這個新對象);
(3)執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性);
(4)返回新對象。
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來可以將它的實例標(biāo)識為一種特定的類型;而這正是構(gòu)造函數(shù)模式勝過工廠模式的地方。
使用構(gòu)造函數(shù)的主要問題就是每個方法都要在每個實例上重新創(chuàng)建一遍。
解決辦法:通過把函數(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); }; var person1 = Person("Nicholas",29,"Software Engineer"); var person2 = Person("Greg", 27, "Doctor");三、原型模式
我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,這個對象的用途就是包含可以由特定類型的所有實例共享的屬性和方法。prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型對象。使用原型對象的好處是可以讓所有對象實例共享他所包含的屬性和方法。
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); //true1、理解原型對象
(1)只要創(chuàng)建了一個新的函數(shù),就會根據(jù)一組特定的規(guī)則誒該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向函數(shù)的原型對象;
(2)在默認(rèn)情況下,所有原型對象都會自動獲得一個constructor(構(gòu)造函數(shù))屬性,這個屬性是一個指向prototype屬性所在函數(shù)的指針;
(3)當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新實例后,該實例內(nèi)部將包含一個指針(內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對象。注意:這個連接存在于實例與構(gòu)造函數(shù)的原型對象之間,而不是存在于實例與構(gòu)造函數(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(); var person2 = new Person();
下圖展示了上述代碼創(chuàng)建的各個對象之間的關(guān)系。
(1)可以通過isPrototypeOf()方法來確定對象之間是否存在[[Prototype]]關(guān)系,如果[[Prototype]]指向調(diào)用isPrototypeOf()方法的對象(Person.prototype),那么這個方法就返回true。
alter(Person.prototype.isprototypeOf(person1)); //true
Object.getPrototypeOf(),在所有支持的實現(xiàn)中。這個方法返回[[Prototype]]的值。
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"
(2)每當(dāng)代碼讀取某個對象的某個屬性時,都會執(zhí)行一次搜索,目標(biāo)是具有給定名字的屬性。搜索首先從對象實例本身開始。如果在實例中找到啦具有給定名字的屬性,則返回屬性的值;如果沒有找到。則繼續(xù)搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性。如果在原型中找到了這個屬性,則返回該屬性的值。
(3)雖然可以意通過對象實例訪問保存在原型中的值,但卻不能夠通過對象實例重寫原型的值。當(dāng)為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性;也就是說,添加這個屬性只會阻止我們訪問原型中的那個屬性,但不會修改那個屬性。
(4)使用delete操作符則可以完全刪除實例屬性,從而使我們能夠重新訪問原型中的屬性。
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(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg"來自實例 alert(person2.name); //"Nicholas"來自原型 delete(person1.name); //完全刪除實例對象 alert(person1.name); //"Nicholas"來自原型
(5)使用hasOwnProperty()方法可以檢測一個屬性是存在于實例中,還是存在于原型中。這個方法只在給定屬性存在于對象實例中時,才會返回true。
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(); var person2 = new Person(); alert(person1.hasOwnPrototype("name")); //false person1.name = "Greg"; alert(person1.name); //"Greg"來自實例 alert(person1.hasOwnPrototype("name")); //true alert(person2.name); //"Nicholas"來自原型 alert(person1.hasOwnPrototype("name")); //false delete(person1.name); //完全刪除實例對象 alert(person1.name); //"Nicholas"來自原型 alert(person1.hasOwnPrototype("name")); //false2、原型與in操作符
(1)有兩方式使用in操作符:多帶帶使用和在for-in循環(huán)使用。在多帶帶使用時,in操作符會在通過對象能夠訪問給定屬性時返回true,無論屬性存在于實例還是原型中;在使用for-in循環(huán)時,返回的是所有能夠通過對象訪問的、可枚舉的屬性,其中既包括存在于實例中的屬性,也包括存在于原型中的屬性。
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(); var person2 = new Person(); alert(person1.hasOwnPrototype("name")); //false alert("name" in person1); //true person1.name = "Greg"; alert(person1.name); //"Greg"來自實例 alert(person1.hasOwnPrototype("name")); //true alert("name" in person1); //true alert(person2.name); //"Nicholas"來自原型 alert(person1.hasOwnPrototype("name")); //false alert("name" in person2); //true delete(person1.name); //完全刪除實例對象 alert(person1.name); //"Nicholas"來自原型 alert(person1.hasOwnPrototype("name")); //false alert("name" in person1); //true
(2)同時使用hasOwnProperty()方法和in操作符,就可以確定該屬性到底是存在于對象中,還是存在于原型中。
function hasOwnProperty(object, name) { return !object.hasOwnProperty(name) && (name in object); } function Person() { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { alert(this.name); }; var person = new Person(); alert(person1.hasOwnPrototype(person,"name")); //true表示屬性存在于原型中 person.name = "Greg"; alert(person1.hasOwnPrototype(person,"name")); //false表示屬性存在于實例中
Object.keys()方法:取得對象上所有可枚舉的實例屬性。這個方法接收一個對象作為參數(shù),返回一個包含所有可枚舉屬性的字符串?dāng)?shù)組。
function Person() { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { alert(this.name); }; var keys = Object.keys(Person.prototype); alert(keys); //"name, age, job, sayName"
使用Object.getOwnPropertyName()方法可以得到所有的實例屬性,無論它是否可枚舉.
function Person() { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { alert(this.name); }; var keys = Object.getOwnPropertyName(Person.prototype); alert(keys); //"constructor,name, age, job, sayName"3、更簡單的原型語法
//原型模式 function Person() { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() { alert(this.name); }; //更簡單的原型語法:用一個包含所有屬性和方法的對象字面量來重寫整個原型對象; 導(dǎo)致的問題:相當(dāng)于重寫了默認(rèn)的prototype屬性,constructor屬性不再指向Person function Person() { } Person.prototype = { name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } };
解決辦法:特意將constructor屬性設(shè)置回適當(dāng)?shù)闹?/p>
function Person() { } Person.prototype = { constructor : Person, //將constructor設(shè)置為原來的值 name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } };
但是,以這種方式重設(shè)constructor屬性導(dǎo)致它的[[Enumberable]]特性被設(shè)置為true。默認(rèn)情況下,原生的constructor屬性是不可枚舉的。因此可以嘗試用Object.defineProperty()重設(shè)構(gòu)造函數(shù)。
function Person() { } Person.prototype = { name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; //重設(shè)構(gòu)造函數(shù) Object.defineProperty(Person.prototype,"constructor",{ enumerable : false, value : Person });4、原型的動態(tài)性
function Person() { } var friend = new Person(); //重寫整個原型對象 Person.prototype = { name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; friend.sayName(); //error
重寫對象之前
重寫原型對象之后
重寫原型對象切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對象實例之間的聯(lián)系;它們引用的仍然是最初的原型。
原型模式的最大問題是由其共享的本質(zhì)所導(dǎo)致的,原型中的所有屬性是被很多實例共享的。但是,實例一般都是要有屬于自己的全部屬性的。
function Person() { } Person.prototype = { name : "Nicholas", age : 29, job : "Software Engineer", friends : ["Shebly", "Court"], sayName : function () { alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.friends.push["Van"]; alert (person1.friends); //"Shebly", "Court","Van" alert (person2.friends); //"Shebly", "Court","Van" alert(person1.friends === person2.friends); //true四、組合使用構(gòu)造函數(shù)模式和原型模式
創(chuàng)建自定義類型的最常見方式,就是組合使用構(gòu)造函數(shù)模式和原型模式。構(gòu)造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。
//構(gòu)造函數(shù) 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); //"Shebly", "Court","Van" alert (person2.friends); //"Shebly", "Court" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true五、動態(tài)原型模式
可以通過檢查某個應(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();
注意:如果在已經(jīng)創(chuàng)建了實例的情況下重寫原型,就會切斷現(xiàn)有實例與新原型之間的聯(lián)系。
六、寄生構(gòu)造函數(shù)模式P160封裝創(chuàng)建對象的代碼,然后再返回新創(chuàng)建的對象
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òu)造函數(shù)模式除了使用new操作符并把使用的包裝函數(shù)叫做構(gòu)造函數(shù)之外,這個函數(shù)模式和工廠模式其實是一模一樣的,下面附上兩種模式的代碼
七、穩(wěn)妥構(gòu)造函數(shù)模式P161穩(wěn)妥對象,指的是沒有公共屬性,而且其方法也不引用this的對象。穩(wěn)妥構(gòu)造函數(shù)遵循與寄生構(gòu)造函數(shù)類似的模式,但有兩點不同:(1)新創(chuàng)建對象的實例方法不引用this;(2)不使用new操作符調(diào)用構(gòu)造函數(shù)。
function Person(name, age, job) { var o = new Object(); //創(chuàng)建要返回的對象 //可以在這里定義私有變量和函數(shù) //添加函數(shù) o.sayName = function() { alert(name); `//注意寄生構(gòu)造函數(shù)中是alert(this.name)` }; //返回對象 return o; } var friend = Person("Nicholas", 29, "Software Engineer"); `//注意寄生構(gòu)造函數(shù)中是var friend = newPerson("Nicholas", 29, "Software Engineer"); ` friend.sayName(); //"Nicholas"
注意,在以這種模式創(chuàng)建的對象中,除了使用sayName()方法之外,沒有其他辦法訪問name的值。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109057.html
摘要:其中,描述符對象的屬性必須是和。吧設(shè)置為,表示不能從對象中刪除屬性。這個方法接收兩個對象參數(shù)要添加和修改其屬性值的對象,第二個是與第一個對象中要添加和修改的屬性值一一對應(yīng)。 理解對象 1、創(chuàng)建自定義對象的兩種方法: (1)創(chuàng)建一個Object實例,然后再為它添加屬性和方法。 var person = new Object(); person.name = Nicholas; ...
摘要:除此之外,在超類型的原型中定義的方法,對子類型而言也是不可兼得,結(jié)果所有類型都只能用構(gòu)造函數(shù)模式。創(chuàng)建對象增強對象指定對象繼承屬性這個例子的高效率體現(xiàn)在它只調(diào)用了一次構(gòu)造函數(shù)。 1、原型鏈 原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。構(gòu)造函數(shù)、原型和實例的關(guān)系:每個構(gòu)造函數(shù)都有一個原型對象;原型對象都包含著一個指向構(gòu)造函數(shù)的指針;實例都包含一個指向原型對象的...
摘要:節(jié)點層次是針對和文檔的一個。每一段標(biāo)記都可以通過一個節(jié)點來表示。用于表明節(jié)點的類型。返回新增的這個節(jié)點。將節(jié)點插入到節(jié)點列表指定的位置。節(jié)點有以下的特性為,為指向元素,指向元素。提供了對元素的標(biāo)簽名,子節(jié)點和特性的訪問。 節(jié)點層次 DOM是針對HTML和XML文檔的一個API。DOM描繪了一個層次化的節(jié)點樹,允許開發(fā)人員添加、移除和修改頁面的某一部分。 DOM可以將任何HTML或X...
摘要:此時的原型對象包括一個指向另一個原型的指針,相應(yīng)的,另一個原型中的指向另一個構(gòu)造函數(shù)。這種關(guān)系層層遞進,就通過一個原型對象鏈接另一個構(gòu)造函數(shù)的原型對象的方式實現(xiàn)了繼承。 讀這篇之前,最好是已讀過我前面的關(guān)于對象的理解和封裝類的筆記。第6章我一共寫了3篇總結(jié),下面是相關(guān)鏈接:讀《javaScript高級程序設(shè)計-第6章》之理解對象讀《javaScript高級程序設(shè)計-第6章》之封裝類 一...
摘要:由設(shè)計,作為編程語言的繼承者,于年首次發(fā)布。表達式表達式是編程語言中的語法實體,可以對其進行評估以確定其值。它是編程語言解釋和計算以產(chǎn)生值的常量變量函數(shù)和運算符的組合。它在年年年和年被評為年度編程語言,是唯一四次獲得該獎項的語言。 ...
閱讀 3147·2021-10-12 10:11
閱讀 1851·2021-08-16 10:59
閱讀 2863·2019-08-30 15:55
閱讀 1237·2019-08-30 14:19
閱讀 2048·2019-08-29 17:03
閱讀 2482·2019-08-29 16:28
閱讀 3231·2019-08-26 13:47
閱讀 2899·2019-08-26 13:36