Why invoke “apply” instead of calling function dir

2019-03-18 17:17发布

问题:

When looking at the source code for raphael or g.raphael or other libraries I've noticed the developer does something like this:

var val = Math.max.apply(Math, data_array);

Why not just invoke the function directly, such as:

var val = Math.max(data_array);

Thanks.

回答1:

I think the explanation from the Mozilla Docs describes it well:

You can assign a different this object when calling an existing function. this refers to the current object, the calling object. With apply, you can write a method once and then inherit it in another object, without having to rewrite the method for the new object.

apply is very similar to call, except for the type of arguments it supports. You can use an arguments array instead of a named set of parameters. With apply, you can use an array literal, for example, fun.apply(this, [name, value]), or an Array object, for example, fun.apply(this, new Array(name, value)).

As for the parameters:

thisArg Determines the value of this inside fun. If thisArg is null or undefined, this will be the global object. Otherwise, this will be equal to Object(thisArg) (which is thisArg if thisArg is already an object, or a String, Boolean, or Number if thisArg is a primitive value of the corresponding type). Therefore, it is always true that typeof this == "object" when the function executes.

argsArray An argument array for the object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function.

The docs give a good example of a use case for apply. In the example below, apply is used to chain a constructor:

function product(name, value)
{
  this.name = name;
  if (value >= 1000)
    this.value = 999;
  else
    this.value = value;
}

function prod_dept(name, value, dept)
{
  this.dept = dept;
  product.apply(this, arguments);
}
prod_dept.prototype = new product();

// since 5 is less than 1000 value is set
var cheese = new prod_dept("feta", 5, "food");

// since 5000 is above 1000, value will be 999
var car = new prod_dept("honda", 5000, "auto");

Notice that in the prod_dept constructor, the this supplied refers to the prod_dept object, and arguments is an array of arguments passed to the product constructor.



回答2:

Math.max won't accept a list by default. "apply" allows you to unpack the list to arguments so that max works correctly.



回答3:

Why invoke “apply” instead of calling function directly? Let's have a snippet:

var obj = {a: "apply"};
func.call(obj, "parameter");
function func(para) {
    if(this.a === "apply"){
        console.log('apply'); 
     }
} 

Here we can see we are using 'this' (context) within a function, If we directly call a func than we don't have any context, than it will take window context, which is wrong. So, if you are using context within your function than use apply method.



回答4:

.apply is often used when the intention is to invoke a variadic function with a list of argument values, e.g.

The Math.max([value1[,value2, ...]]) function returns the largest of zero or more numbers.

Math.max(10, 20); // 20
Math.max(-10, -20); // -10
Math.max(-10, 20); // 20

The Math.max() method doesn't allow you to pass in an array. If you have a list of values of which you need to get the largest, you would normally call this function using Function.prototype.apply(), e.g.

Math.max.apply(null, [10, 20]); // 20
Math.max.apply(null, [-10, -20]); // -10
Math.max.apply(null, [-10, 20]); // 20

However, as of the ECMAScript 6 you can use the spread operator:

The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) are expected.

Math.max(...[10, 20]); // 20
Math.max(...[-10, -20]); // -10
Math.max(...[-10, 20]); // 20

When calling a function using the variadic operator, you can even add additional values, e.g.

Math.max(...[10, 20], 50); // 50
Math.max(...[-10, -20], 50); // 50


回答5:

Generally you would use apply() and call() to be able to set the context or the object to which the calling function belongs to. The function then effectively becomes a method of that object/context which is helpful if you'll need to access the fields of the context.

This is how Javascript natively invokes functions:

  1. Make an argument list (argList) out of parameters 1 through the end
  2. The first parameter is thisValue
  3. Invoke the function with this set to thisValue and the argList as its argument list

Reference: Understanding JavaScript Function Invocation and "this" by Yehuda Katz

So, when calling a function directly you are actually implicitly passing in a default context to the function. Javascript passes 'window' or 'undefined' as context when you don't specify it using call() or apply()

This answer also gives an example which might help in understanding the usage