为什么我们需要Function.prototype.call,我们可以通过在javascript中将函数附加到对象来实现相同的功能

时间:2018-02-06 19:00:43

标签: javascript jquery ecmascript-6

我看过这些调用,应用和绑定的例子



var fruit = { name: 'Strawberry' }

function showDetails(size, price) {
  console.log(this.name + ' ' + size + ': $' + price + '/lb')
}

showDetails.apply(fruit, ['small', 10])
// Strawberry small: $10/lb

showDetails.call(fruit, 'small', 10)
// Strawberry small: $10/lb

var bound = showDetails.bind(fruit, 'small', 10)
bound()
//Strawberry small: $10/lb

showDetails.bind(fruit, 'small', 10)()
// Strawberry small: $10/lb




这里所有函数都做同样的事情并检查下面的代码,不使用任何这些(调用,应用和绑定)



var fruit = { name: 'Strawberry' };
function showDetails(size, price) {
  console.log(this.name + ' ' + size + ': $' + price + '/lb')
}
fruit.show =showDetails;

fruit.show(small,10); //Strawberry small: $10/lb




它的工作方式和预期的一样,为什么,我们什么时候需要调用,应用和绑定。

如何在ES6中使用这些功能?

3 个答案:

答案 0 :(得分:2)

  • 我们需要(想要)callapply因为它们比替代方案简单得多(显然取决于用例)
  • 我们需要callapply,因为替代方法不适用于我们无法分配属性的对象(冻结或密封)
  • 当在评估函数期间该方法不应该是对象的属性时,我们需要callapply

我们还需要apply,因为call无法使用动态数量的参数(忽略新的扩展语法),请参阅What is the difference between call and apply?bind是一个完全不同的野兽,请参阅Javascript call() & apply() vs bind()?

答案 1 :(得分:2)

实际上,在许多情况下,在对象上定义方法将是可行的替代方案。但是,有时它不是一个选项:

1。该对象不允许定义方法:



"use strict";
function showDetails() { console.log(this.name); }
var obj = Object.seal({ name: 'Strawberry' });
showDetails.call(obj); // Strawberry
obj.showDetails = showDetails; // fails




2。对象已具有该名称

的属性



function name() { console.log(this.name) }
var obj = Object.seal({ name: 'Strawberry' });
name.call(obj); // Strawberry
obj.name = name;
obj.name(); // does not show 'Strawberry', but the function




如果所述属性为只读(使用definePropertywritable: false),它也可能失败。

3。添加属性会影响返回值

假设该功能执行自检,例如它计算它拥有的属性数量:



function memberCount() { return Object.keys(this).length; };
var obj = { name: 'Strawberry' };
console.log(memberCount.call(obj)); // 1
obj.count = memberCount;
console.log(obj.count()); // 2




4。添加属性会影响剩余代码

对于您期望某种行为的对象(如数组:

),这尤其是一个问题



function first() { return this[0]; };
var arr = ['Strawberry', 'Banana'];
console.log(first.call(arr)); // Strawberry
arr.first = first;
// ...
for (var i in arr) { // Iterate through array
    if (arr.hasOwnProperty(i)) {
        console.log(arr[i]); // Oops, the function appears also.
    }
}




applybind

的其他示例

还有其他一些例子,如果目标对象已经 方法,则使用applybind非常有用:

apply可以与需要单独参数的方法一起使用,而您只需要一组值。在ES6中,您可以使用扩展语法来克服这个问题,但传统上,apply就是解决此问题的方法。

例如,要获取数组中的最小值:



var arr = [5,3,1,6,2];
console.log(Math.min.apply(Math, arr));




bind方法通常用于将方法作为回调传递,但您需要将其绑定到对象:



var obj = {
    flavour: 'Strawberry',
    smell: function () { console.log(this.flavour) }
};

window.addEventListener('load', obj.smell); // ......> undefined
window.addEventListener('load', obj.smell.bind(obj)); // .....>Strawberry




ES6注意事项

必须绑定this,仍然可以使用这些方法。但是使用箭头函数语法,this保留其词法值,并将this参数传递给applycallbind这样的函数无效。通常它也变得不必要,因为词汇this通常正是所需要的。

以获取数组的最小值为例。使用ES6,可以写成如下:



const arr = [5,3,1,6,2,4];
console.log(Math.min(...arr));




或者举例说明一个方法(使用this)作为回调传递:



class Cls {
    constructor() { 
        this.flavour = 'Strawberry'
        this.smell = () => console.log(this.flavour)
    }
};

window.addEventListener('load', (new Cls).smell); // ......> Strawberry




答案 2 :(得分:0)

  

我们可以通过在javascript中将对象附加到对象来实现相同的功能

根据你的例子你是对的。但是在javascript中我们需要将函数作为回调函数传递给browser-event-loop-queue来执行异步。在这种情况下,对象将丢失。请查看以下示例

var fruit = { name: 'Strawberry' };
function showDetails(size, price) {
  console.log(this.name + ' ' + size + ': $' + price + '/lb')
}
fruit.show =showDetails;

setTimeout(fruit.show, 100);

这里的输出是未定义的,所以要附加参数和上下文对象,我们需要“bind”

现在我们可以使用

进行上述工作了

setTimeout(fruit.show.bind(fruit, size, price), 100);

用于apply / call是通过动态提供其上下文和参数来执行函数。检查以下呼叫的使用情况。

var Array.protoype.forEach = function(cb /*, thisArg*/){
  var T;
  if (arguments.length > 1) {
      T = arguments[1];
    }
  for(var i =0; i<this.length; i++){
    cb.call(T, this[i], i, this);
  }
}