写在前面

call、apply 和 bind 都是JS中Function对象原型中的公有方法。要想理解这三个函数的意思和用法,需要先了解JS中 this 指针的真正含义,因为这三个函数都是用来指定 this 指向的对象的。

这三个方法是所有函数对象公有的方法,因此只能被函数对象调用,简单来说就是call、apply和bind 点前面的必须是函数,即 function.call、function.apply、function.bind

该方法大致可以分为两类:

函数调用方式

fuc()、fuc.call() 和 fuc.apply(),这三种方法的意思都是在执行 fuc 函数,只不过后两种函数执行方式是加了条件的,自定义指定 this 的。call() 和 apply() 的返回值是,调用有指定this值和参数的函数的结果,若该方法没有返回值,则返回 undefined

函数创建函数

fuc.bind(),该方法是用于在 fuc 函数的基础上返回一个绑定 this 对象的新函数,即其返回值是返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

1. call 的用法

1.1 语法

完整版参考资料 mdn call

  1. function.call(thisArg, arg1, arg2, ...)

call 方法接受n个参数,这 n 个参数是要传入调用 call 的该函数的参数列表。其中第一个参数是指定调用 call 的该函数里的this对象,后面(n-1)个参数是该函数的其他参数。

1.2 非严格模式

  1. function f(a,b){
  2. console.log(this);
  3. console.log(a);
  4. console.log(b);
  5. }
  6. f(2,5);//输出为 window 2 5
  7. //因为此时未自定义指定this对象,则this值是默认传入的调用该函数的对象,即window.f(2,5),即this是window。
  8. f.call(null,2,5);//输出为 window 2 5
  9. f.call(undefined,2,5);//输出为 window 2 5
  10. f.call(10,2,5);//输出为 Number {10} 2 5
  11. f.call('test',2,5);//输出为 String {"test"} 2 5

以上代码可以看出,使用 call 方法调用函数时,指定 this 值为 null 或 undefined,都会默认指向全局window对象。而指定 this 为其他数据类型的值则会变成对应的对象传入,如 数字10 变成Number {10},即Number对象10,字符串”test”变成String {“test”},即字符串对象

1.3 严格模式

那么有没有一种方法是让 call 指定什么值 this 就是什么值,不要默认的window 或者包装成对应的对象。其实是有的,上面那种 call 调用方式是非严格模式的 call 调用。可以设置为严格模式,则只需要加入一句’use strict’; 即可严格自定义指定 this ,如下所示;

  1. function f(a,b){
  2. 'use strict';//加上这一句,意思是使用严格模式
  3. console.log(this);
  4. console.log(a);
  5. console.log(b);
  6. }
  7. f.call(null,2,5);//输出为 null 2 5
  8. f.call(undefined,2,5);//输出为 undefined 2 5
  9. f.call(10,2,5);//输出为 10 2 5
  10. f.call('test',2,5);//输出为 test 2 5

1.4 call 的特殊用途—继承

在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承

  1. function Product(name, price) {
  2. this.name = name;
  3. this.price = price;
  4. }
  5. function Food(name, price) {
  6. Product.call(this, name, price);
  7. this.category = 'food';
  8. }
  9. function Toy(name, price) {
  10. Product.call(this, name, price);
  11. this.category = 'toy';
  12. }
  13. var cheese = new Food('feta', 5);
  14. var fun = new Toy('robot', 40);
  15. console.log(cheese.name);//输出 feta
  16. console.log(fun.name);//输出 robot

2. apply 的用法

2.1 语法

完整版参考资料 mdn apply

  1. function.apply(thisArg, [argsArray])

apply 的用法和 call 的用法极为相似,表示的含义也是类似的,也是用于自定义指定函数里的 this ,唯一不同的是,参数类型不同,apply 接受两个参数,一个是指定的 this 对象, 另一个是函数里其余的参数组成的数组。

2.2 非严格模式和严格模式

和 call 的两个模式是一样的,其实可以说是函数具有的两种特性,因为严格模式语句是一般放在函数里规定该函数的调用时什么模式的,因此以下的 bind 方式调用也是这样区分的。

2.3 apply 的特殊用途—push

根据 apply 的特征,常常用来将数组添加到另一个数组时巧用,示例如下:

  1. var array = ['a', 'b'];
  2. var elements = [0, 1, 2];
  3. array.push(elements);
  4. console.log(array); // ["a", "b", [0, 1, 2]]
  5. //使用apply得到想要的样子
  6. var array = ['a', 'b'];
  7. var elements = [0, 1, 2];
  8. array.push.apply(array, elements);
  9. console.log(array); // ["a", "b", 0, 1, 2]

3. bind 的用法

3.1 语法

完整版参考资料 mdn bind

  1. let newFunction = function.bind(thisArg, arg1, arg2, ...)

bind()方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

3.2 bind 的用途

bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。

JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:

  1. this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
  2. var module = {
  3. x: 81,
  4. getX: function() { return this.x; }
  5. };
  6. module.getX(); // 81
  7. var retrieveX = module.getX;
  8. retrieveX();
  9. // 返回 9 - 因为函数是在全局作用域中调用的
  10. // 创建一个新函数,把 'this' 绑定到 module 对象
  11. // 新手可能会将全局变量 x 与 module 的属性 x 混淆
  12. var boundGetX = retrieveX.bind(module);
  13. boundGetX(); // 81

PS:bind 暂时没有真正用到过,不是特别理解其用途意义,以上实例来源于mdn

4. 总结

call、apply 和 bind 都是函数对象拥有的方法,call()的使用方法是接受n个参数,其中第一个参数是指定调用call()方法的函数里的 this 值,剩余的 (n-1)个参数是要传入调用call()方法的函数的参数。apply()的使用方法是接受两个参数,一个是指定调用apply()方法的函数里的 this 值,另一个是要传入调用apply()方法的函数的参数组成的数组。bind()是在当前函数基础上创建并返回一个新的函数,bind()接受n个参数,第一个参数是指定返回的新函数里的this值,后(n-1)个参数是要传入返回的新函数里的参数。