4
function f(a) { return a}

f(1) // => 1

f.call(null, 1) // => 1

Function.prototype.call(f, null, 1) // => undefined

Why the last line return undefined, I thought they are the same.

5
  • 1
    Function.prototype.call.call(f, null, 1) What you're doing right now is var fp = Function.prototype; fp.call(f, null, 1) and fp.call(...) ain't the same as f.call(...). These are simply different functions. Commented Jun 12, 2017 at 1:31
  • See stackoverflow.com/questions/31074664/… Commented Jun 12, 2017 at 1:33
  • thank you guys for the quick rely, this is a mind stretcher Commented Jun 12, 2017 at 1:55
  • @mko I'd appreciate if you checked my answer, I don't think the current answer, nor the one he linked to makes a good job of answering the question. That's just my opinion though. Commented Jun 12, 2017 at 3:04
  • @mko I updated my answer to give you a digestible explanation of the difference between Function.prototype.call and Function.prototype.call.call based on the specification of how Function.prototype.call is really supposed to work. Hope it helps... Commented Jun 12, 2017 at 18:30

2 Answers 2

2

These will be the same:

function f(a) { return a}

console.log(f(1)); // => 1

console.log(f.call(null, 1)); // => 1

console.log(Function.prototype.call.call(f, null, 1)); // => 1

Notice the additional .call in the last statement.

And here's the explanation:

Function.prototype.call

According to the spec, Function.prototype.call returns an abstract operation Call(func, thisArg, argList).

Therefore, f.call(null, 1) will return the abstract operation Call(f, null, 1) where f is the function being called, null is the context from which it is called, and 1 is the argument passed to f. This will give you the desired output.

Based on that, Function.prototype.call(f, null, 1) will result in the abstract operation Call(Function.prototype, f, null, 1) where Function.prototype is the function being called, f is the context, and null and 1 are the arguments passed to Function.prototype. Of course this will not work as intended.

Function.prototype.call.call

However, Function.prototype.call.call(f, null, 1) will return the abstract call operation Call(Function.prototype.call, f, null, 1), where Function.prototype.call is the function to be called, f is the context from which it is called, and null and 1 are passed as arguments. So what would that look like? Well, since f is the context and call is the function being invoked with (null,1), the end result is identical to: f.call(null, 1).

Sign up to request clarification or add additional context in comments.

1 Comment

It works I need some coffee to figure this out, thanks
2

Let's start with this:

function fn() { console.log(this); }
fn.a = function(){console.log(this)}
fn.a() // function fn() { console.log(this); }

So let's dig deeper and try to implement a fake call function:

  Function.prototype.fakeCall = function () {
    // console.log(this)
    // taking the arguments after the first one
    let arr = Array.prototype.slice.call(arguments, 1);
    // on the first run this will be Function.fakeCall but on the second pass it will be the first argument (the a function)
    this.apply(arguments[0], arr);
  }

  function a(ar){ console.log(ar + this.name) };
  let obj = {name : "thierry"};
 // a.fakeCall( obj, 'hi ')

Function.fakeCall.fakeCall(a, obj, 'hi ');

Thus when we do this: Function.prototype.fakeCall.fakeCall(a, obj, 'hi ')

what happens is, on the first run we have:

  1. arr = [ obj, 'hi ']
  2. this = Function.fakeCall

so we end up with Function.fakeCall.apply(a, [ obj, 'hi ']);

Then on the second run we have

  1. arr = ['hi']
  2. this = a

so we end up with a.apply(obj, ['hi']) which is the same as a.call(obj, 'hi');

If however we did Function.fakeCall(a, obj, 'hi '); On the first run we would have this = Function and that won't work. It will throw an error in this case, in your case it just returns undefined. That is easily implementable with a try-catch.

  Function.prototype.fakeCall = function () {
    let arr = Array.prototype.slice.call(arguments, 1);
    try{
       return this.apply(arguments[0], arr);
    }catch(e){}
  }

  function a(ar){ return ar + this.name };
  let obj = {name : "thierry"};
  console.log(Function.fakeCall(a, obj, 'hi ')); // undefined

  console.log(Function.fakeCall.fakeCall(a, obj, 'hi ')); // hi thierry

1 Comment

your style of explanation teach me more about the underlying mechanism. thank you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.