In Javascript is there equivalent to .apply that d

2020-05-25 04:02发布

问题:

Seems easy enough, i want to call a function with array of arguments. Sure, i can say func.apply(this, ['some', 'arguments']); but that will change the value of this inside func. Any idea how to do this without changing it?

回答1:

You cannot, because of the way this works in JavaScript. Read on:

Going by the "Entering An Execution Context" section of the ECMAScript spec: when you call a function, the value of this is determined by what's to it's left (called the activation object) . Let's create a function called steve, and put him in an object:

function steve(){}
var obj = { method: steve };

…when we call steve as obj.method(), his this is obj, because obj was the activation object.

The tricky case is when nothing is to the left of a function call:

steve(); // Who am I ?!

There's nothing to the left of the function call — effectively, it's null — so the value of this is set to a default value of the global object (window in web browsers, global in Node.js, etc.).

So you are, in fact, setting a function's this every time you call it.

P.S. calling steve.apply(null, []) is equivalent to calling steve().



回答2:

Note that in ES6 you can also write:

func(...someArray)

Here this becomes Window inside func, which is of no use. But if you write:

obj.func(...someArray)

this in inside func will be obj.

This may be the feature i was looking for 5 years ago, but that was 5 years ago, so who remembers :)



回答3:

The value of this is no mystery when calling a function, depending on how it is called "normally" (without call or apply):

func(); // `this` is the global object, or undefined in ES5 strict mode
obj.func(); // `this` is `obj`

So to avoid "changing" the value of this, just pass in the correct value to apply, depending on how you would call it "normally":

func.apply(undefined, []); // global object, or undefined in ES5 strict mode
obj.func.apply(obj, []);


回答4:

If you are calling a function alone, pass null.

func.apply(null, ['some', 'arguments']);

If you are calling a function that is a method of an object, pass that object.

var arr = [];
arr.push.apply(arr, ['some', 'arguments']);

In order to match a how this works.



回答5:

What you want doesn't make much sense.

In the ecmascript standard this is defined this way:

11.1.1 The this Keyword The this keyword evaluates to the value of the ThisBinding of the current execution context.

The thisBinding resolution for a functions execution context is specified this way:

10.4.3 Entering Function Code The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList:

  1. If the function code is strict code, set the ThisBinding to thisArg.
  2. Else if thisArg is null or undefined, set the ThisBinding to the global object.
  3. Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
  4. Else set the ThisBinding to thisArg.
  5. Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the argument.
  6. Set the LexicalEnvironment to localEnv.
  7. Set the VariableEnvironment to localEnv.
  8. Let code be the value of F’s [[Code]] internal property.
  9. Perform Declaration Binding Instantiation using the function code code and argumentList as described in 10.5.

In other words: either you specify it or it is automatically set to the global object. It will never inherit the this binding from it's parent scope, so the only way to give the thisBindiong of the parent scope to it's children is either by

  1. save it in a local variable in the parent scope like var self = this
  2. inject it with call/apply like the code you gave in your question.