Cannot directly use .prototype.call function

function f(a) { return a} f(1) // => 1 f.call(null, 1) // => 1 Function.prototype.call(f, null, 1) // => undefined 

Why the last line returns undefined , I thought they were the same.

+4
javascript
source share
2 answers

They 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 

And here for an explanation:

Function.prototype.call

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

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

Based on this, Function.prototype.call(f, null, 1) will cause an abstract operation (Function.prototype, f, null, 1) to be called, where Function.prototype is the called function, f is the context, and null and 1 - arguments passed to Function.prototype function. Of course, this will not work as intended.

Function.prototype.call.call

However, Function.prototype.call.call(f, null, 1) will return an abstract call operation Call (Function.prototype.call, f, null, 1), where Function.prototype.call is the function that will be called, f - this is the context from which it is called , and null and 1 are passed as arguments.

So what would it look like?

Well, since f is a context, and a call is a function called with (null, 1), the end result is identical: f.call(null, 1) .

+2
source share

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 go 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, at the first start we have:

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

so in the end we get Function.fakeCall.apply(a, [ obj, 'hi ']);

Then on the second run we have

  • arr = ['hi']
  • this = a

as a result, we get a.apply(obj, ['hi']) , which coincides with a.call(obj, 'hi');

If we did Function.fakeCall(a, obj, 'hi '); In the first run, we would have this = Function , and that would not work. In this case, it throws an error, in your case it just returns undefined. This is easy to implement with 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 
+2
source share

All Articles