1

I have checked many similar questions on stackoverflow, such as call.call 1 , call.call 2, but I am a newcomer, and I cannot make comment anywhere. I hope I can find a comprehensive and thorough explanation of how the JavaScript interpreter execute these functions, with opening a new question here.

Here are some examples:

function my(p) { console.log(p) }
Function.prototype.call.call(my, this, "Hello"); // output 'Hello'

The above example is a standard usage of Function.prototype.call, which is easier to understand. my understanding is: 'my' as an function object executes its inherited method 'Function.prototype.call' ie. my.(Function.prototype.call) => my.call(this, 'Hello')

Function.call.call(my, this, "Hello") // output 'Hello'

I am confused here, in comparison with the above example. I do not know how JavaScript interpreter works here. 'my' sees Function.call and Function.prototype.call as same method?

Function.prototype.call(my, this, 'Hello2') // output nothing
Function.call(my, 'Hello2') // output nothing

I cannot explain why this statement does not throw an error? In fact, I do not know how Function.prototype.call as a method works.

Function.prototype.call.call.call(my, this, "Hello3"); // output 'Hello3'

I cannot explain how the JavaScript interpreter interpretes the above statement? interpreting 'call' from right to left? then what does my.(Function.prototype.call.call) mean?

Function.prototype.call.call.call.call(my, this, "Hello4"); // output 'Hello4'

why I can put any number of '.call' here, and the output is the same? Does not each call consumes an argument as 'this' object, which should imply only three arguments are not enough? a similar question as the immediately above one

Then more examples:

var $ = Function.prototype.call
$(my, this, 'Hello5') // Exception: TypeError: Function.prototype.call called on incompatible Proxy

why it does not output nothing, just as one above example?

var v = Function.prototype.call.call 
v(my, this, 'Hello6') // Exception: TypeError: Function.prototype.call called on incompatible Proxy

Does it mean, when use variable v, then JavaScript interpreter tries to interpret v isolatedly, without seeing there are arguments following? Then the interpreter thinks the 'this' is global variable 'window'? I do not know how the interpreter works differently between v() and Function.prototype.call.call()

Can anyone help, please? Thanks!

Community
  • 1
  • 1
nlite
  • 53
  • 7
  • `Function.prototype.call.call === Function.prototype.call // true` `Function.call === Function.prototype.call.call // true` – zerkms Apr 15 '17 at 09:19
  • Thanks. Yes, they are strictly equal, but can you please explain why their outputs are different when they are followed by the same arguments in parentheses? – nlite Apr 17 '17 at 06:58
  • Haven't I done it in my answer? – zerkms Apr 17 '17 at 07:06

3 Answers3

1

Function.prototype.call.call(my, this, "Hello"); means:

Use my as this argument (the function context) for the function that was called. In this case Function.prototype.call was called.

So, Function.prototype.call would be called with my as its context. Which basically means - it would be the function to be invoked.

It would be called with the following arguments: (this, "Hello"), where this is the context to be set inside the function to be called (in this case it's my), and the only argument to be passed is "Hello" string.

As soon as your my function does not use this context in any way - you can pass anything:

Function.prototype.call.call(my, 123, 'Hello2') // outputs "Hello2"

As soon as Function.prototype.call.call refers to the same function as Function.prototype.call.call.call (and Function.call and my.call) and so on, you can add as many .call property accesses as you want and it would not change anything.

As of your second question:

var $ = Function.prototype.call
$(my, this, 'Hello5')

does not work, since the context for the function call is set dynamically during call (unless it's bound with .bind() or is an ES2015 arrow function).

So when you invoke it as $ the context is not set explicitly (since it's a variable, not an object), so by default it's set to undefined or the global object (depending on how you run your code).

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • When I invoke a function variable, why JavaScript interpreter does not translate $ into Function.prototype.call.call, and then see $ is followed by an object within parentheses? It can find the 'this' object this way. So I still do not know how the function is executed here. – nlite Apr 17 '17 at 07:08
  • It does not, because that's how the standard tells the interpreter to work: the context is determined during a function call. And `$(...)` does not carry any context. – zerkms Apr 17 '17 at 07:25
  • Should I refer to the ECMA-262? If so, which section, please? I am not quite clear of the conception of context. "when you invoke it as $ the context is not set explicitly", you mean, the _**this**_ is not set explicitly? – nlite Apr 17 '17 at 13:04
  • @nlite that's correct. It is `this` that is not set in that case. http://www.ecma-international.org/ecma-262/6.0/#sec-function-calls-runtime-semantics-evaluation see the item 5 – zerkms Apr 17 '17 at 20:03
0

Every object is linked to a prototype object. When trying to retrieve a property that does not exist on an object, its prototype object will be looked up. Read more.

this is a special variable available in JavaScript functions. Its value depends on how you call the function, not how you define it (with some exceptions). Read more.


As you can see, call is defined on Function.prototype which means all function objects will have access to it. It is a special function in that it allows you to explicitly set the value of this when invoking a function.

One common usage is calling array functions on array-like objects, which don't inherit from Array.prototype:

function f(){
    const args = Array.prototype.slice.call( arguments );
    // `args` is now a real array with all the privileged functions.
}

To get back to your examples,

function my(p) { console.log(p) }
Function.prototype.call.call(my, this, "Hello"); // output 'Hello'

Here you are calling call on Function.prototype.call.

It is the equivalent of

function my(p) { console.log(p) }
my.call(this, "Hello"); // output 'Hello'

Since call is defined on Function.prototype, and it is itself a function, when you call call twice, you are retrieving the same function. That's why you can chain them indefinitely.

function my(p) { console.log(p) }
const call1 = Function.prototype.call;
const call2 = Function.prototype.call.call;    // Same as `call1.call`.
const call3 = my.call;

console.log( call1 === call2 );
console.log( call1 === call3 );

Now to the second part of the problem. How this works inside functions. Read more.

The thing to remember is that its value is determined dynamically, based on how you call the function, not how you define it (with some exceptions).

When you do this, the call function's this value will be whatever it is in the current scope (when in global scope, it is undefined in strict mode and window in loose˙).

var v = Function.prototype.call.call 
v(my, this, 'Hello6') // Exception: TypeError: Function.prototype.call called on incompatible Proxy

That's why you're getting the error, this is not a function, as call is expecting it to be. When you call a function from an object, this will refer to that object, and that's what happens when you call call on some function (functions are objects too).

pishpish
  • 2,574
  • 15
  • 22
0

I struggled with this question because mentally I had a hard time making the leap from 'this' being a parameter that is then treated as sort of a pseudo-object from which the call method is called from. I summarized my understanding (in Nodejs):

function f(a) {console.log(a);}

f.call(global, 1);
//function reference to be called = f
//context to call it from = global
//parameters used when invoking = 1
//equivalent call => global.f(1)

Function.prototype.call.call(f, global, 1);
//function reference to be called = Function.prototype.call
//context to call it from = f
//parameters used when invoking = global, 1
//equivalent call => f.call(global, 1)

References used:

A shorthand for Function.prototype.call.call?

Javascript Function.prototype.call()

Can't use Function.prototype.call directly

a is a function, then what `a.call.call` really do?

sok0
  • 1