摘要:缺陷在子類構造函數中執行了一遍父類構造函數,在實現子類原型的類式繼承時又調用了一遍父類構造函數,因此調用了兩遍構造函數。
類式繼承 原理
類的原型對象的作用就是為類的原型添加公有屬性和公有方法,但類不能直接訪問這些屬性和方法,必須通過原型prototype來訪問。而我們實例化一個父類的時候,新創建的對象復制了父類的構造函數內的屬性與方法,并且將原型__proto__指向了父類的原型對象,這樣就擁有了父類原型對象上的屬性和方法,并且這個新創建的對象可直接訪問到父類原型對象上的屬性與方法,同樣也可以訪問從父類構造函數中復制的屬性和方法。
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對父類實例化,繼承了父類,所以說父類中的公有屬性要是引用類型,就會在子類中被所有實例共用,因此一個子類的實例更改子類原型從父類構造函數中繼承出來的公有屬性就會影響到其他子類。
2、由于子類實現的繼承是靠其原型prototype對父類的實例化實現,因此在創建父類的時候,是無法向父類傳遞參數的,因而實例化父類的時候也無法對父類構造函數內的屬性進行初始化。
構造函數(竊取)繼承 原理由于call方法可以更改函數的作用域,因此在子類中,對父類調用這個方法就是將子類中的變量在父類中執行一遍,
由于父類中是給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,所以父類的原型方法自然不會被子類繼承,而如果要想被子類繼承就必須要放在構造函數中,這樣創建出來的每個實例都會多帶帶擁有一份而不能共用,這樣就違背了代碼復用的原則.
組合繼承 原理在子類構造函數中執行父類構造函數,在子類原型上實例化父類,融合了類式繼承和構造函數繼承兩者的優點。并過濾了其缺點。
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"];缺陷
在子類構造函數中執行了一遍父類構造函數,在實現子類原型的類式繼承時又調用了一遍父類構造函數,因此調用了兩遍構造函數。
原型式繼承 原理對類式繼承的一個封裝
// 聲明一個過渡對象繼承父對象, 并返回過渡對象的實例 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"];缺陷
與類式繼承一樣, 父類對象中的值類型被復制, 引用類型的屬性被共用.
寄生式繼承 原理對原型繼承的第二次封裝, 并且在第二次封裝過程中對繼承的對象進行擴展,
這樣新創建的對象不僅僅有父類中的屬性和方法, 而且還添加新的屬性和方法.
寄生式繼承依托于原型繼承模式同時也是為了寄生組合式繼承模式的實現。
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寄生組合式繼承 原理
對子類賦予父類原型的一個引用.即需要父類的原型對象的一個副本, 而這副本可以通過原型繼承得到,
但因為這樣直接賦值給子類會造成父類原型對象復制得到的復制對象p中的constructor指向不是子類對象,
因此需要對復制對象p做一次增強, 修復其constructor屬性指向不正確的問題, 最后將得到的復制對象p賦值給子類的原型, 這樣子類的原型就繼承了父類的原型并且沒有執行父類的構造函數.
function inheritObject(o) { // 聲明一個過渡對象繼承父對象, 并返回過渡對象的實例 function F() {}; F.prototype = o; return new F(); } function inheritPrototype(subClass, superClass) { // 復制一份父類的原型副本保存在變量中 var p = inheritObject(superClass.prototype); // 修正因為重寫子類原型導致子類的constructor屬性被修改 p.constructor = subClass; // 設置子類原型 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.對象, 通過點語法的形式一個一個添加方法, 否則直接賦予對象就會覆蓋從父類原型繼承的對象.
單繼承單繼承 屬性復制
var extend = function(target, source) { // 遍歷源對象的屬性 for(var property in source) { // 將源對象中的屬性復制到目標對象中 target[property] = source[property]; } //返回目標對象 return target; };多繼承
多繼承 屬性復制
var mix = function() { var i = 1, // 從第二個參數起為被繼承的對象 len = arguments.length, // 獲取參數長度 target = arguments[0], // 第一個對象為目標對象 arg; // 緩存參數對象 for(; i < len; i++) { // 緩存當前對象 arg = arguments[i]; // 遍歷被繼承對象中的屬性 for(var property in arg) { // 將被繼承對象中的屬性復制到目標對象中 target[property] = arg[property]; } } // 返回目標對象 return target; };
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87880.html
摘要:基于原型的面向對象在基于原型的語言中如并不存在這種區別它只有對象不論是構造函數,實例,原型本身都是對象。允許動態地向單個的對象或者整個對象集中添加或移除屬性。為了解決以上兩個問題,提供了構造函數創建對象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認識面向對象 1. JavaScript...
摘要:基于原型的面向對象在基于原型的語言中如并不存在這種區別它只有對象不論是構造函數,實例,原型本身都是對象。允許動態地向單個的對象或者整個對象集中添加或移除屬性。為了解決以上兩個問題,提供了構造函數創建對象的方式。 showImg(https://segmentfault.com/img/remote/1460000013229218); 一. 重新認識面向對象 1. JavaScript...
摘要:避免脆弱的基類問題。紅牌警告沒有提到上述任何問題。單向數據流意味著模型是單一的事實來源。單向數據流是確定性的,而雙向綁定可能導致更難以遵循和理解的副作用。原文地址 1. 你能說出兩種對 JavaScript 應用開發者而言的編程范式嗎? 希望聽到: 2. 什么是函數編程? 希望聽到: 3. 類繼承和原型繼承的不同? 希望聽到 4. 函數式編程和面向對象編程的優缺點? ...
摘要:眾多面向對象的編程思想雖不盡一致,但是無論哪種面向對象編程語言都具有以下的共通功能。原型編程以類為中心的傳統面向對象編程,是以類為基礎生成新對象。而原型模式的面向對象編程語言沒有類這樣一個概念。 什么是面向對象?這個問題往往會問到剛畢業的新手or實習生上,也是往往作為一個技術面試的開頭題。在這里我們不去談如何答(fu)好(yan)問(guo)題(qu),僅談談我所理解的面向對象。 從歷...
閱讀 3952·2021-11-24 09:38
閱讀 1421·2021-11-19 09:40
閱讀 2778·2021-11-18 10:02
閱讀 3691·2021-11-09 09:46
閱讀 1765·2021-09-22 15:27
閱讀 3110·2019-08-29 15:24
閱讀 997·2019-08-29 12:40
閱讀 1683·2019-08-28 18:24