因為在工作中看到bind,搞了很久之後才發現自己根本把bind和apply、call搞混了,決定先把這個部分給搞清楚。

前言

當我們在建立一個物件或函式(特別的物件)時,除了我們給他的東西以外,JavaScript還會同時給它三個方法:bind、call、apply

Bind

funcToCopy.bind(ObjToBind,[參數1, 參數2]):回傳一個函數,該函數的this指向您指定的物件,如有傳入參數,則該參數將會被固定。

首先介紹 bind(),我們都知道JavaScript有this這個神奇又容易出錯東西,當我們在物件外呼叫this時,this會指向全域變數,例如:瀏覽器就是指向window,但是如果我們在物件內呼叫this,this則指向這個物件,大家先看一下以下範例:

// 建立一個person物件
let person = {
    firstName: "Johlmike",
    lastName: "Lin",
    getFullName: function(){
        return this.firstName + " " + this.lastName;
    }
};
// 建立一個otherPerson物件
let otherPerson = {
    firstName: "Angelica",
    lastName: "Chih"
};

console.log(person.getFullName()); // "Johlmike Lin"
console.log(otherPerson.getFullName()); // Error!

我想大家都知道這個結果,因為在otherPerson裡面並沒有getFullName這個函數,所以我們要用bind來幫忙一下。

// 建立一個person物件
let person = {
    firstName: "Johlmike",
    lastName: "Lin",
    getFullName: function(){
        return this.firstName + " " + this.lastName;
    }
};
// 建立一個otherPerson物件
let otherPerson = {
    firstName: "Angelica",
    lastName: "Chih"
};

// 複製 getFullName 函數到 copyGetFullName ,並將 this 綁到 otherPerson
let copyGetFullName = person.getFullName.bind(otherPerson);

console.log(person.getFullName()); // "Johlmike Lin"
console.log(copyGetFullName()); // "Angelica Chih"

Call 和 Apply

funcToCall.call(objToBind, arg_1, arg_2):執行該函數,並且該函數的this指向您指定的物件,參數也使用您傳入的參數。

funcToApply.apply(objToBind, [arg_1, arg2]):基本上與call相同,但傳入參數的部分是接受一整個含有參數的陣列,而不是一個一個的參數。

call和apply基本上是一樣的東西,唯獨傳入的東西不一樣而已,和Bind不同的地方則是,這兩個函數會直接執行,並回傳這個函數該回傳的東西,來看以下範例應該很快就能清楚了:

// 建立一個person物件
let person = {
    firstName: "Johlmike",
    lastName: "Lin",
    getFullName: function(){
        return this.firstName + " " + this.lastName;
    }
};
// 建立一個otherPerson物件
let otherPerson = {
    firstName: "Angelica",
    lastName: "Chih"
};
// 立刻執行getFullName函數,this指向otherPerson,並回傳結果至otherPersonFullName
// call可改成apply
let otherPersonFullName = person.getFullName.call(otherPerson);

console.log(person.getFullName()); // "Johlmike Lin"
console.log(otherPersonFullName); // "Angelica Chih"

示範傳入參數:

// 建立一個person物件
let person = {
    firstName: "Johlmike",
    lastName: "Lin"
};
// 建立一個otherPerson物件
let otherPerson = {
    firstName: "Angelica",
    lastName: "Chih"
};
// 定義一個sayHello函數,接受一個招呼詞參數
function sayHello(helloWord) {
    console.log(this.firstName + " " + this.lastName + ", " + helloWord);
}

sayHello.call(person, "nice to meet you!");// "Johlmike Lin, nice to meet you!"
sayHello.apply(otherPerson, ["good day!"]);// "Angelica Chih, good day!"

Function Borrowing

其實就是跟其他物件借函數用的意思,示範一下大家就懂了,非常簡單好懂。

// 建立一個person物件
let person = {
    firstName: "Johlmike",
    lastName: "Lin",
    getFullName: function(){
        return this.firstName + " " + this.lastName;
    }
};
// 建立一個otherPerson物件
let otherPerson = {
    firstName: "Angelica",
    lastName: "Chih"
};
console.log(person.getFullName()); // "Johlmike Lin"

// person有getFullName,otherPerson沒有
// 但是otherPerson想用getFullName
// 只好借一下了!
console.log(person.getFullName.call(otherPerson)); // "Angelica Chih"

Function Currying

老實說我自己也不太清楚currying的意思,所以可能會有點出入,Function Currying可以把一個函數所要傳入的參數給簡化,簡單來說,就是把函數的某個或某些參數值給固定住的意思。

還記得bind嗎?好像有個功能沒提到,bind除了可以複製一份自己、綁定this並回傳以外,還可以讓這個複製的函數的參數固定,就像這樣:

// 這個函數會把參數相乘
function mutiply(num1, num2){
    console.log(num1 * num2);
}

// 複製一份,這次綁定的東西不管他,固定num1為5
var otherFunc = mutiply.bind(this, 5);

// 執行時會直接跳過第一個被固定的參數,直接傳給num2
otherFunc(10); // 5 * 10 = 50
otherFunc(20); // 5 * 20 = 100
otherFunc(100, 5); // 5 * 100 = 500

// 再複製一份,這次兩個參數都固定,num1為5,num2為20
var otherFunc2 = mutiply.bind(this, 5, 20);

// 不管輸入什麼都是 5 * 20 = 100
otherFunc2(10, 10); // 5 * 20 = 100
otherFunc2(20, 40); // 5 * 20 = 100
otherFunc2(100, 7); // 5 * 20 = 100