JS中call、apply、bind使用指南

為什么需要這些?主要是因為this,來看看this干的好事。

box.onclick = function(){
  function fn(){
    alert(this);
  }
  fn();
};

我們原本以為這里面的this指向的是box,然而卻是Window。一般我們這樣解決:

box.onclick = function(){
  var _this = this;
  function fn(){
    alert(_this);
  }
  fn();
};

將this保存下來。

還有一些情況,有時我們想讓偽數(shù)組也能夠調(diào)用數(shù)組的一些方法,這時call、apply、bind就派上用場了。

我們先來解決第一個問題修復(fù)this指向。

box.onclick = function(){
  function fn(){
    alert(this);
  }
  fn();
};

改成如下:

box.onclick = function(){
  function fn(){
    console.log(this);
  }
  fn.call(this);
};

很神奇吧,call的作用就是改變this的指向的,第一個傳的是一個對象,就是你要借用的那個對象。

fn.call(this);

這里的意思是讓this去調(diào)用fn這個函數(shù),這里的this是box,這個沒有意見吧?如果這個你不清楚,很可能你是javscript的新朋友。box調(diào)用fn,這句話非常重要,我們知道this它始終指向一個對象,剛好box就是一個對象。那么fn里面的this就是box。

box.onclick = function(){
  function fn(){
    console.log(this);
  }
  fn.call(this);
};

這句話在某些情況下是可以簡寫的,比如:

box.onclick = function(){
  var fn = function(){
    console.log(this); //box
  }.call(this);
};

或者這樣:

box.onclick = function(){
  (function(){
    console.log(this);
  }.call(this)); //box
};

又或者這樣:

var objName = {name:'JS2016'};
var obj = {
  name:'0 _ 0',
  sayHello:function(){
    console.log(this.name);
  }.bind(objName)
};
obj.sayHello();//JS2016

call和apply、bind但是用來改變this的指向的,但也有一些小小的差別。下面我們來看看它們的差別在哪。

function fn(a,b,c,d){
  console.log(a,b,c,d);
}

//call
fn.call(null,1,2,3);

//apply
fn.apply(null,[1,2,3]);

//bind
var f = fn.bind(null,1,2,3);
f(4);

結(jié)果如下:

1 2 3 undefined
1 2 3 undefined
1 2 3 4

前面說過第一個參數(shù)傳的是一個你要借用的對象,但這么我們不需要,所有就傳了一個null,當(dāng)然你也可以傳其他的,反正在這里沒有用到,除了第一個參數(shù)后面的參數(shù)將作為實際參數(shù)傳入到函數(shù)中。

call就是挨個傳值,apply傳一個數(shù)組,bind也是挨個傳值,但和call和apply還有多少不同,使用call和apply會直接執(zhí)行這個函數(shù),而bind并不會而是將綁定好的this重新返回一個新函數(shù),什么時候調(diào)用由你自己決定。

var objName = {name:'JS2016'};
var obj = {
  name:'0 _ 0',
  sayHello:function(){
    console.log(this.name);
  }.bind(objName)
};
obj.sayHello();//JS2016

這里也就是為什么我要用bind的原因,如果用call的話就會報錯了。自己想想這個sayHello在obj都已經(jīng)執(zhí)行完了,就根本沒有sayHello這個函數(shù)了。

這幾個方法使用的好的話可以幫你解決不少問題比如:

正常情況下Math.max只能這樣用

Math.max(10,6)

但如果你想傳一個數(shù)組的話你可以用apply

var arr = [1,2,30,4,5];
console.log(Math.max.apply(null,arr));

又或者你想讓偽數(shù)組調(diào)用數(shù)組的方法

function fn(){
  [].push.call(arguments,3);
  console.log(arguments); //[1, 2, 3]
}
fn(1,2);

再者:

var arr = ['aaabc'];
console.log(''.indexOf.call(arr,'b')); //3

牛逼不,簡直偷梁換柱,當(dāng)然還有很多可以利用的,自己盡情花輝去吧。

簡單說一下這種偷梁換柱的原理吧,實際上瀏覽器內(nèi)部根本就不在乎你是誰,它只關(guān)心你傳給我的是不是我能夠運行的,如下:

正常情況

var str = 'aaabc';
console.log(str.indexOf('b'));

而這種情況其實做的事情和上面一模一樣,看我來拆解。

var arr = ['aaabc'];
''.indexOf.call(arr);

這句話就是說讓arr調(diào)用字符串的indexOf方法,前面說過了瀏覽器內(nèi)部不在乎你是誰,所以誰都可以來調(diào)用,但不是100%成功,具體看如下。

''.indexOf.call(arr,'b')

這里的arr就是['aaabc'],內(nèi)部很可能拆成了'aaabc',因此就成了下面的這段代碼。

'aaabc'.indexOf('b');

這就是它們的秘密。

這里得說一下bind在某些瀏覽器下不兼容。我們來模擬一個玩玩。

Function.prototype.$bind = function(obj){
    //保存當(dāng)前this
  var _this = this;
    //截取除了第一個以外的所有實際參數(shù)
  var a = [].slice.call(arguments,1);
    //返回一個新函數(shù)
  return function(){
    //讓當(dāng)前那個調(diào)用的函數(shù)的this指向obj,并且把實參傳給它,這里用了concat是因為,我們可能在綁定以后還傳遞參數(shù),所以才把他們合并起來。如f(4)這個是在綁定以后傳的參數(shù),a這個argument是綁定時的。
    _this.apply(obj,a.concat([].slice.call(arguments)));
  };
};

function fn(a,b,c,d){
  console.log(a,b,c,d);
}

var f = fn.$bind(null,1,2,3);
f(4);

這個方法和實際上的bind還是差別很大的,如

var arr = ['JSS'];

var index = ''.indexOf.$bind(arr,'S');
console.log(index())


function fff(){
  [].push.$bind(arguments,1);
  console.log(arguments);
}

fff();

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容