在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 代碼復用模式(推薦篇)
代碼復用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設計模式之狀態(tài)模式
JavaScript 核心(晉級高手必讀篇)
設計模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設計模式之中介者模式
設計模式之裝飾者模式
設計模式之模板方法
設計模式之外觀模式
強大的原型和原型鏈
設計模式之構造函數模式
揭秘命名函數表達式
深入理解J avaScript 系列(結局篇)
執(zhí)行上下文(Execution Contexts)
函數(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設計模式之適配器模式
設計模式之組合模式
設計模式之命令模式
S.O.L.I.D 五大原則之單一職責 SRP
編寫高質量 JavaScript 代碼的基本要點
求值策略
閉包(Closures)
對象創(chuàng)建模式(上篇)
This? Yes,this!
設計模式之代理模式
變量對象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向對象編程之一般理論
設計模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴倒置原則 DIP
設計模式之迭代器模式
立即調用的函數表達式
設計模式之享元模式
設計模式之原型模式
根本沒有“JSON 對象”這回事!
JavaScript 與 DOM(下)
面向對象編程之 ECMAScript 實現(xiàn)
全面解析 Module 模式
對象創(chuàng)建模式(下篇)
設計模式之職責鏈模式
S.O.L.I.D 五大原則之開閉原則 OCP
設計模式之橋接模式
設計模式之策略模式
設計模式之觀察者模式
代碼復用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設計模式之工廠模式

代碼復用模式(推薦篇)

介紹

本文介紹的四種代碼復用模式都是最佳實踐,推薦大家在編程的過程中使用。

模式 1:原型繼承

原型繼承是讓父對象作為子對象的原型,從而達到繼承的目的:

function object(o) {
    function F() {
    }  
    F.prototype = o;
    return new F();
}    
// 要繼承的父對象
var parent = {
    name: "Papa"
};  
// 新對象
var child = object(parent);  
// 測試
console.log(child.name); // "Papa"  
// 父構造函數
function Person() {
    // an "own" property
    this.name = "Adam";
}
// 給原型添加新屬性
Person.prototype.getName = function () {
    return this.name;
};
// 創(chuàng)建新person
var papa = new Person();
// 繼承
var kid = object(papa);
console.log(kid.getName()); // "Adam"  
// 父構造函數
function Person() {
    // an "own" property
    this.name = "Adam";
}
// 給原型添加新屬性
Person.prototype.getName = function () {
    return this.name;
};
// 繼承
var kid = object(Person.prototype);
console.log(typeof kid.getName); // "function",因為是在原型里定義的
console.log(typeof kid.name); // "undefined", 因為只繼承了原型

同時,ECMAScript5 也提供了類似的一個方法叫做 Object.create 用于繼承對象,用法如下:

/* 使用新版的ECMAScript 5提供的功能 */
var child = Object.create(parent);  
var child = Object.create(parent, {
    age: { value: 2} // ECMA5 descriptor
});
console.log(child.hasOwnProperty("age")); // true

而且,也可以更細粒度地在第二個參數上定義屬性:

// 首先,定義一個新對象man
var man = Object.create(null);  
// 接著,創(chuàng)建包含屬性的配置設置
// 屬性設置為可寫,可枚舉,可配置
var config = {
    writable: true,
    enumerable: true,
    configurable: true
};  
// 通常使用Object.defineProperty()來添加新屬性(ECMAScript5支持)
// 現(xiàn)在,為了方便,我們自定義一個封裝函數
var defineProp = function (obj, key, value) {
    config.value = value;
    Object.defineProperty(obj, key, config);
}  
defineProp(man, 'car', 'Delorean');
defineProp(man, 'dob', '1981');
defineProp(man, 'beard', false);

所以,繼承就這么可以做了:

var driver = Object.create( man );
defineProp (driver, 'topSpeed', '100mph');
driver.topSpeed // 100mph

但是有個地方需要注意,就是 Object.create(null)創(chuàng)建的對象的原型為 undefined,也就是沒有 toString 和 valueOf 方法,所以 alert(man);的時候會出錯,但 alert(man.car);是沒問題的。

模式 2:復制所有屬性進行繼承

這種方式的繼承就是將父對象里所有的屬性都復制到子對象上,一般子對象可以使用父對象的數據。

先來看一個淺拷貝的例子:

/* 淺拷貝 */
function extend(parent, child) {
    var i;
    child = child || {};
    for (i in parent) {
        if (parent.hasOwnProperty(i)) {
            child[i] = parent[i];
        }
    }
    return child;
}  
var dad = { name: "Adam" };
var kid = extend(dad);
console.log(kid.name); // "Adam"  
var dad = {
    counts: [1, 2, 3],
    reads: { paper: true }
};
var kid = extend(dad);
kid.counts.push(4);
console.log(dad.counts.toString()); // "1,2,3,4"
console.log(dad.reads === kid.reads); // true

代碼的最后一行,你可以發(fā)現(xiàn) dad 和 kid 的 reads 是一樣的,也就是他們使用的是同一個引用,這也就是淺拷貝帶來的問題。

我們再來看一下深拷貝:

/* 深拷貝 */
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) ? [] : {};
                extendDeep(parent[i], child[i]);
            } else {
                child[i] = parent[i];
            }
        }
    }
    return child;
}  
var dad = {
    counts: [1, 2, 3],
    reads: { paper: true }
};
var kid = extendDeep(dad);  
kid.counts.push(4);
console.log(kid.counts.toString()); // "1,2,3,4"
console.log(dad.counts.toString()); // "1,2,3"  
console.log(dad.reads === kid.reads); // false
kid.reads.paper = false;

