JS學習筆記 - 代碼複用

本文章記錄本人在學習 JavaScript 中看書理解到的一些東西,加深記憶和而且整理記錄下來,方便以後的複習。編程

js 中複用代碼

說道代碼複用,通常都會涉及到對象繼承。在js中有許多能夠選擇的繼承方法。這些方法對於學習和理解多種不一樣的模式有很大的好處,由於它們有助於提供對語言的掌握程度。數組

可是在開發的過程當中,並非全部的代碼複用都會使用到繼承。其中一部緣由在於,事實上使用的js庫可能以這樣的或那樣的方式解決了該問題。而另外一方面的緣由就在於不多須要在js中創建長並且複雜的繼承鏈。在靜態強類型語言中,繼承能夠能是惟一複用代碼的方法。在js中,常常有更加簡潔並且優美的方法。包括:借用方法、綁定、複製屬性以及從多個對象中混入屬性等許多方法。閉包

混入

混入是針對經過屬性複製實現繼承的思想作進一步的擴展,mix-in模式並非複製一個完整的對象,而是從多個對象中複製出任意的成員並將這些成員組合成新的對象。app

實現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] = arguemnts[arg][prop];
            }
        }
    }
    return child;
}

mix-in實現很是簡單,只須要遍歷每一個參數,而且複製出傳遞給該函數的每一個對象中的每一個屬性。學習

借用方法

有的時候,咱們只須要對象中的一兩個方法,可是又不想和對象造成父-子繼承關係。只是想是用所須要的方法,而不但願繼承全部那些永遠用不到的屬性和方法。在這種狀況下,能夠經過使用借用方法模式來實現。測試

而這個方法是受益於call()apply()的。js中函數也是對象,而且它們自身也存在一些屬性和方法,好比callapply()this

使用call()apply()分別借用方法:prototype

// call
notmyobj.doStuff.call(myobj, param1, p2, p3);

// apply
notmyobj.doStuff.apply(myobj, [param1, p2, p3]);

在知道notmyobj對象上有doStuff方法的狀況下,又想不繼承notmyobj來使用doStuff方法。這個使用call()apply()就派上用場了。指針

還有一個場景是常用到借用方法的。那就是借用數組方法。由於數據具備許多很強大的方法,並且有時候須要操做arguments的時候會用上。可是arguments是一個僞數組,不具備原生數組強大的方法。這個使用借用方法就派上用場了:

function f() {
    var args = [].slice.call(arguemnts, 1, 3);
    return args;
}

借用和綁定

考慮到借用方法不是經過調用callapply()就是經過簡單的複製,在借用方法的內部,this所指向的對象是基於調用表達式而肯定的,可是有的時候「鎖定」this的值,或者將其綁定到特定的對象而且預先肯定該對象。

舉一栗子:

var one = {
    name: 'object',
    say: function (greet) {
        return greet + ", " + this.name;
    }
};

// 測試
one.say('hi'); // hi, object

接着另外一對象two中沒有say()方法,借用onesay()方法:

var two = {
    name: 'another object'
}
// 測試
one.say.call(two, 'hi'); // hi, another object

在上面的例子中,借用的say()方法的this是指向two的。可是在什麼樣的場景中,應該將函數指針賦值給一個全局變量,或者將該函數作爲回調函數來傳遞?在客戶端編程中有許多事件和回調,所以確實發生了許多這樣混淆的事件。

舉一個栗子:

// 給變量賦值
// this 將指向全局變量
var say = one.say;
say('hello') // hello, undefined

// 做爲回調傳遞
var yetanother = {
    name: 'yet another object',
    method: function (callback) {
        return callback('hola');
    }
};

// 測試
yetanother.method(one.say) // holla, undefined

在上面的兩種狀況下,say()方法的this值都是指向全局對象。並且整個代碼都沒法按照預期來運行。爲了修復(綁定)對象與方法之間的關係。可使用一個簡單的函數來實現:

function bind(o, m) {
    return function () {
        return m.apply(o, [].slice.call(arguments))
    }

上面的bind()方法接受兩個參數。一個是對象o,另外一個是方法m,並將二者綁定起來。而後返回一個函數。

使用bind()來解決問題:

var twosay = bind(two, one.say);
twosay('yo'); // yo another object

奢侈的擁有綁定所須要輔助的代價就是額外的閉包的開銷。

ES5 bind()

ECMAScript5中給Function.protoype添加了一個bind()方法,使得bind()call()、apply()同樣簡單易用。

可是在不支持ECMAScript5的運行環境下,咱們能夠本身實現一個bind()方法(來自 MDN):

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis || window,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

而後使用自帶的bind()方法來重寫一下上面的栗子:

var twosay = bind(two, one.say);
twosay('Bonjour'); // yo another object

最後,若是文章有什麼錯誤和疑問的地方,請指出。與sf各位共勉!

相關文章
相關標籤/搜索