javascript-call、apply和bind方法的理解

1. 介紹

剛出來找前端工作的時候,最常見的面試題就是“談談你對call和apply的理解”,以前只知道這些名詞,但是一點也不理解。隨著對jquery的熟悉發現jquery源碼中很多都用到了apply方法,就順便總結了一些功能類似的call和bind方法的使用。

JavaScript中的每一個Function對象的原型上都有一個apply()方法和一個call()方法,call 和 apply 都是為了改變某個函數運行時的 context 即上下文而存在的,換句話說,就是為了改變函數體內部 this 的指向。call和apply是為了動態改變this而出現的,當一個對象沒有某個方法,但是其它對象有,我們可以借助call或apply用其它對象的方法來操作。
bind方法也可以用來改變當前函數執行的上下文,和call、apply不同的是bind返回對應函數,便于稍后調用而apply 、call 則是立即調用 。

1.1 相關定義:

  • call()
    語法:fun.call([thisObj[,arg1[, arg2[, [,.argN]]]]])
    定義:調用一個對象的一個方法,以另一個對象替換當前對象。
    說明: call 方法可以用來代替另一個對象調用一個方法。call 方法可將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。 如果沒有提供 thisObj 參數,那么 Global 對象被用作 thisObj。
  • apply()
    語法:fun.apply([thisObj[,argArray]])
    定義:應用某一對象的一個方法,用另一個對象替換當前對象。
    說明: 如果 argArray 不是一個有效的數組或者不是 arguments 對象,那么將導致一個 TypeError。 如果沒有提供 argArray 和 thisObj 任何一個參數,那么 Global 對象將被用作 thisObj, 并且無法被傳遞任何參數。
  • bind()
    語法:fun.bind(thisArg[, arg1[, arg2[, ...]]])
    定義:創建一個新的函數, 當被調用時,將其this關鍵字設置為提供的值,在調用新函數時,可以在調用之前提供一個給定的參數序列。
    說明: 參數arg1, arg2, ...當綁定函數被調用時,這些參數將置于實參之前傳遞給被綁定的方法。這些參數會被插入到目標函數的參數列表的開始位置,傳遞給綁定函數的參數會跟在它們的后面。bind返回由指定的this值和初始化參數改造的原函數拷貝。

1.2 差異:

  • call和apply:call()方法接受的是若干個參數的列表,而apply()方法接受的是一個包含多個參數的數組。
  • bind和call、apply:bind返回對應函數,便于稍后調用而apply 、call 則是立即調用 。

1.3 應用場景:

  • 使用原生對象的方法
  • 實現繼承
  • bind方法的簡單使用

2. 使用

2.1 使用原生對象的方法

(1)類數組使用Array原生方法

let arrayLike = {0: "hello", 1: "sunny", 2: "~", length: 3};
let arrayLikeToArray1_1 = Array.prototype.slice.call(arrayLike); // ["hello", "sunny", "~"]
let arrayLikeToArray1_2 = Array.prototype.slice.call(arrayLike, 0, 2); // ["hello", "sunny"]
let arrayLikeToArray2_1 = Array.prototype.slice.apply(arrayLike); // ["hello", "sunny", "~"]
let arrayLikeToArray2_2 = Array.prototype.slice.apply(arrayLike, [0, 2]); // ["hello", "sunny"]

(2)擴展Array對象屬性,使類數組也可以使用這些靜態方法
測試的時候只擴展了部分屬性,如果有其它需求自己擴展呦~~~方法類似。

let array1 = ["hello", "sunny", "~"];
Array.join = function (a, sep) {

    // return Array.prototype.join.call(a, sep);
    return Array.prototype.join.apply(a, [sep]);
};
Array.slice = function (a, start, end) {

    // return Array.prototype.slice.call(a, start, end);
    return Array.prototype.slice.apply(a, [start, end]);
};
Array.map = function (a, callback, thisArg) {

    // return Array.prototype.map.call(a, callback, thisArg);
    return Array.prototype.map.apply(a, [callback, thisArg]);
};
console.log(Array.join(array1, "-")); // hello-sunny-~
console.log(Array.slice(array1, 0, 1)); // ["hello"]
Array.map(array1, function (value, index) {

    console.log("index=" + index + ",value=" + value);
}); // index=0,value=hello index=1,value=sunny index=2,value=~

說明:用call和apply都是可以擴展的哈,注釋掉的方法也是可行的。

2.2 實現繼承

子類使用父類的方法,嚴格來講不算是繼承,只是調用其它對象的方法(該方法內的this指向使用者)。

function Animal(name) {

    this.name = name || "Animal";
    this.showName = function() {

        console.log(this.name);
    }
}
function Cat(name) {

    this.name = name || "Cat";
}
let animal = new Animal();
let cat1 = new Cat();
let cat2 = new Cat("cat2");
// 子類使用父類的方法,也可以延伸至某些對象調用其它對象的方法
animal.showName.call(cat1); // Cat
animal.showName.apply(cat1); // Cat
animal.showName.call(cat2); // cat2
animal.showName.apply(cat2); // cat2

2.3 bind方法的簡單使用

說明:在實際開發中很少看到bind的使用,所以只做了兩個簡易的demo。對于bind的更高級用法,需要大家自己去摸索了,我的了解就僅限于這里了。
(1)創建綁定函數

window.name = "window";
let user = {
    name: "user",
    getName: function() {

        console.log(this.name);
    }
};
user.getName(); // user
let getUserName1 = user.getName; // this指向全局作用域
getUserName1(); // window
let getUserName2 = user.getName.bind(user);
getUserName2(); // user

(2)偏函數

function add() {

    let sum = 0;
    for (let i = 0, len = arguments.length; i < len; i++) {

        sum += arguments[i];
    }
    console.log(this + ":sum=" + sum);
}
let add1 = add.bind("add1", 2, 3);
add1(); // add1:sum=5
add1(5); // add1:sum=10
let add2 = add.bind("add2", "hello");
add2(); // add2:sum=0hello
add2(" sunny"); // add2:sum=0hello sunny

3. 擴展

3.1 類數組

  • 類數組是一個對象,雖然該對象并不是由Array構造函數所創建的,但它依然呈現出數組的行為。類數組對象擁有一個特性:可以在類數組對象上應用(利用call、apply方法)數組的操作方法。
  • 在瀏覽器環境中,document.getElementsByTagName()語句返回的就是一個類數組對象。在function調用中,function代碼內的arguments變量(保存傳入的參數)也是一個類數組對象。在ECMAScript 5標準中,字符串string就是一個只讀的類數組對象。

幾種常見的類數組:

let obj = {0: "hello", 1: "sunny", 2: "~", length: 3};
let anchor = document.getElementsByTagName("a");
let msg = "hello";
function test() {

    console.log(arguments); // arguments
}

3.2 柯里化

  • 之前看函數式編程的時候了解過一點,大概意思就是把一個有n個參數的函數變成n個只有1個參數的函數。
    柯里化的簡單實現:
// 普通函數
function sum1(x, y, z) {

    console.log("sum1:x+y+z=" + (x + y + z));
}
sum1(1, 3, 4); // sum1:x+y+z=8
// 柯里化后的函數
function sum2(x) {

    return function (y) {

        return function (z) {

            console.log("sum2:x+y+z=" + (x + y + z));
        }
    }
}
sum2(1)(3)(4); // sum2:x+y+z=8

3.3 偏函數

  • 偏函數,固定函數中的某一個或幾個參數,返回一個新的函數,接收剩下的參數, 參數個數可能是1個,也可能是2個,甚至更多。具體示例見bind方法簡單使用-demo2

4. 相關知識

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容