1. 程式人生 > >前端基礎:call,apply,bind的的理解

前端基礎:call,apply,bind的的理解

需求 fmt prot 上一個 article 綁定 stat data font

背景

前兩天在做小程序的需求的時候用到bind的時候才想起自己對這三的東西的了解比較淺薄,這個時候用的時候就有點怕。時候還是要好好學習下,理解下怎麽玩。

正文

先說callapply吧:
ECMAScript3給Function的原型定義了兩個方法,他們是Function.prototype.callFunction.prototype.apply. 在實際開發中,特別是在一些函數式風格的代碼編寫中,call和apply方法尤為有用。

1、call和apply區別

其實他們的作用是一樣的,只是傳遞的參數不一樣而已。

apply: 接受2個參數,第一個參數指定了函數體內this對象的指向,第二個參數為數組或者一個類數組。apply傳入的是一個參數數組,也就是將多個參數組合成為一個數組傳入,而call則作為call的參數傳入(從第二個參數開始)。
舉個栗子:

let obj1 = {
    name: "copyes",
    getName: function(){
        return this.name;
    }
}
let obj2 = {
    name: ‘Fanchao‘
}

console.log(obj1.getName());  // "copyes"
console.log(obj1.getName.call(obj2));  // "Fanchao"
console.log(obj1.getName.apply(obj2));  // "Fanchao"
function showArgs(a, b, c){
    console.log(a,b,c);
}

showArgs.call(this, 3,4,5);
showArgs.apply(this, [5,6,7]);

2、常見的call 和 apply 用法

  • 數組之間追加

let arr1 = [12, ‘foo‘, {name: ‘fanchao‘}, -1024];
let arr2 = [‘copyes‘, ‘22‘, 1024];

Array.prototype.push.apply(arr1, arr2);
console.log(arr1);
// [ 12, ‘foo‘, { name: ‘fanchao‘ }, -1024, ‘copyes‘, ‘22‘, 1024 ]
  • 獲取數組中的最大值和最小值

let numbers = [5,665,32,773,77,3,996];
let maxNum = Math.max.apply(Math, numbers);
let maxNum2 = Math.min.call(Math, 5,665,32,773,77,3,996);

console.log(maxNum);
console.log(maxNum2);
  • 驗證是否是數組(前提是toString()方法沒有被重寫過)

function isArray(obj){
    return Object.prototype.toString.call(obj)  === ‘[object Array]‘;
}

console.log(isArray(1));
console.log(isArray([1,2]));
  • 類(偽)數組使用數組方法

var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

3、通過一個面試題去深入理解下apply 和 call

定義一個 log 方法,讓它可以代理 console.log 方法:

// 常規做法
function log(msg){
    console.log(msg);
}
log(12);
log(1,2)

上面能夠基本解決打印輸出問題,但是下面多參數的時候就gg了。換個更好的方式吧。

function log(){
    console.log.apply(console, arguments);
}
log(12);
log(1,2)

接下來是要在每一條打印信息前面都要加上一個特定字符串‘fanchao`s‘又怎麽說呢?

function log(){

    let args = Array.prototype.slice.call(arguments);
    
    args.unshift(‘(fanchao`s)‘);

    console.log.apply(console, args);
}
log(12);
log(1,2)

4、說完了call和apply,接下來說說bind

bind() 方法與 apply 和 call 很相似,也是可以改變函數體內 this 的指向。
MDN的解釋是:bind()方法會創建一個新函數,稱為綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind()方法的第一個參數作為 this,傳入 bind() 方法的第二個以及以後的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。
看個栗子:

var func = function(){
    console.log(this.x);
    console.log(arguments);
}

 
func();  // undefined, {}
var obj = {
    x: 2
}
var bar = func.bind(obj,1);

bar(); // 2 , {‘0‘:1}

一個有意思的事:

var bar = function() {
    console.log(this.x);
}
var foo = {
    x: 3
}
var sed = {
    x: 4
}
var func = bar.bind(foo).bind(sed);
func(); //3

var fiv = {
    x: 5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3

在Javascript中,多次 bind() 是無效的。更深層次的原因, bind() 的實現,相當於使用函數在內部包了一個 call / apply ,第二次 bind() 相當於再包住第一次 bind() ,故第二次以後的 bind 是無法生效的。

5、這三個方法的異同點是什麽呢?

還是先看個栗子:

var obj = {
x: 81,
};

var foo = {
getX: function() {
return this.x;
}
}

console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81

看到bind後面對了一對括號。區別是,當你希望改變上下文環境之後並非立即執行,而是回調執行的時候,使用 bind() 方法。而 apply/call 則會立即執行函數。

總結

apply 、 call 、bind 三者都是用來改變函數的this對象的指向的;
apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用後續參數傳參;
bind是返回對應函數,便於稍後調用;apply、call則是立即調用 。

前端基礎:call,apply,bind的的理解