Function.prototype.call.bind

在JavaScript中借用方法

在JavaScript中,有時候須要在一個不一樣的對象上重用一個函數,而不是在定義它的對象或者原型中。經過使用call(),applay()和bind(),咱們能夠很方便地從不一樣的對象借用方法,而不須要繼承它們 – 這是一個在專業JavaScript開發者的工具箱中頗有用的工具。javascript

這篇文章假設你已經充分了解了call()apply() 和 bind() 以及它們的不一樣點。html

在JavaScript中,你接觸的幾乎全部東西都是一個對象,除了string,number 和 booleans這樣不可變的原始值。一個數組是一種對象類型,適合用於有序數據列表的遍歷和修改,在它的原型中有不少有用的方法,例如slice,join,push 和 pop。java

咱們看到對象最多見的使用狀況就是從一個數組中借用方法,由於它們都是列表類型的數據結構。最常被借用的方法是 Array.prototype.slice程序員

function myFunc() {

    // 錯誤, arguments是一個類數組對象, 不是一個真實的數組
    arguments.sort();

    // 借用 Array 原型中的方法 slice, 它接受一個類數組對象 (key:value)
    // 並返回一個真實的數組
    var args = Array.prototype.slice.call(arguments);

    // args 如今是一個真正的數組, 因此可使用Array的sort()方法
    args.sort();

}

myFunc('bananas', 'cherries', 'apples');

 

借用方法是可行的,由於call和apply容許咱們在一個不一樣的上下文中調用方法,是一個很好的方式來重用已經存在的函數,而無需讓一個對象擴展自另外一個對象。數組實際上在它的原型中定義了不少方法,並且通常是可重用的,下面再舉兩個例子是join和filter:編程

// 接收一個字符串 "abc" 並輸出 "a|b|c
Array.prototype.join.call('abc', '|');
 // 接收一個字符串並移除全部的非元音字母
Array.prototype.filter.call('abcdefghijk', function(val) {
    return ['a', 'e', 'i', 'o', 'u'].indexOf(val) !== -1;
}).join('');

正如你所看到的,不只僅對象能夠得益於從數組中借用方法,字符串也能夠。然而,由於方法通常被定義在了原型上,因此每次咱們借用方法都要寫String.prototype 或者 Array.prototype,這是不少餘而且很煩人的事。一種有效的方法的是使用Literals(字面量)。數組

Literal是一種語法語言結構, 遵循 JavaScript 的規則,MDN 上這樣解釋:數據結構

你可使用literals來表示JavaScript中的值。這些都是固定的值,不是變量,你能夠再腳本中按照字面寫出來。app

Literals容許咱們以縮略的形式訪問原型方法:ide

[].slice.call(arguments);
[].join.call('abc', '|');
''.toUpperCase.call(['lowercase', 'words', 'in', 'a', 'sentence']).split(',');

這樣變得簡單些了,但看起來仍是有點醜,仍是要使用 [] 和 ""來借用它們的方法。咱們能夠更進一步縮短,經過存儲一個引用,把字面量和它的方法做爲一個變量:函數式編程

var slice = [].slice; 
slice.call(arguments);

var join = [].join;
join.call('abc', '|');

var toUpperCase = ''.toUpperCase;
toUpperCase.call(['lowercase', 'words', 'in', 'a', 'sentence']).split(',');

經過引用被借用的方法,咱們能夠很方便地使用call()調用它,享受全部可重用性的好處。爲了繼續減小冗長,咱們來看一下是否能夠在每次調用的時候,不寫call() 或者 apply() 就能借用一個方法:

var slice = Function.prototype.call.bind(Array.prototype.slice);
slice(arguments);

var join = Function.prototype.call.bind(Array.prototype.join);
join('abc', '|');

var toUpperCase = Function.prototype.call.bind(String.prototype.toUpperCase);
toUpperCase(['lowercase', 'words', 'in', 'a', 'sentence']).split(',');

如你所見,經過使用Function.prototype.call.bind,咱們如今能夠靜態綁定「被借用」的來自不一樣本地原型的方法,可是var slice = Function.prototype.call.bind(Array.prototype.slice)究竟是如何工做的呢?

Function.prototype.call.bind 乍一眼看起來有些複雜,可是理解它是如何工做的很是有用。

  • Function.prototype.call是一個引用,用來調用一個函數而且把它的「this」值設置爲使用內部提到的方法。

  • 記住「bind」返回一個新的函數,這個函數老是會牢記它的「this」值。所以,.bind(Array.prototype.slice)會返回一個新的函數,它的「this」被永久地設置成了 Array.prototype.slice 函數。

經過結合以上兩個,咱們如今有了一個新的函數,它將會調用「call」函數而且「this」限定爲了「slice」函數。簡單地調用slice()即可以引用以前限定的方法。

繼承很棒,可是當程序員想要重用一些對象或者模塊中的常見功能時會常常求助於它。若是你正在單獨使用繼承來重用代碼,你可能會作錯事,在不少狀況下,簡單的借用一個方法會變得很是麻煩。

到目前爲止,咱們僅僅討論了借用本地方法,但其實能夠借用任何方法!使用下面的代碼來計算一個運動員所得的比賽分數:

var scoreCalculator = {
    getSum: function(results) {
        var score = 0;
        for (var i = 0, len = results.length; i < len; i++) {
            score = score + results[i];
        }
        return score;
    },
    getScore: function() {
        return scoreCalculator.getSum(this.results) / this.handicap;
    }
};

var player1 = {
    results: [69, 50, 76],
    handicap: 8
};

var player2 = {
    results: [23, 4, 58],
    handicap: 5
};

var score = Function.prototype.call.bind(scoreCalculator.getScore);

// Score: 24.375
console.log('Score: ' + score(player1));

// Score: 17
console.log('Score: ' + score(player2));

儘管上面的例子是人爲的,可是很容易看到用戶定義的方法也能像本地方法那樣被方便地借用。

Call, bind 和 apply 容許咱們改變函數被調用的方式,在借用一個函數時常常被使用。大多數開發者都很熟悉借用本地方法,可是不多知道用戶定義的方法也能夠。

在過去的幾年裏,JavaScript中的函數式編程逐漸增多,我但願簡短的使用Function.prototype.call.bind來借用方法將變得愈來愈廣泛。

連接:https://www.zcfy.cc/article/borrowing-methods-in-javascript-by-david-shariff-794.html

相關文章
相關標籤/搜索