Call, apply 与bind

125 阅读2分钟

call、apply、bind的作用是改变函数运行时this的指向。

call

fun.call(thisArg[, arg1[, arg2[, ...]]])

call 方法第一个参数是要绑定给**this**的值,后面传入的是一个参数列表。当第一个参数为nullundefined的时候,默认指向window

使用:

var obj ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = obj.fn;    b.call(obj,1,2)       // 3

原理实现:分三步

  • 第一步:给传入对象(执行上下文)新增一个函数(函数的行为和要调用的函数行为一样)

  • 第二步:调用对象的新增的函数

  • 第三步:删除对象的这个新增函数;

    Function.prototype.call = function(obj) { obj.fn = this; obj.fn(); delete obj.fn; }

例:

function person(age) {
    console.log(age)
    console.log(this.name)
}
var obj = {
  name: "test",
  sex: "a"
}
Function.prototype.mycall = function (context) {
    console.log(this);
}
person.mycall(obj, 18) // 上面的this会输出 person函数对象

Function.prototype.mycall = function (context) {
  //context指向外部传入的执行上下文
  var context = context || windows;
  context.fn = this//this指原函数, 这里相当于给传入的执行上下文对象新增了一个函数。

  //将传入的参数存储起来
  var args = [];
  var len = arguments.length;
  for (var i = 1; i < len; i++) {
    args.push(arguments[i]);
  }
  //上面代码也可以换成
  //args = Array.prototype.slice.call(arguments, 1);

  var result = eval('context.fn(' + args.join(",") + ')'); //调用函数
  delete context.fn; //删除执行上下文中新增的函数,不能改变原有对象
  return result;
}

apply

apply接受两个参数,第一个参数是要绑定给this的值第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window

fun.apply(thisArg, [argsArray])
var obj ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = obj.fn;    b.apply(obj,[1,2])       // 3

自定义实现:

Function.prototype.myapply = function (thisArg, argsArray) {  //thisArg指向外部传入的执行上下文  var thisArg = thisArg || windows, result;

  thisArg.fn = this;//this指原函数, 这里相当于给传入的执行上下文对象新增了一个函数。
  if(!argsArray){
    result = thisArg.fn();
  } else {
      var newArgs = [];
      for(let i = 0; i < argsArray.length; i++){            newArgs.push('argsArray['+i+']');
      } 
      //上面代码也可以换成
      //newArgs = Array.prototype.slice.call(argsArray, 1);
  }
  result = eval('thisArg.fn(' + newArgs + ')'); //调用函数  delete thisArg.fn; //删除执行上下文中新增的函数,不能改变原有对象  return result;
}

bind

  和call很相似,第一个参数是**this的指向,从第二个参数开始是接收的参数列表**。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。

fun.bind(thisArg[, arg1[, arg2[, ...]]])

例:

 var obj ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }

    var b = obj.fn;    b.bind(obj,1,2)() // 3

js实现:

if (!Function.prototype.bind) { //低版本可能不存在bind函数
    Function.prototype.bind = function () {
        var self = this,                        // 保存原函数
            context = [].shift.call(arguments), // 保存需要绑定的this上下文
            args = [].slice.call(arguments);    // 剩余的参数转为数组
        return function () {                    // 返回一个新函数
            self.apply(context, [].concat.call(args, [].slice.call(arguments)));
        }
    }
}

区别

  • 返回值:call和apply返回的是函数调用的结果; bind返回的是函数的引用。
  • 参数列表:第一个参数都是上下文 this , call和bind第二个参数开始后接受的是参数列表;apply第二个参数是参数数组;