摘要:在沒有必要創建構造函數,只想讓一個對象與另一個對象保持類似的情況下,原型式繼承是完全可以勝任的。方便我們進行面向對象的編程。
理解對象 屬性類型 1.數據屬性
特性:
Configurable : 表示能否通過 delete 刪除屬性,能否修改屬性特性,能否把屬性改為訪問器屬性
Enumerable : 表示能否通過 for in 循環返回
Writable : 表示能否修改屬性的值
Value : 包含屬性的值,讀取或寫入實際上操作的是這個值
2.訪問器屬性特性:
Configurable : 表示能否通過 delete 刪除屬性,能否修改屬性特性,能否把屬性改為訪問器屬性
Enumerable : 表示能否通過 for in 循環返回
Get : 讀取時調用的參數.默認值為 undefined
Set : 寫入時調用的參數。 默認值為 undefined
3.注意:訪問器屬性不能直接定義,必須使用 Object.defineProperty()定義。
修改屬性默認的特性,必須使用 Object.defineProperty()方法
get,set,并不一定要定義,只定義 get 為只讀,只定義 set 為只寫不可讀。
定義多個屬性可以使用 Object.defineProperties()方法
讀取屬性的特性,使用 Object.getOwnPropertyDescriptor()
創建對象 1.工廠模式定義一個方法接受參數,用于創建對象,并將其返回
function createPerson(name, age) { var o = new Object(); o.name = name; o.age = age; return o; } var person1 = createPerson("andy_chen", 18); var person2 = createPerson("andy_chen", 18);
工廠模式可以創建多個相似對象的問題,卻沒解決對象識別的問題。例如person1的類型是什么2.構造函數模式 :
function Person(name, age) { this.name = name; this.age = age; this.sayName = function() { alert(this.name); }; } var person1 = new Person("andy_chen", 18); var person2 = new Person("andy_chen", 18); person1.sayName(); person2.sayName();使用 new 操作符。實際上有以下 4 個步驟:
創建一個新對象
將構造函數的作用域賦給對象(使 this 指向新對象)
執行構造方法(為這個對象添加屬性)
返回新對象
構造函數的問題在于,每個方法都要在每個實例中重新創建一遍。即例子中,person1和person2的sayName的不相等的。3.原型模式: 理解原型對象
但是,完成同樣的功能的方法,卻每個實例都要創建一遍,這顯然不合理,所以,又出現了下面的原型模式
一圖勝千言:
只要創建了一個新函數,就會根據一組特定規則為該函數創建一個 prototype,這個屬性指向函數的對象原型。
對象原型中,則默認有一個 constructor 屬性,指向該新函數。
通過新函數創建的實例,有一個[[prototype]]屬性(在 chrome,firefox,safari 中該屬性即為proto),指向了新函數的 prototype。
注意:該屬性僅僅是執行構造函數的 prototype,也即是說,他們與構造函數沒有直接聯系了
讀取某個對象的屬性時,會先在實例上找,如果沒找到,則進一步在實例上的 prototype 屬性上找
為實例添加屬性的時候會屏蔽掉原型上屬性。這個時候即使置為 null 也沒法訪問到原型上的屬性,只有通過 delete 刪掉之后才可以
XXX.prototype.isPrototype(xxx), 可以用這個方法判定對象是否是該實例的原型對象
Object.getPrototypeOf() 用這個可以獲取實例對應的原型對象 (ES5 新增方法)
in 操作符多帶帶使用時: in 操作符 可以確定屬性是否存在于對象上(無論是存在于實例上還是原型上)
用于 for 循環中時,返回的是所有能夠通過對象訪問的,可枚舉的屬性。(IE8 中,如果開發者自定義 toString 類似的系統不可枚舉的方法,瀏覽器還是不會將它遍歷出來)
ES5:Object.keys() 可以返回一個包含所有可枚舉屬性的字符串數組
Object.getOwnPropertyNames() 可以返回所有實例屬性,無論是否可枚舉
//原型模式的實現: function Person() {} Person.prototype.name = "andy chen"; Person.prototype.sayName = function() { alert(this.name); };更簡單的原型語法
重寫整個 prototype,不過會導致 constructor 改變。所以需要重新指定 constructor.
//更簡單的原型語法 function Person() {} Person.prototype = { constructor: Person, //因為這種寫法會覆蓋掉原來的Person.prototype,需要重新為constructor賦值 name: "andy chen", sayName: function() { alert(this.name); } }; var person1 = new Person(); var person2 = new Person();
原型模式的問題:所有實例都共享一個prototype,類似上面的例子,person1,person2的name屬性是共享的。如果修改其中一個,會導致另一個也受影響。所以,才會出現下面構造函數與原型模式組合使用4.組合使用構造函數和原型模式
創建自定義類型最常見的方式就是組合使用構造函數和原型模式
構造函數定義實例屬性,而原型模式用于定義方法和共享的屬性. 所以,上面的例子可以改寫成這樣:
function Person(name) { this.name = name; } Person.prototype = { constructor: Person, sayName: function() { alert(this.name); } }; var person1 = new Person("andy chen"); var person2 = new Person("andy chen");
除了使用組合模式創建對象,還有以下幾種方式,可以針對不同的情況選擇。
5.動態原型模式在構造方法中,判斷是否是第一次進入使用構造方法,如果是,則添加一系列的方法到原型上
6.寄生構造函數模式類基本思想是創建一個函數,該函數的作用僅僅是封裝創建對象的代碼然后再返回新創建的對象。
7.穩妥構造函數模式: 穩妥對象指的是沒有公共屬性,而且其方法也不引用 this 對象。最適合用于一些安全的環境或者在防止數據被其他程序改動時使用
穩妥構造函數遵循與寄生構造函數類似的模式,但有兩點不同:
新創建的對象實例不引用 this.
不使用 new 操作符調用構造函數
繼承OO 語言一般擁有兩種繼承方式:接口繼承(只繼承方法簽名)以及實現繼承(繼承實際方法)
ES 無法像其他 OO 語言一樣支持接口繼承,只能依靠原型鏈實現 實現繼承
每個構造函數都有一個原型對象,原型對象包含一個指向構造函數的指針.
每個實例都包含一個指向原型對象的內部指針的內部屬性(在 chrome 中一般為proto屬性)
那么,如果我們有個新的構造函數,并讓它的原型對象等于另一個類型的實例,結果會怎樣.
對于這個新的構造函數,它的原型對象就變成了另一個類型的實例,而這個實例中,又包含一個內部屬性,指向了另一個原型對象(該原型對象內部 constructor 指向另一個構造函數),如果這個原型對象又是另一個類型的實例,則它又包含了一個內部屬性,繼續指向上層的原型對象。這樣層層遞進,就形成了原型鏈。
如下圖:
在實例中搜索屬性的時候,便是基于原型鏈來搜索的,先搜索實例,再在原型鏈上一層層往上搜,直到找到或者到原型鏈末端才會停下來
由于所有引用類型都繼承了 Object,所以原型鏈的最頂層是 Object
使用原型鏈實現繼承時,不能使用對象字面量創建原型方法,因為這樣會重寫原型鏈
原型鏈實現繼承的方式:function Animal() { this.name = "animal"; } Animal.prototype.getName = function() { return this.name; }; function Cat() { this.catName = "cat"; } Cat.prototype = new Animal(); var cat1 = new Cat(); var cat2 = new Cat(); alert(cat1.getName()); //由于第10行,將Cat的原型指向Animal的實例,因為實例中有指向Animal.prototype的指針。所以,這里可以訪問到getName() cat1.name = "changed name"; alert(cat2.getName());原型鏈的問題:
使用原型鏈,由于是使用新的實例作為子類型的原型,實例中卻包含了父類型的屬性,所以原來父類型的屬性,就都到了子類型的原型上了。這就會造成子類型的不同實例會共享同個屬性.如上例子中,第 15 行,改變 cat1 實例的 name 屬性影響到了 cat2 的 name 屬性
創建子類型的時候,不能向父類型傳遞參數
2. 借用構造函數由于原型鏈存在問題,所以便出現了借用構造函數的方法
在子類型的構造方法中,調用父類型的構造方法:SuperType.call(this); 將父類型的屬性添加到子類型上,并且可以傳遞參數給父類型
function Animal() { this.name = "animal"; } function Cat() { Animal.call(this); } var cat1 = new Cat(); var cat2 = new Cat(); cat1.name = "changed name"; alert(cat1.name); //changed name alert(cat2.name); //animal //借用構造函數的方式,各實例之間的屬性便不會互相影響借用構造函數問題:
類似創建對象單純使用構造方法一樣,也會造成公有的方法無法公用。所以一般也很少多帶帶使用此方式
3. 組合繼承組合原型鏈以及借用構造函數
使用原型鏈實現對原型屬性和方法的繼承
借用構造函數來實現對實例中屬性的繼承。
function Animal() { this.name = "animal"; } Animal.prototype.getName = function() { return this.name; }; function Cat() { Animal.call(this); //借用構造函數 } Cat.prototype = new Animal(); //原型鏈方式 Cat.prototype.constructor = Cat; //這里可以 var cat1 = new Cat(); var cat2 = new Cat(); cat1.name = "changed name"; alert(cat1.getName()); //changed name alert(cat2.getName()); //animal組合繼承的問題:
父類的屬性會存在于子類型的原型上,導致被不同實例共享。雖然由于借用構造函數之后,導致實例上又重寫了這些屬性,所以每個實例有各自的屬性。
另外,instanceof 和 isPrototypeOf 能夠識別基于組合繼承創建的對象
**組合繼承,并不完美
因為我們只需要繼承父類型原型上的屬性而已,不需要父類型實例的屬性。
還有更好的方法,但我們首先要先了解一下其他繼承方式**
//如果o為某個對象的prototype,則object返回的 對象,包含了該對象原型上的所有方法 function object(o) { function F() {} F.prototype = o; return new F(); }
Es5 新增的 Object.create() ,類似這樣。 在沒有必要創建構造函數,只想讓一個對象與另一個對象保持類似的情況下,原型式繼承是完全可以勝任的。不過,包含引用類型值的屬性始終會共享
5. 寄生式繼承在復制新對象后,繼續以某種方式增強對象,即為寄生式繼承。
function createAnother(original) { var clone = object(original); clone.sayHi = function() { doSomeThing(); }; return clone; }
在主要考慮對象而不是自定義類型和構造函數的時候,適合使用寄生式繼承
缺點: 類似單純的構造函數模式使用,函數不能復用
6. 寄生組合式繼承通過原型式繼承,繼承父類的原型方法。再通過構造函數方法,繼承父類的屬性。
function Animal() { this.name = "animal"; } Animal.prototype.getName = function() { return this.name; }; function Cat() { Animal.call(this); //借用構造函數 } //原型繼承方式 function object(superProto) { function F() {} F.prototype = superProto; return new F(); } Cat.prototype = object(Animal.prototype); //通過一個空的函數作為媒介,將空函數的原型指向父類型原型,并將子類型的原型指向這個空函數的實例。便只繼承父類原型上的屬性及方法 Cat.prototype.constructor = Cat; //這里可以之后添加子類的方法 Cat.prototype.run = function() { alert("cat run"); }; var cat1 = new Cat(); var cat2 = new Cat(); cat1.name = "changed name"; alert(cat1.getName()); //changed name alert(cat2.getName()); //animal
最后,寄生組合式繼承是引用類型最理想的繼承范式。
上述代碼還能再進一步優化。
//原型繼承方式 function object(superProto) { function F() {} F.prototype = superProto; return new F(); } //公用的繼承方法 function inheritPrototype(subType, superType) { subType.prototype = object(superType.prototype); subType.prototype.constructor = subType; } function Animal() { this.name = "animal"; } Animal.prototype.getName = function() { return this.name; }; function Cat() { Animal.call(this); //借用構造函數 } inheritPrototype(Cat, Animal); //調用此方法繼承原型 //這里可以之后添加子類的方法 Cat.prototype.run = function() { alert("cat run"); }; var cat1 = new Cat(); var cat2 = new Cat(); cat1.name = "changed name"; alert(cat1.getName()); //changed name alert(cat2.getName()); //animal小結
這是 js 對象的創建以及繼承,es6 中新增了關鍵字class和extend。方便我們進行面向對象的編程。
但是理解背后的繼承原理對我們編程過程中也是極有幫助的
:)
喜歡就收藏或者點個贊唄 !!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100598.html
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結方便自己復習詳細內容會在之后一一對應地補充上去有些在我的個人主頁筆記中也有相關記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結,方便自...
摘要:寫在前面金三銀四又到了一年一度的跳槽季相信大家都在準備自己面試筆記我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結方便自己復習詳細內容會在之后一一對應地補充上去有些在我的個人主頁筆記中也有相關記錄這里暫且放一個我的面試知識點目錄大家 寫在前面: 金三銀四, 又到了一年一度的跳槽季, 相信大家都在準備自己面試筆記, 我也針對自己工作中所掌握或了解的一些東西做了一個目錄總結,方便自...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
閱讀 2843·2023-04-26 01:02
閱讀 1863·2021-11-17 09:38
閱讀 791·2021-09-22 15:54
閱讀 2899·2021-09-22 15:29
閱讀 888·2021-09-22 10:02
閱讀 3432·2019-08-30 15:54
閱讀 2007·2019-08-30 15:44
閱讀 1586·2019-08-26 13:46