彻底弄懂bind,apply,call三者的区别 (转)

  • 彻底弄懂bind,apply,call三者的区别 (转)已关闭评论
  • 161 次浏览
  • A+
所属分类:Web前端
摘要

我们知道bind,call,apply的作用都是用来改变this指向的,那为什么要改变this指向呢?请看下面的例子:


为什么要改变this指向?

我们知道bind,call,apply的作用都是用来改变this指向的,那为什么要改变this指向呢?请看下面的例子:

var name="lucy"; let obj={  name:"martin",  say:function () {  console.log(this.name);  } }; obj.say(); //martin,this指向obj对象 setTimeout(obj.say,0); //lucy,this指向window对象

可以观察到,正常情况下 say 方法中的 this 是指向调用它的 obj 对象的,而定时器 setTimeout 中的 say 方法中的 this 是指向window对象的(在浏览器中),这是因为 say 方法在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的,但我们需要的是 say 方法中 this 指向obj对象,因此我们需要修改 this 的指向。

apply方法

apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。

日常用法:改变this指向

示例:

回调函数绑定this指向:

var name="martin"; var obj={  name:"lucy",  say:function(year,place){  console.log(this.name+" is "+year+" born from "+place);  } }; var say=obj.say; setTimeout(function(){  say.apply(obj,["1996","China"]) } ,0); //lucy is 1996 born from China,this改变指向了obj say("1996","China") //martin is 1996 born from China,this指向window,说明apply只是临时改变一次this指向

 

小技巧:改变参数传入方式

示例:

求数组中的最大值:

var arr=[1,10,5,8,3]; console.log(Math.max.apply(null, arr)); //10 

  

其中Math.max函数的参数是以参数列表,如:Math.max(1,10,5,8,3)的形式传入的,因此我们没法直接把数组当做参数,但是apply方法可以将数组参数转换成列表参数传入,从而直接求数组的最大值。

call方法

call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。

示例:

var arr=[1,10,5,8,3]; console.log(Math.max.call(null,arr[0],arr[1],arr[2],arr[3],arr[4])); //10 

  

采纳以参数列表的形式传入,而apply以参数数组的形式传入。

bind方法

bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。

示例:

var arr=[1,10,5,8,12]; var max=Math.max.bind(null,arr[0],arr[1],arr[2],arr[3]) console.log(max(arr[4])); //12,分两次传参 

  

可以看出,bind方法可以分多次传参,最后函数运行时会把所有参数连接起来一起放入函数运行。

实现bind方法(面试题):

简易版

Function.prototype.bind=function () {  var _this=this;  var context=arguments[0];  var arg=[].slice.call(arguments,1);  return function(){  arg=[].concat.apply(arg,arguments);  _this.apply(context,arg);  }  }; 

  

完美版

//实现bind方法  Function.prototype.bind = function(oThis) {  if (typeof this !== 'function') {  // closest thing possible to the ECMAScript 5  // internal IsCallable function  throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');  }  var aArgs = Array.prototype.slice.call(arguments, 1),  fToBind = this,  fNOP = function() {},  fBound = function() {  // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用  return fToBind.apply(this instanceof fBound  ? this  : oThis,  // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的  aArgs.concat(Array.prototype.slice.call(arguments)));  };  // 维护原型关系  if (this.prototype) {  // 当执行Function.prototype.bind()时, this为Function.prototype   // this.prototype(即Function.prototype.prototype)为undefined  fNOP.prototype = this.prototype;   }  // 下行的代码使fBound.prototype是fNOP的实例,因此  // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例  fBound.prototype = new fNOP();  return fBound;  };  var arr=[1,11,5,8,12];  var max=Math.max.bind(null,arr[0],arr[1],arr[2],arr[3]);  console.log(max(arr[4])); //12 

  

apply,call,bind三者的区别

  • 三者都可以改变函数的this对象指向。
  • 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
  • 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
  • bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行

 

 

ref: https://zhuanlan.zhihu.com/p/82340026