深拷貝以后,兩個值就不相等了,bingo!

模式 3:混合(mix-in)

混入就是將一個對象的一個或多個(或全部)屬性(或方法)復制到另外一個對象,我們舉一個例子:

function mix() {
    var arg, prop, child = {};
    for (arg = 0; arg < arguments.length; arg += 1) {
        for (prop in arguments[arg]) {
            if (arguments[arg].hasOwnProperty(prop)) {
                child[prop] = arguments[arg][prop];
            }
        }
    }
    return child;
}  
var cake = mix(
                { eggs: 2, large: true },
                { butter: 1, salted: true },
                { flour: '3 cups' },
                { sugar: 'sure!' }
                );  
console.dir(cake);

mix 函數將所傳入的所有參數的子屬性都復制到 child 對象里,以便產生一個新對象。

那如何我們只想混入部分屬性呢?該個如何做?其實我們可以使用多余的參數來定義需要混入的屬性,例如 mix(child,parent,method1,method2)這樣就可以只將 parent 里的 method1 和 method2 混入到 child 里。上代碼:

// Car 
var Car = function (settings) {
    this.model = settings.model || 'no model provided';
    this.colour = settings.colour || 'no colour provided';
};  
// Mixin
var Mixin = function () { };
Mixin.prototype = {
    driveForward: function () {
        console.log('drive forward');
    },
    driveBackward: function () {
        console.log('drive backward');
    }
};  
// 定義的2個參數分別是被混入的對象(reciving)和從哪里混入的對象(giving)
function augment(receivingObj, givingObj) {
    // 如果提供了指定的方法名稱的話,也就是參數多余3個
    if (arguments[2]) {
        for (var i = 2, len = arguments.length; i < len; i++) {
            receivingObj.prototype[arguments[i]] = givingObj.prototype[arguments[i]];
        }
    }
    // 如果不指定第3個參數,或者更多參數,就混入所有的方法
    else {
        for (var methodName in givingObj.prototype) {
            // 檢查receiving對象內部不包含要混入的名字,如何包含就不混入了
            if (!receivingObj.prototype[methodName]) {
                receivingObj.prototype[methodName] = givingObj.prototype[methodName];
            }
        }
    }
}  
// 給Car混入屬性,但是值混入'driveForward' 和 'driveBackward'*/
augment(Car, Mixin, 'driveForward', 'driveBackward');  
// 創(chuàng)建新對象Car
var vehicle = new Car({ model: 'Ford Escort', colour: 'blue' });  
// 測試是否成功得到混入的方法
vehicle.driveForward();
vehicle.driveBackward();

該方法使用起來就比較靈活了。

模式 4:借用方法

一個對象借用另外一個對象的一個或兩個方法,而這兩個對象之間不會有什么直接聯(lián)系。不用多解釋,直接用代碼解釋吧:

var one = {
    name: 'object',
    say: function (greet) {
        return greet + ', ' + this.name;
    }
};  
// 測試
console.log(one.say('hi')); // "hi, object"  
var two = {
    name: 'another object'
};  
console.log(one.say.apply(two, ['hello'])); // "hello, another object"  
// 將say賦值給一個變量,this將指向到全局變量
var say = one.say;
console.log(say('hoho')); // "hoho, undefined"  
// 傳入一個回調函數callback
var yetanother = {
    name: 'Yet another object',
    method: function (callback) {
        return callback('Hola');
    }
};
console.log(yetanother.method(one.say)); // "Holla, undefined"  
function bind(o, m) {
    return function () {
        return m.apply(o, [].slice.call(arguments));
    };
}  
var twosay = bind(two, one.say);
console.log(twosay('yo')); // "yo, another object"  
// ECMAScript 5給Function.prototype添加了一個bind()方法,以便很容易使用apply()和call()。  
if (typeof Function.prototype.bind === 'undefined') {
    Function.prototype.bind = function (thisArg) {
        var fn = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
        return function () {
            return fn.apply(thisArg, args.concat(slice.call(arguments)));
        };
    };
}  
var twosay2 = one.say.bind(two);
console.log(twosay2('Bonjour')); // "Bonjour, another object"  
var twosay3 = one.say.bind(two, 'Enchanté');
console.log(twosay3()); // "Enchanté, another object"