摘要:缺陷在子類構(gòu)造函數(shù)中執(zhí)行了一遍父類構(gòu)造函數(shù),在實現(xiàn)子類原型的類式繼承時又調(diào)用了一遍父類構(gòu)造函數(shù),因此調(diào)用了兩遍構(gòu)造函數(shù)。
類式繼承 原理
類的原型對象的作用就是為類的原型添加公有屬性和公有方法,但類不能直接訪問這些屬性和方法,必須通過原型prototype來訪問。而我們實例化一個父類的時候,新創(chuàng)建的對象復(fù)制了父類的構(gòu)造函數(shù)內(nèi)的屬性與方法,并且將原型__proto__指向了父類的原型對象,這樣就擁有了父類原型對象上的屬性和方法,并且這個新創(chuàng)建的對象可直接訪問到父類原型對象上的屬性與方法,同樣也可以訪問從父類構(gòu)造函數(shù)中復(fù)制的屬性和方法。
var Parent = function() { this.member = ["father", "mother"]; }; var Child = function() {}; Child.prototype = new Parent(); // test: var child1 = new Child(); var child2 = new Child(); console.log(child1.member); // ["father", "mother"]; console.log(child2.member); // ["father", "mother"]; child1.member.push("uncle"); console.log(child1.member); // ["father", "mother", "uncle"]; console.log(child2.member); // ["father", "mother", "uncle"];缺陷
1、由于子類通過其原型prototype對父類實例化,繼承了父類,所以說父類中的公有屬性要是引用類型,就會在子類中被所有實例共用,因此一個子類的實例更改子類原型從父類構(gòu)造函數(shù)中繼承出來的公有屬性就會影響到其他子類。
2、由于子類實現(xiàn)的繼承是靠其原型prototype對父類的實例化實現(xiàn),因此在創(chuàng)建父類的時候,是無法向父類傳遞參數(shù)的,因而實例化父類的時候也無法對父類構(gòu)造函數(shù)內(nèi)的屬性進行初始化。
構(gòu)造函數(shù)(竊取)繼承 原理由于call方法可以更改函數(shù)的作用域,因此在子類中,對父類調(diào)用這個方法就是將子類中的變量在父類中執(zhí)行一遍,
由于父類中是給this綁定屬性的,因此子類自然就繼承了父類中的公有屬性。
var Parent = function() { this.member = ["father", "mother"]; this.speak = function() { console.log("Chinese!"); } }; Parent.prototype = { constructor: Parent, say: function() { console.log("Hi!"); } }; var Child = function() { Parent.call(this); }; // test: var child1 = new Child(); var child2 = new Child(); console.log(child1.member); // ["father", "mother"]; console.log(child2.member); // ["father", "mother"]; console.log(child1.speak()); // Chinese! console.log(child1.say()); // Uncaught TypeError: child1.say is not a function child1.member.push("uncle"); console.log(child1.member); // ["father", "mother", "uncle"]; console.log(child2.member); // ["father", "mother"];缺陷
由于這種類型的繼承沒有涉及原型prototype,所以父類的原型方法自然不會被子類繼承,而如果要想被子類繼承就必須要放在構(gòu)造函數(shù)中,這樣創(chuàng)建出來的每個實例都會多帶帶擁有一份而不能共用,這樣就違背了代碼復(fù)用的原則.
組合繼承 原理在子類構(gòu)造函數(shù)中執(zhí)行父類構(gòu)造函數(shù),在子類原型上實例化父類,融合了類式繼承和構(gòu)造函數(shù)繼承兩者的優(yōu)點。并過濾了其缺點。
var Parent = function() { this.member = ["father", "mother"]; this.speak = function() { console.log("Chinese!"); } }; Parent.prototype = { constructor: Parent, say: function() { console.log("Hi!"); } }; var Child = function() { Parent.call(this); }; Child.prototype = new Parent(); // test: var child1 = new Child(); var child2 = new Child(); console.log(child1.member); // ["father", "mother"]; console.log(child2.member); // ["father", "mother"]; console.log(child1.speak()); // Chinese! console.log(child1.say()); // Hi! child1.member.push("uncle"); console.log(child1.member); // ["father", "mother", "uncle"]; console.log(child2.member); // ["father", "mother"];缺陷
在子類構(gòu)造函數(shù)中執(zhí)行了一遍父類構(gòu)造函數(shù),在實現(xiàn)子類原型的類式繼承時又調(diào)用了一遍父類構(gòu)造函數(shù),因此調(diào)用了兩遍構(gòu)造函數(shù)。
原型式繼承 原理對類式繼承的一個封裝
// 聲明一個過渡對象繼承父對象, 并返回過渡對象的實例 function inheritObject(o) { function F() {}; F.prototype = o; return new F(); }; var book = { name: "web", type: ["html", "css"] }; // test: var html5Book = inheritObject(book); html5Book.name = "html5Book"; html5Book.type.push("html5"); var jsBook = inheritObject(book); jsBook.name = "jsBook"; jsBook.type.push("js"); console.log(html5Book.name); console.log(html5Book.type); // ["html", "css", "html5", "js"]; console.log(jsBook.name); console.log(jsBook.type); // ["html", "css", "html5", "js"];缺陷
與類式繼承一樣, 父類對象中的值類型被復(fù)制, 引用類型的屬性被共用.
寄生式繼承 原理對原型繼承的第二次封裝, 并且在第二次封裝過程中對繼承的對象進行擴展,
這樣新創(chuàng)建的對象不僅僅有父類中的屬性和方法, 而且還添加新的屬性和方法.
寄生式繼承依托于原型繼承模式同時也是為了寄生組合式繼承模式的實現(xiàn)。
function inheritObject(o) { // 聲明一個過渡對象繼承父對象, 并返回過渡對象的實例 function F() {}; F.prototype = o; return new F(); } var book = { name: "web", type: ["html", "css"] }; function createBook(obj) { var o = new inheritObject(obj); o.getName = function() { console.log("webBook"); }; return o; } var newBook = createBook(book); console.log(newBook.name); // web console.log(newBook.type); // ["html", "css"] console.log( newBook.getName() ); // webBook寄生組合式繼承 原理
對子類賦予父類原型的一個引用.即需要父類的原型對象的一個副本, 而這副本可以通過原型繼承得到,
但因為這樣直接賦值給子類會造成父類原型對象復(fù)制得到的復(fù)制對象p中的constructor指向不是子類對象,
因此需要對復(fù)制對象p做一次增強, 修復(fù)其constructor屬性指向不正確的問題, 最后將得到的復(fù)制對象p賦值給子類的原型, 這樣子類的原型就繼承了父類的原型并且沒有執(zhí)行父類的構(gòu)造函數(shù).
function inheritObject(o) { // 聲明一個過渡對象繼承父對象, 并返回過渡對象的實例 function F() {}; F.prototype = o; return new F(); } function inheritPrototype(subClass, superClass) { // 復(fù)制一份父類的原型副本保存在變量中 var p = inheritObject(superClass.prototype); // 修正因為重寫子類原型導(dǎo)致子類的constructor屬性被修改 p.constructor = subClass; // 設(shè)置子類原型 subClass.prototype = p; } var Parent = function(language) { this.language = language; this.member = ["father", "mother"]; this.speak = function() { console.log(this.language); } }; Parent.prototype = { constructor: Parent, say: function() { console.log("Hi!"); } }; var Child = function(language, name) { Parent.call(this, language); this.name = name; }; inheritPrototype(Child, Parent); // test: var child1 = new Child("English", "xiaoming"); var child2 = new Child("japanese", "xiaoli"); child1.member.push("uncle"); console.log( child1.speak() ); // English console.log( child1.say() ); // Hi! console.log( child1.member ); // ["father", "mother", "uncle"] console.log( child2.speak() ); // English console.log( child2.say() ); // Hi! console.log( child2.member ); // ["father", "mother"] Child.prototype.getName = function() { console.log("child~"); }; var child3 = new Child(); console.log( child3.getName() ); // child~ console.log( child3.say() ); // Hi~ Child.prototype = { getMember: function() { console.log(this.member); } }; var child4 = new Child(); console.log( child4.getMember() ); // ["father", "mother"] console.log( child4.say() ); // Uncaught TypeError: child3.say is not a function缺陷
子類再想添加方法必須通過prototype.對象, 通過點語法的形式一個一個添加方法, 否則直接賦予對象就會覆蓋從父類原型繼承的對象.
單繼承單繼承 屬性復(fù)制
var extend = function(target, source) { // 遍歷源對象的屬性 for(var property in source) { // 將源對象中的屬性復(fù)制到目標(biāo)對象中 target[property] = source[property]; } //返回目標(biāo)對象 return target; };多繼承
多繼承 屬性復(fù)制
var mix = function() { var i = 1, // 從第二個參數(shù)起為被繼承的對象 len = arguments.length, // 獲取參數(shù)長度 target = arguments[0], // 第一個對象為目標(biāo)對象 arg; // 緩存參數(shù)對象 for(; i < len; i++) { // 緩存當(dāng)前對象 arg = arguments[i]; // 遍歷被繼承對象中的屬性 for(var property in arg) { // 將被繼承對象中的屬性復(fù)制到目標(biāo)對象中 target[property] = arg[property]; } } // 返回目標(biāo)對象 return target; };
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/87880.html
摘要:基于原型的面向?qū)ο笤诨谠偷恼Z言中如并不存在這種區(qū)別它只有對象不論是構(gòu)造函數(shù),實例,原型本身都是對象。允許動態(tài)地向單個的對象或者整個對象集中添加或移除屬性。為了解決以上兩個問題,提供了構(gòu)造函數(shù)創(chuàng)建對象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認(rèn)識面向?qū)ο?1. JavaScript...
摘要:基于原型的面向?qū)ο笤诨谠偷恼Z言中如并不存在這種區(qū)別它只有對象不論是構(gòu)造函數(shù),實例,原型本身都是對象。允許動態(tài)地向單個的對象或者整個對象集中添加或移除屬性。為了解決以上兩個問題,提供了構(gòu)造函數(shù)創(chuàng)建對象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認(rèn)識面向?qū)ο?1. JavaScript...
摘要:避免脆弱的基類問題。紅牌警告沒有提到上述任何問題。單向數(shù)據(jù)流意味著模型是單一的事實來源。單向數(shù)據(jù)流是確定性的,而雙向綁定可能導(dǎo)致更難以遵循和理解的副作用。原文地址 1. 你能說出兩種對 JavaScript 應(yīng)用開發(fā)者而言的編程范式嗎? 希望聽到: 2. 什么是函數(shù)編程? 希望聽到: 3. 類繼承和原型繼承的不同? 希望聽到 4. 函數(shù)式編程和面向?qū)ο缶幊痰膬?yōu)缺點? ...
摘要:眾多面向?qū)ο蟮木幊趟枷腚m不盡一致,但是無論哪種面向?qū)ο缶幊陶Z言都具有以下的共通功能。原型編程以類為中心的傳統(tǒng)面向?qū)ο缶幊蹋且灶悶榛A(chǔ)生成新對象。而原型模式的面向?qū)ο缶幊陶Z言沒有類這樣一個概念。 什么是面向?qū)ο螅窟@個問題往往會問到剛畢業(yè)的新手or實習(xí)生上,也是往往作為一個技術(shù)面試的開頭題。在這里我們不去談如何答(fu)好(yan)問(guo)題(qu),僅談?wù)勎宜斫獾拿嫦驅(qū)ο蟆?從歷...
閱讀 3959·2021-11-24 09:38
閱讀 1433·2021-11-19 09:40
閱讀 2783·2021-11-18 10:02
閱讀 3705·2021-11-09 09:46
閱讀 1778·2021-09-22 15:27
閱讀 3119·2019-08-29 15:24
閱讀 1006·2019-08-29 12:40
閱讀 1689·2019-08-28 18:24