摘要:那么在代碼復用方面都有哪些方法構造模式構造函數與普通函數的唯一區別在于調用方式不同構造函數首字母大寫只是慣例,任何函數都可以用關鍵字來作為構造函數調用構造函數普通函數。
復用是一項非常重要的生活技能,因為生命是有限的,無意義的重復等于浪費生命。作為一個程序開發者,代碼的復用既是一種能力,也是對積極生活的一種態度。那么JS 在代碼復用方面都有哪些方法?構造模式
...................................................................................................
構造函數與普通函數的唯一區別在于調用方式不同(構造函數首字母大寫只是慣例),任何函數都可以用new關鍵字來作為構造函數調用(構造函數 = new + 普通函數)。
function Parent() { this.name = "jim"; this.say = function() { console.log(this.name); }; console.log(this.name); } Parent(); // 輸出 jim console.log(Parent); // 輸出 Parent?(){/* 函數體-略 */} var child1 = new Parent(); // 輸出 jim 構造函數創建 child1 對象(解析執行) var child2 = new Parent(); // 輸出 jim 構造函數創建 child2 對象(解析執行) console.log(child1); // 輸出 Parent?{name: "jim", say: ? ()} console.log(child1.say); // 輸出 ? ()?{/* 函數體-略 */} child1.say(); // 輸出 jim child2.say(); // 輸出 jim console.log(child1.name); // 輸出 jim (child1 繼承了 Parent name) console.log(child2.name); // 輸出 jim (child2 繼承了 Parent name) child1.name = "tom1"; // 修改 child 的 name 屬性 child2.name = "tom2"; // 修改 child 的 name 屬性 child1.say(); // 輸出 tom1(說明 child 本地實例化了name屬性 ) child2.say(); // 輸出 tom2(說明 child 本地實例化了name屬性 ) console.log(child1.name); // 輸出 tom1(說明 child 本地實例化了name屬性 ) console.log(child2.name); // 輸出 tom2(說明 child 本地實例化了name屬性 ) delete child1.name; // 刪除 child1 的 name 屬性 delete child2.name; // 刪除 child2 的 name 屬性 console.log(child1.name); // 輸出 undefined(說明 child1 本地實例化name屬性已刪除 ) console.log(child2.name); // 輸出 undefined(說明 child2 本地實例化name屬性已刪除 ) Parent(); // 輸出 jim (說明構造函數屬性 和 構造對象屬性 沒有關系)
缺點:無法復用父對象屬性方法,當子對象數量變多,反復使用 new 重新創建父對象.
原型模式我們知道所有引用類型都是 Object,也就是說引用類型的原型是 Object,他們是一個繼承的關系。另外,原型的屬性可以自定義。
function fn() { this.keyThis = ["fnThisValue"]; } // name: "fn" prototype: {constructor: fn()} __proto__: Object // 函數名是 fn // 函數 prototype 指向一個對象,該對象的屬性constructor 指向函數自身 // 函數 __proto__ 指向 Object(重點 __proto__ 是一個原型引用指針,指向父級原型) // 此時fn 未執行, this 雖然指向window , 但是 keyThis 并未聲明和賦值 // 以上是 JS 內部已經實現好的,下面我們來自定義一個原型屬性 fn.prototype.keyProto = ["fnProtoValue"]; console.log(fn.prototype); // 輸出 {keyProto: ["fnProtoValue"], constructor: fn(),__proto__: Object} var foo = new fn(); // fn() 執行, this指向window,key1聲明和賦值 console.log(foo); // 輸出 // fn{ // keyThis:["fooThisValue"], // __proto__:{ keyProto: ["fnProtoValue"], constructor: fn(), __proto__: Object} // } // foo 僅僅是一個構造對象(重點對象沒有原型屬性),原型引用指針__proto__指向 fn 的原型 // 原型鏈 就是 __proto__:{__proto__:{···}} console.log(foo.keyThis); // 輸出 ["fooThisValue"] console.log(foo.keyProto); // 輸出 ["fnProtoValue"] foo.keyThis.push("fooThis"); foo.keyProto.push("fooProto"); console.log(foo); // 輸出 // fn{ // keyThis:["fooThisValue", "fooThis"], // __proto__:{ keyProto: ["fnProtoValue", "fooThis"], constructor: fn(), __proto__: Object} // } // foo 的原型屬性竟然被修改了,這應該不是我們想要的(小本本記下來),所以父級常量最好用 this 來定義 console.log(fn.prototype); // 輸出{ keyProto: ["fnProtoValue", "fooThis"], constructor: fn(), __proto__: Object}
缺點:雖然復用父對象屬性方法,當子對象數量變多,反復使用 new 重新創建父對象.
借用模式在 JS 基礎數據類型操作系列(四)函數 中,我們介紹了 call,apply 和 bind 的函數作用域借用操作,這也是一種代碼復用的好方法。
function Parent() { this.keyThis = ["fnThisValue"]; } Parent.prototype.keyProto = ["fnProtoValue"]; function Child() { Parent.call(this); console.log(this.keyThis); // 輸出 ["fnThisValue"] console.log(this.keyProto); // 輸出 undefined } Child(); // 這種借用只能夠針對 this 綁定的屬性方法起作用。 var jim = new Child(); console.log(jim.keyThis); // 輸出 ["fnThisValue"] console.log(jim.keyProto); // 輸出 undefined // 這種借用只能夠針對 this 綁定的屬性方法起作用。代理模式
function inherit(parent, child) { var F = function() {}; F.prototype = parent.prototype; child.prototype = new F(); child.prototype.constructor = child; } function Parent() { this.keyThis = ["fnThisValue"]; } Parent.prototype.keyProto = ["fnProtoValue"]; function Child() {} inherit(Parent, Child); var jim = new Child(); console.log(jim.keyThis); // 輸出 undefined console.log(jim.keyProto); // 輸出 ["fnProtoValue"]
缺點:只是代理了原型
標準模式在 ES 5 中,提供了Object.create()方法來實現原型構造繼承(語法糖)。
Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__
語法 :Object.create(proto, [propertiesObject]) 。
第二個可選參數是 null 或一個對象,添加到新創建對象的自定義可枚舉屬性,對應 Object.defineProperties()的第二個參數。
function Parent() {} Parent.prototype.keyProto = ["fnProtoValue"]; var jim = Object.create(Parent, { key: { value: "val" } }); console.log(jim); // 輸出 Function?{key: "val",__proto__: Parent()} jim.hasOwnProperty("key"); var Fn = { key:"value" } Object.create(Fn) // {__proto__:{ key:"value"}}克隆模式
通過復制屬性來實現繼承
淺克隆簡單對象,單層克隆
function extend(parent, child) { var i; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { child[i] = parent[i]; // 這里只是引用, 并非實例化 } } return child; } var Parent = { key:"value", arr:[1,2,3,4], obj:{ key:"value", arr:[1,2,3,4], } } var kid = extend(Parent) kid.arr.push(4); console.log(Parent.arr) // 輸出 [1,2,3,4,4]深克隆
復雜對象,遞歸克隆
function extendDeep(parent, child) { var i, toStr = Object.prototype.toString, astr = "[object Array]"; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { if (typeof parent[i] === "object") { child[i] = toStr.call(parent[i]) === astr ? [] : {}; arguments.callee(parent[i], child[i]); } else { child[i] = parent[i]; } } } return child; } var Parent = { key:"value", arr:[1,2,3,4], obj:{ key:"value", arr:[1,2,3,4], } } var kid = extendDeep(Parent) kid.arr.push(4); console.log(Parent.arr) // 輸出 [1,2,3,4]
缺點:針對的是對象,不是函數,當然對象用這個是最好的
總結綜上了解,我們想要一個既可以繼承this屬性,又可以繼承prototype屬性的方法。繼承this屬性最好用的是借用模式,繼承prototype屬性最好用的是Object.create()標準模式。
function parent() { this.money = 1000; } parent.prototype.say = function(money) { console.log("I have " + (this.money + money)); } function inherit(parent,childParams){ function Child() { parent.call(this); // 借用 父級 this 屬性 } childParams = childParams || {}; // 定義額外參數 Child.prototype = Object.create(parent.prototype,childParams); // parent.prototype 指向原型對象parent Prototype // Object.create(parent.prototype) // 輸出 {__proto__:{ say:? (money),constructor:? parent(), __proto__:Object}} Child.prototype.constructor = Child; // 原型的構造函數應該永遠指向自身 return new Child() } var jim = inherit(parent); var tom = inherit(parent,{key:{value:500}}); jim.say(100); //輸出 I have 1100 tom.say(500); //輸出 I have 1100 tom.key //輸出 500
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94480.html
摘要:如下代碼所示,可以使用構造函數來創建父對象,這樣做的話,自身的屬性和構造函數的原型的屬性都將被繼承。方法繼承自對象這是中構造函數鏈的一個示例。 代碼復用及其原則 代碼復用,顧名思義就是對曾經編寫過的代碼的一部分甚至全部重新加以利用,從而構建新的程序。在談及代碼復用的時候,我們首先可以想到的是繼承性。代碼復用的原則是: 優先使用對象組合,而不是類繼承 在js中,由于沒有類的概念,因此實例...
摘要:事件多路復用器收集資源的事件并且把這些事件放入隊列中,直到事件被處理時都是阻塞狀態。最后,處理事件多路復用器返回的每個事件,此時,與系統資源相關聯的事件將被讀并且在整個操作中都是非阻塞的。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關注我的專欄,之后的博文將在專欄同步:...
摘要:本文章記錄本人在學習中看書理解到的一些東西,加深記憶和并且整理記錄下來,方便之后的復習。但是在開發的過程中,并不是所有的代碼復用都會使用到繼承。而且整個代碼都無法按照預期來運行。為了修復綁定對象與方法之間的關系。 本文章記錄本人在學習 JavaScript 中看書理解到的一些東西,加深記憶和并且整理記錄下來,方便之后的復習。 js 中復用代碼 說道代碼復用,一般都會涉及到對...
摘要:,的事件回調函數中調用的操作方法。以為例調用關系模式實際就是將中的改名為,調用過程基本一致,最大的改良是間的雙向綁定。和間,有一個對象,可以操作修改,使用。 參考:MVC,MVP 和 MVVM 的圖示 - 阮一峰http://www.ruanyifeng.com/blo...Web開發的MVVM模式http://www.cnblogs.com/dxy198...界面之下:還原真實的MV...
摘要:,的事件回調函數中調用的操作方法。以為例調用關系模式實際就是將中的改名為,調用過程基本一致,最大的改良是間的雙向綁定。和間,有一個對象,可以操作修改,使用。 參考:MVC,MVP 和 MVVM 的圖示 - 阮一峰http://www.ruanyifeng.com/blo...Web開發的MVVM模式http://www.cnblogs.com/dxy198...界面之下:還原真實的MV...
閱讀 1481·2021-11-17 09:33
閱讀 1260·2021-10-11 10:59
閱讀 2892·2021-09-30 09:48
閱讀 1905·2021-09-30 09:47
閱讀 3024·2019-08-30 15:55
閱讀 2337·2019-08-30 15:54
閱讀 1493·2019-08-29 15:25
閱讀 1646·2019-08-29 10:57