復用是一項非常重要的生涯妙技,由於性命是有限的,無意義的重複即是糟蹋性命。作為一個順序開發者,代碼的復用既是一種才能,也是對主動生涯的一種立場。那末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