call,apply,bind

2018-07-13  本文已影响0人  夜舞暗澜_3ea2

call是做什么用的?

JS中使用原型思想。如果一个obj原型链上没有特定的方法,那么JS就不知道如何调用它。比如一个类数组对象(ex. HTMLCollection)就没有好用的数组方法。
但是类数组对象与数组的结构几乎一致,为什么就不能使用数组的方法呢?
所以JS给我们提供了一种方法,能够告诉JS,我们指定了obj像Array一样运作,例如Array.prototype.map.call(obj)

查看示例:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"

Food是没有name和price属性的。但是为了继承Product的一系列属性,Food在构造过程中call了Product的构造函数。
看到了?这也是继承的一种方法。
其实当我们调用对象上的方法时,写成obj.func(...args),其内部实现也是func.call(obj, ...args)

语法:

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

call和apply的区别?

根据MDN,两者只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组

刚才是fun.call(thisArg, arg1, arg2, ...)
现在是fun.apply(thisArg, [arg1, arg2, ...])

能看出来吗?就差这么一点。

bind

这里涉及到this的指向问题。当我们调用一个函数时,函数中的this会指向当前调用函数的对象
bind()方法相当于创建一个新函数,不管调用这个函数的对象是谁,this都将会指向绑定的对象。
Function.prototype.bind()-MDN

语法:

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

还是看例子:

var module = {
  x: 42,
  getX: function() {
    return this;
  }
}

var unboundGetX = module.getX;
console.log(unboundGetX());
// > [object Window]

var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
/*
> Object { x: 42, getX: function() {
    return this;
  } }
*/

在不使用bind时,调用unboundGetX()方法的对象是window,所以this指向window。
当用bind绑定module时,this重新指向了module。

一些比较重要的补充:

1. bind()函数在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。如果不能运行,可以在脚本开头加入腻子:

if (!Function.prototype.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 fNOP === true时,说明返回的fBound被当做new的构造函数调用
          return fToBind.apply(this instanceof fNOP
                 ? this
                 : oThis,
                 // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 维护原型关系
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();

    return fBound;
  };
}

你不用看懂,只要把这段代码贴到开头就好了。

2. 匿名函数和箭头函数:

匿名函数的this通常会指向全局window,这很适合使用bind做出改变。
示例:

var module = {
  x: 42
}

var unboundGetX = function(){return this};
console.log(unboundGetX())
// > [object Window]

var boundGetX = function(){return this}.bind(module);
console.log(boundGetX())
// > Object { x: 42 }

但是!箭头函数会默认做一个bind(this)的操作,也就是箭头函数是不能使用bind的

var boundGetX = ()=>{return this}.bind(module);
console.log(boundGetX())
// Error: Unexpected token .
上一篇下一篇

猜你喜欢

热点阅读