摘要:在構造函數內部,將屬性設置成等于全局的函數。調用構造函數的一個實例后,該實例內部將包含一個指針中稱為,指向構造函數的原型對象。原型對象的問題省略了為構造函數傳遞初始化參數這一環節,結果所有實例在默認情況下都取得相同的屬性值。
創建對象
使用對象字面量的形式一個接口會創建很多對象, 會產生大量的重復代碼。
工廠模式:用函數來封裝以特定接口創建對象的細節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("Simon", 29, "software Engineer"); var person2 = createPerson("Zaynex",22, "Doctor");
這種模式解決了創建多個相似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)
構造函數模式- 可用于創建特定模式的對象,像Object、Array等原生構造函數,在運行時會自動出現在執行環境中。
我們利用構造函數重寫下剛才的函數。
function createPerson(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); }; } var person1 = new Person("Simon",29, "software Engineer"); var person2 = new Person("Simon",29, "software Engineer");構造函數與工廠模式的差異
沒有顯示地創建對象;
直接將屬性和方法賦給this對象;
沒有 return 語句;
我們注意到Person開頭是大寫,按照慣例來講,構造函數開頭字母是大寫,非構造函數以小寫字母開頭。
調用構造函數四步驟創建一個新對象;
將構造函數的作用域賦給新對象(因此this就指向了這個新對象);
執行構造函數中的代碼(為構造函數新對象添加屬性)
返回新對象
person1和person2都保存著Person的一個不同的實例。這兩個對象都有一個constructor(構造函數)屬性,該屬性指向Person。
alert(person1.constructor == Person) //true; alert(person2.constructor == Person) //true;
對象的constructor屬性最初是用來標識對象類型的。但是提到檢測對象類型,還是instanceof操作符更可靠一些。
alert(person1 instanceof Object); alert(person1 instanceof Person); alert(person2 instanceof Object); alert(person2 instanceof Person); //都為true.
我們所創建的所有對象都是Object的實例,同時也是Person的實例。
創建自定義的構造函數意味著將來可以作為實例標識為一種特定的類型;構造函數模式勝過工廠模式的地方
把構造函數當函數- 任何函數,只要通過 new 操作符來調用,那它就可以作為構造函數;
//當做構造函數使用
var person = new Person("Simon", 29, "software Engineer"); person.sayName(); //Simon //普通函數調用 Person("Genf", 23, "DOCTOR"); //添加到window window.sayName(); // Genf 以剛才的那種方式定義的構造函數定義在Global對象中(在瀏覽器中是window對象),在全局作用域中調用函數時,this指向的是window對象 // 在另外一個對象的作用域中調用 var o = new Object(); Person.call(o, "Kristen", 25, "nusd"); o.sayName(); // Kristen構造函數的缺陷
每個方法都要在每個實例上重新創建一遍。
person1 和 person2 都有一個名為 sayName() 的方法;但那兩個方法都不是同一個 Function 的實例,因此會有不同的作用域鏈和標識符解析;不同實例上的同名函數是不同的。
不要忘了,每個函數都是一個對象!所以sayName方法也可以這樣寫,因此每個Person實例都包含著不同的Function實例。以這種方式創建函數,會導致不同餓作用域和標識符解析。
this.sayName = new Function("alert(this.name)"); //與聲明函數在邏輯上是等價的
我們可以檢驗下
alert(person1.sayName() == person2.sayName) //false;
創建兩個完成相同任務的Function 實例沒有必要,況且有this對象在,根本不用在執行代碼前就把函數綁定到特定對象上面。
我們可以通過函數定義轉移構造函數外部來解決這個問題。
function Person(name, age ,job)
{ this.name = name; this.age = age; this.sayName = sayName; } function sayName() { alert(this.name); } var person1 = new Person("Simon", 29, "software Engineer"); var person2 = new Person("Zaynex", 29, "DOCTOR");
把sayName()函數的定義轉移到了構造函數外部。
在構造函數內部,將sayName屬性設置成等于全局的 sayName 函數。這樣sayName 包含的是一個指向函數的指針。 person1和person2共享同一個sayName()函數。
但問題是:
在全局作用域中定義的函數實際上只能被某個對象調用,如果對象需要定義很多方法,那么就要定義多個全局函數。
因此我們需要用原型模式來解決這個問題。
原型模式我們創建的每個函數都有一個 prototype(原型) 屬性,這個屬性屬于指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例的共享的屬性和方法。即通過構造函數而創建的那個對象實例的原型對象。
我們不必將構造函數定義對象實例的信息中,而是可以將這些信息直接添加到對象原型中。
function Person(){ } Person.prototype.name ="Simon"; Person.prototype.age = 29; Person.prototype.job = "software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); var person2 = new Person(); person2.sayName(); alert(person1.sayName == person2.sayName);
實際上,person1和person2都不包含屬性和方法, 但可以調用person1.sayName().這是通過查找對象屬性的過程來實現的。
理解原型對象無論何時,只要創建了新函數,就會根據一組特定的規則為該函數創建一個 prototype 屬性,這個屬性指向函數的原型對象。
在默認情況下,所有原型都會自動獲得一個constructor(構造函數)屬性,這個屬性包含在一個指向 prototype屬性所在的函數的指針。舉例說明: Person.prototype.constructor 指向Person.
創建了自定義構造函數之后,其原型對象默認只會取得 constructor 屬性。其他方法都是從Object繼承來的。
調用構造函數的一個實例后,該實例內部將包含一個指針(ES5中稱為[[Prototype]],指向構造函數的原型對象。在腳本中沒有標準形式訪問[[Prototype]],但在FF,SF,Chrome中的每個對象都支持屬性_proto_;在其他實現中,該屬性對腳本不可見。
要明確的是, 這個鏈接存在于實例與構造函數的原型對象之間,而非實例與構造函數之間。
雖然在現實中無法訪問到[[Prototype]],但可以通過 isPrototypeOf()來確定是否存在這種關系。
在ES5中新增一個方法,使用 Object.getPrototypeOf()可以方便的獲取一個對象的原型
每當代碼讀取某個對象的某個屬性時,都會執行一次搜索, 1.先從實例本身開始搜索屬性,存在,搜索結束。若不存在,執行2 2.從實例的原型開始搜索屬性。
繼續剛才的代碼。如果我們繼續給實例添加相同的屬性,會怎樣?
function Person(){ } Person.prototype.name ="Simon"; 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 = "xiwenzheng"; alert(person1.name) //xiwenzheng ——來自實例 alert(person2.name) // Simon ——來自原型
在person1這個實例中重寫屬性,那么解釋器搜索到了實例本身的屬性直接返回,對于person2而言,實例中沒有屬性,那么再往實例的原型開始搜素屬性。
給對象添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性,就是阻止我們訪問原型對象,但并不會修改原型對象中的同名屬性。即使將person1.name 設置為 null 也不會影響原型對象中的同步屬性。
不過delete 實例屬性,就可以訪問原型對象中的屬性了。
function Person(){
} Person.prototype.name ="Simon"; 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 = "xiwenzheng"; alert(person1.name); //xiwenzheng ——來自實例 alert(person2.name); // Simon ——來自原型 delete person1.name; alert(person1.name); // Simon 來自原型
使用hasOwnProperty()可以檢測一個屬性是否存在實例中還是存在原型中,這個方法只在給定屬性存在于對象實例中才會返回 true;
我們繼續采用剛才刪除部分的整段代碼。
alert(person1.hasOwnProperty("name")); // 返回false
原先person1.name是存在對象實例中的(被我們設為了"Zaynex"),但是被我們delete了。
如果我們不delete的話,那就是true了。要想獲得原型屬性的描述符,必須要在原型對象上調用 Object.hasOwnPropertydDsecriptor();
in 操作符會在通過對象能夠訪問給定屬性時返回 true ,不論該實行存在于實例中還是原型中。
利用in:判斷是否有該屬性
利用hasOwnProperty()判斷是否存在對象實例中;
結合以后就可以判斷該屬性是在原型中還是在實例中。
function hasPrototypeProperty(object, name ){
return !object.hasOwnProperty(name) && (name in object);
}
person1.name = "Zaynex";
alert(hasPrototypeProperty(person1, "name")); //false;存在實例中
for-in 循環時,返回的都是通過對象訪問的、可枚舉的屬性(即將[[Enumberable]]標記為true的屬性),在ES5中constructor 和 prototype屬性的 [[Enumberable]]
設為false,但并不是所有瀏覽器都照此實現。
想取得對象上所有可枚舉的實例屬性,可以使用Object.Keys()方法。
function Person(){ } Person.prototype.name ="Simon"; 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 var p1 = new Person(); p1.name = "Rob"; p1.age = 29; var p1keys = Object.keys(p1); alert(p1keys); // name ,age
如果想得到所有實例屬性,不論是否可枚舉,都可以使用 Object.getOwnPropertyNames()
var keys = Object.keys(Person.prototype); alert(keys);// constructor, name ,age, job, sayName更簡單的原型語法
之前的例子中每添加一個屬性和方法都要 Person.prototype,我們進行適當的封裝。
function Person(){ } Person.prototype = { name : "Simon", age : 29; job : "software Engineer", sayName : function () { alert(this.name); } };
我們將 Person.prototype 設置為等于一個以對象字面量形式創建的新對象。
之前介紹到,每創建一個函數,同時會創建它的prototype對象,這個對象會指定獲得constructor 屬性。而我們在這里的語法本質上是重寫了默認的 prototype 對象。
所以 constructor屬性也編程了新對象的屬性。(指向Object構造函數),不再指向Person了。
instanceof 測試 Object 和 Person 都返回 true,但constructor 屬性則等于Object而不等于 Person ;
如果 constructor 的值很重要,則可以特意設置回適當的值
function Person(){ } Person.prototype = { constructor:Person, name : "Simon", job : "software Engineer", sayName : function () { alert(this.name); } }
注意,以這種方式重設constructor屬性會導致 [[Enumberable]]特性設置為true,但默認我們是不可枚舉constructor屬性的。
為了兼容ES5的JS引擎,可以用 Object.defineProperty();
function Person(){ } Person.prototype = { name : "Simon", job : "software Engineer", sayName : function () { alert(this.name); } } Object.defineProperty(Person.prototype, "constructor", { enumerable: false, value: Person });原型的動態性
在原型中找值的過程是一次搜索,因此我們對原型對象所做的任何修改都能立即從實例中反應出來——即使是先創建實例后修改原型。
不信你看:
var friend = new Person(); Person.prototype.sayHi = function(){ alert("hi"); }; friend.sayHi(); // "hi"
這個可以歸結于實例與原型之間的松散鏈接關系。我們首先會在實例中搜索sayHi的屬性,在沒找到的情況下會繼續搜索原型,因為實例與原型之間的鏈接只不過是一個指針。
但是如果重寫整個原型對象,情況就不一樣了。調用構造函數時會為實例添加一個指向最初原型的[[Prototype]]指針,而把原型修改為另外一個對象就等于切斷了構造函數與最初原型之間的聯系。
請記住,實例中的指針僅指向原型,而不指向構造函數。
function Person(){ } var friend = new Person(); Person.prototype = { constructor:Person, name : "Simon", job : "software Engineer", sayName : function () { alert(this.name); } }; friend.sayName(); //error
在這個例子中,我們創建了Person的實例,然后又重寫了其原型對象,然后在調用sayName()時發生錯誤,因此friend指向的原型不包含以該名字命名的屬性。
省略了為構造函數傳遞初始化參數這一環節,結果所有實例在默認情況下都取得相同的屬性值。
共享導致的問題,很多屬性可以共享,對于包含引用類型值的屬性來說,問題比較突出。
function Person(){
} Person.prototype = { constructor:Person, name : "Simon", 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 === person2.friends) // true;
修改person1.friends 引用的數組,添加字符串,由于 friends數組存在 Person.prototype 而非 person1中,所以修改也會造成person2.friends反映出來,如果我們的初衷就是要共享同一個數組,那無話可說。
可是一般都是要有屬于自己的全部屬性的。而這個問題正是我們很少看到有人多帶帶使用原型模式的原因。
組合使用構造函數模式和原型模式創建自定義類型的最常見方式就是這個。構造函數用于定義實例屬性,原型模式用于定義方法和共享的屬性。這樣,每個實例都會有自己的一份實例屬性的副本,但又同事共享著對方法的引用,最大限度節省了內存。此外,這種混成模式還支持向構造函數傳遞參數。
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,Court,Van alert(person2.friends); // shelby,Court alert(person1.friends === person2.friends); // false alert(person1.sayName === person2.sayName); // true
實例屬性都是在構造函數中定義的,所有實例共享的屬性是在 constructor 和方法sayName()是在原型中定義的。
動態原型模式當其他OO語言經驗開發人員看到獨立的構造函數和原型時,會感到困惑。因此出現了 動態原型模式———即把所有信息都封裝在了構造函數中,而通過在構造函數中初始化原型(僅在必要的情況下),又保持了同事使用構造函數和原型的優點。
換句話說,可以通過檢查某個應該存在的方法是否有效,來決定是否需要初始化原型。
function Person (name, age, job){ //屬性 this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; if( typeof this.sayName != "function"){ Person.prototype.sayName = function() { alert(this.name); }; } } // 只有在sayName不存在的情況下, 才將其添加到原型中,這段代碼只會在初次調用函數時執行。此后原型已經完成初始化,不需要再做修改。 var friends1 = new Person("Nicholas", 29, "software Engineer"); var friends2 = new Person("Zaynex",19,"Engineer"); friends1.sayName(); friends2.sayName();
由于第一次當friends1初始化之后,friends2就不需要再進行初始化原型。
詳情參考點擊此處
在上述幾種模式都不適用的情況下,我們可以使用寄生構造函數模式。
基本思想:創建一個函數,該函數的作用僅僅是封裝對象的代碼,然后再返回新創建的對象。
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寄生構造函數應用
在特殊情況下為對象創建構造函數。 假設我們想創建一個具有額外方法的特殊數組,由于不能直接修改Array構造函數,因此可以使用這個模式。
function SpecialArray(){ var values = new Array(); values.push.apply(values, arguments); values.toPipedString = function(){ return this.join("|"); } return values; } var colors = new SpecialArray("red", "blue", "green"); alert(colors.toPipedString()); // red|blue|green
寄生構造函數模式:返回的對象與構造函數或者與構造函數的原型屬性沒有關系;不能依賴于 instanceof操作符確定對象類型。因此不建議在已使用其他模式的情況下使用該種模式。
穩妥構造函數模式 應用場景穩妥對象,是指沒有公共屬性,其方法也不引用this的對象。適合在安全環境下(這些環境會禁止使用this 和 new),或者放置數據被其他應用程序改動時使用。
穩妥函數與寄生構造函數差異新創建的對象的實例方法不引用this。
不使用new 操作符調用構造函數。
function Person(name, age, job) {
var o = new Object(); //可以在這里定義私有變量和函數。 // //添加方法 o.sayName = function(){ alert(name); }; //返回對象 return o;
}
var friend = Person("Nicholas", 29, "software Engineer");
friend.sayName();
以這種模式創建的對象,除了使用sayName()方法以外,沒有其他辦法訪問name的值。
與計生構造函數模式類似,使用穩妥構造函數模式創建的對象與構造函數之間沒有什么關系,因此instanceof操作符對這種對象沒有意義。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86385.html
摘要:在上百種語言中算是命好的一個,還有就是最近納入高考體系的。由以下三個部分構成。就是對實現該標準規定的各個方面內容的語言的描述。是針對但經過擴展的用于的應用程序編程接口。將頁面映射為由節點構成的樹狀結構。 JavaScript的歷史這里就不再贅述了,當然JavaScript的歷史還是比較有意思的。在上百種語言中JavaScript算是‘命’好的一個,還有就是最近納入高考體系的python...
摘要:數據類型中有種簡單數據類型也稱為基本數據類型和。在中非空字符串,非零數字,任意對象,都被認為。而空字符串,和,,認為是。用于表示整數和浮點數。標識符由數字字母下劃線美元符組成,但首字母不能是數字。變量方法對象命名推薦駝峰法。 JavaScript語法 一.語法簡介 因為JavaScript語法和Java等語法非常類似。所以只是簡單介紹一下。 大小寫 JavaScript是大小寫敏感的語...
摘要:上一篇你不知道的筆記寫在前面這是年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響年是向上的一年在新的城市穩定連續堅持健身三個月早睡早起游戲時間大大縮減,學會生活。 上一篇:《你不知道的javascript》筆記_this 寫在前面 這是2019年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響2018年是向上的一年:在新的城市穩定、...
摘要:可以使用偵聽器或處理程序來預訂事件,以便事件發生時執行相應的代碼。響應某個事件的函數稱為事件處理程序或事件偵聽器。可以刪除通過級方法指定的事件處理程序。 JavaScript和HTML之間的交互是通過事件實現的。 事件:文檔或瀏覽器窗口中發生的一些特定的交互瞬間。 可以使用偵聽器(或處理程序來預訂事件),以便事件發生時執行相應的代碼。 1. 事件流 事件流:從頁面中接收事件的順序。 ...
摘要:全局執行環境的變量對象始終是作用域鏈中的最后一個變量對象。綜上,每個函數對應一個執行環境,每個執行環境對應一個變量對象,而多個變量對象構成了作用域鏈,如果當前執行環境是函數,那么其活動對象在作用域鏈的前端。 1.幾個概念 先說幾個概念:函數、執行環境、變量對象、作用域鏈、活動對象。這幾個東東之間有什么關系呢,往下看~ 函數 函數大家都知道,我想說的是,js中,在函數內部有兩個特殊...
摘要:實現原型鏈的方式如下讓原型對象稱為另一個構造函數的實例這個實例繼承了的屬性上述代碼繼承是通過來實現,創建的實例,并將該實例賦給。無疑,集兩者之大成,這才是最常用的繼承模式。 原型鏈 JavaScript的繼承主要依靠原型鏈來實現的。我們知道,構造函數,原型,和實例之間的關系:每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個原型對象的指針。 實現原型鏈...
閱讀 2943·2023-04-26 01:49
閱讀 2074·2021-10-13 09:39
閱讀 2289·2021-10-11 11:09
閱讀 928·2019-08-30 15:53
閱讀 2821·2019-08-30 15:44
閱讀 927·2019-08-30 11:12
閱讀 2981·2019-08-29 17:17
閱讀 2378·2019-08-29 16:57