美文网首页
call,apply,bind

call,apply,bind

作者: 蛮吉大人123 | 来源:发表于2018-08-06 14:16 被阅读5次

这三个方法都是为了改变调用方法的 this 指向的,所以了解他们之前需要先聊一下 JavaScript 中的 this 关键字。

this

JavaScript 中的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

注:ES6 的箭头函数 this 关键字是在声明时就被绑定了,不存在改变的情况。

this 的指向

除去不常用的 witheval 的情况,this 的指向大致可以分为以下 4 种。

  • 作为对象的方法调用
  • 作为普通函数调用
  • 构造器调用
  • Function.prototype.callFunction.prototype.apply 调用

1. 对象的方法调用

当函数作为对象的方法调用是,this 指向该对象:

var obj = {
  name: 1,
  getName: function() {
     console.log(this === obj); // true
     console.log(this.a); // 1
  }
};
obj.getName();

2. 普通函数调用

当作为普通函数调用时,this 总是指向全局对象。在浏览器的 JavaScript 里,这个全局对象是 window 对象。

window.name = 'globalName';

var obj = {
  name: 'Zhang San',
  getName: function() {
    return this.name;
  }
};

var getName = obj.getName;
console.log(getName()); // globalName

正是由于 JavaScript 的这个特点使得有的时候我们所使用的方法指向了全局 window 对象。
比如在 div 节点的时间函数内部,有一个局部的 callback 方法, callback 方法被作为普通函数调用时,callback 内部的 this 指向了 window 而我们往往是想让它指向该 div 节点。

<html>
  <body>
    <div id="div1">我是一个 div</div>
  </body>
  <script>
    window.id = 'window';

    document.getElementById('div1').onclick = function() {
      alert(this.id); // div1
      var callback = function() {
        alert(this.id); // window
      }
      callback();
    };
  </script>
</html>

此问题有一种简单的解决方案就是在外部把this用变量保存下来:

document.getElementById('div1').onclick = function() {
  var _this = this;
  var callback = function() {
    alert(_this.id); // div1
  }
  callback();
};

3. 构造器调用

除了宿主提供的一些内置函数,大部分 JavaScript 函数都可以当做构造器使用。构造器的外表跟普通函数一模一样,区别只是被调用的方式。当用 new 运算符创建对象时,该函数总会返回一个对象,通常情况下,构造器中的 this 指向返回的这个对象:

var MyClass = function(name) {
  this.name = name;
  this.getName = function() {
    return this.name;
  }
}
var obj = new MyClass('Zhang San')
console.log(obj.getName()); // Zhang San

4. Function.prototype.callFunction.prototype.apply 调用

Function.prototype.callFunction.prototype.apply 可以动态的改变传输函数的 this

var obj1 = {
  name: 'Zhang San',
  getName: function() {
    return this.name;
  }
};

var obj2 = {
  name: 'Li Si'
};

console.log(obj1.getName()); // Zhang San
console.log(obj1.getName.call(obj2)); // Li Si

apply,call

callapply 是ECAMScript 3 给 Function 的原型定义的两个方法,在实际开发中,特别是在一些函数式风格的代码编写中,callapply 方法尤为有用。

区别

applycall 这两个方法的作用一模一样, 区别只是传入的参数不同。
apply 接收两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传递给被调用的函数:

var func = function(a, b, c) {
  alert([a, b, c]); // [1, 2, 3]
}
func.apply(null, [1, 2, 3]);

如果传入的第一个参数是 null,函数体内的 this 会指向默认的宿主对象,在浏览器中则是 window

call 可以说是 apply 的一个语法糖,如果我们明确地知道函数接受多少个参数,而且像一目了然的表达形参和实参的对应关系,那么也可以用 call 来传送参数。

bind

bind 是ECAMScript 5 中定义的方法,它只是改变了函数内部 this 的指向,但是并没有调用,我们可以通过简化版的bind的内部实现来理解 bind

Function.prototype.bind = function() {
  var self = this,
      context = [].shift.call(arguments),
      args = [].slice.call(arguments);
  return function() {
    return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
  }
};

用途

  1. 改变 this 指向

比如上述 callback的问题,也可以用 call / apply / bind 的方式解决

document.getElementById('div1').onclick = function() {
  var callback = function() {
    alert(this.id); // div1
  }
  callback.call(this);
};
  1. 借用其他对象的方法

杜鹃既不会筑巢,也不会孵雏,而是把自己的蛋寄托给云雀等其他鸟类,让他们代为孵化和养育。

在 JavaScript 中也存在类似的借用现象。
借用的第一种场景是 “借用构造函数”,通过这种,可以实现一种类似继承的效果:

var A = function(name) {
  this.name = name;
}

var B = function() {
  A.apply(this, arguments);
}

B.prototype.getName = function() {
  return this.name;
}

var b = new B('Zhang San');
console.log(b.getName()); // Zhang San

第二种场景跟我们的关系更加密切,就是可以让类数组对象调用数组对象的方法

// 向 arguments 里面添加一个新的元素
(function() {
  Array.prototype.push.call(arguments, 3);
  console.log(arguments); // [1, 2, 3]
})(1, 2)

// arguments 转化为真正的数组
(function() {
  const  args = [].slice.call(arguments);
  console.log(args); // [1, 2]
})(1, 2)

相关文章

网友评论

      本文标题:call,apply,bind

      本文链接:https://www.haomeiwen.com/subject/vtdheftx.html