Skip to content Skip to sidebar Skip to footer

Can't Use Function.prototype.call Directly

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 a

Solution 1:

These will be the same:

functionf(a) { return a}

console.log(f(1)); // => 1console.log(f.call(null, 1)); // => 1console.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).

Solution 2:

Let's start with this:

functionfn() { 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 onelet 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);
  }

  functiona(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{
       returnthis.apply(arguments[0], arr);
    }catch(e){}
  }

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

Post a Comment for "Can't Use Function.prototype.call Directly"