Last active
March 30, 2023 01:59
-
-
Save cowboy/4477847 to your computer and use it in GitHub Desktop.
JavaScript: call invo-cursion?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// OOP | |
console.log( 'OHAI'.blink() ); | |
// Call invocation | |
console.log( String.prototype.blink.call('OHAI') ); | |
// $ always makes things look awesome. | |
var $ = Function.prototype.call; | |
// Very explicit call invocation | |
console.log( $.call(String.prototype.blink, 'OHAI') ); | |
// Very, very explicit call invocation, ie. call invo-cursion? | |
console.log( $.call($,$,$,$,$,$,$,$,$,$,$,$, String.prototype.blink, 'OHAI') ); | |
// ^^^^^^^^^^^^^^^^^^^^^^^ "bonus" calls | |
// You can have fun with apply invocation and _ too. | |
var _ = Function.prototype.apply; | |
// Very, very explicit apply invocation, ie. apply invo-cursion. | |
console.log( _.apply(_,[_,[_,[_,[_,[_,[_,[_, [ String.prototype.blink, ['OHAI'] ]]]]]]]]) ); | |
// ^^^^^^^^^^^^^^^^^^^^^^ "bonus" applies, and fun w/brackets ^^^^^^^^ |
@jackocnr For the last FYI, I thought it would be like this: when you pass Number.call(or Function.call)
to Array.prototype.map, the Number.call(or Function.call)
is no longer an Object method, it is now just a simple function, with the call
missing this
then JavaScript runtime call an undefined
function and will throw an error.
Array.apply(null, {length: 5}).map(Number.call) // TypeError: undefined is not a function
For better understanding, here is another example:
var call = Function.call;
call() // TypeError: call is not a function
call.call(Number, undefined, 1) // 1
call.call(String, undefined, 'foo') // foo
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In case this helps anyone else: I was confused as to why you needed that last
Number
passed in as the 2nd arg tomap
. I know thatmap
invokes the given callback with 3 args:currentVal
,index
andarray
, and thatNumber
just takes 1 arg, so instead of directly usingNumber
as the callback (it would receive the first argcurrentVal
(undefined)), we useNumber.call
as the callback, which swallows the first arg as thethis
value and instead invokesNumber
with the second argindex
, which is what we want.But it turns out
call
doesn't just magically know which function it was called on, it just executes whichever function is in it'sthis
value (I believe). Normally that will indeed be the function it was called on (it is effectively an Object method - see the MDN article onthis
), but in this case, we passNumber.call
intomap
, which is then stored internally simply ascallback
, so it is no longer an Object method, it is now just a simple function, so (I assume) it'sthis
defaults to the global object, which it then tries to invoke as a function, which throws an error. So in this case theNumber
fromNumber.call
is lost and never actually gets called. That is why we have to pass inNumber
as the 2nd arg tomap
because it is then set as thethis
for thecallback
(which in this case isNumber.call
), and so based on our past assertions,call
will then invoke it.As mentioned earlier in this thread: an interesting consequence of all this is that it doesn't matter how you invoke
call
- it works just as well if you domap(Function.prototype.call, Number)
.Props to these articles for helping me understanding this: Some interesting aspects of “call” method in JavaScript and Partial Application in JavaScript.
Feedback welcome on these wild theories!
FYI if you remove the last
Number
argument, and instead runArray.apply(null, {length: 5}).map(Number.call)
then in Chrome you get the error:Uncaught TypeError: Array.apply(...).map is not a function
, which is confusing. Anyone understand this?