-
-
Save cowboy/4477847 to your computer and use it in GitHub Desktop.
// 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 ^^^^^^^^ |
That enables a very easy way to create number lists:
$.call(_, Array, 0, new Array(5)).map($, parseInt)
// 0, 1, 2, 3, 4
@ricardobeat I had no idea that Array.apply(null, new Array(5))
would return a non-sparse array. Very cool!
Ideas for to be speedings. Thanking you.
Also @ricardobeat, I'd probably do something more like this (just in general) to create a number list:
Array.apply(null, {length: 5}).map(Number.call, Number) // [0, 1, 2, 3, 4]
In the vein of .map(Number.call, Number)
this function will swallow n
leading function arguments:
function swallow(n, fn) {
return n ? swallow(n - 1, fn.call.bind(fn)) : fn;
}
function log(a, b, c) {
console.log([a, b, c]);
}
log(6, 7, 8) // [ 6, 7, 8 ]
swallow(1, log)(6, 7, 8) // [ 7, 8, undefined ]
swallow(2, log)(6, 7, 8) // [ 8, undefined, undefined ]
swallow(3, log)(6, 7, 8) // [ undefined, undefined, undefined ]
In that way of creating number lists it can be misleading to write Number.call
, I would suggest to point to a more generic location like the Function constructor:
Array.apply(null, {length: 5}).map(Function.call, Number) // [0, 1, 2, 3, 4]
So its more explicit that you can use any other function:
Array.apply(null, {length: 5}).map(Function.call, Math.exp) // [1, 2.71, 7.38, 20.08, 54.59]
But if you need the numbers as strings and performance is a priority you are better off using Object.keys
instead of the String constructor:
keys(Array.apply(null, {length: 5})) // ["0", "1", "2", "3", "4"]
@altIvan while using a more generically-located Function#call isn't a bad idea, Function.call
is no more generic than Number.call
as both Function
and Number
are constructor functions. What you meant to suggest was Function.prototype.call
which is the prototype object on which Function#call is actually defined.
Array.apply(null, {length: 5}).map(Function.prototype.call, Number) // [0, 1, 2, 3, 4]
That being said, it's a moot point, as Number.call
Function.call
and Function.prototype.call
are all references to the same function.
Also:
// Indices as strings.
Array.apply(null, {length: 5}).map(String.call, String) // ["0", "1", "2", "3", "4"]
// Number list.
Object.keys(Array.apply(null, {length: 5})).map(Number) // [0, 1, 2, 3, 4]
on very old/obscure versions of webkit
Array.apply(null, {length: N})
may not work, returns something like
[object Object] is not a valid argument for Function.prototype.apply
However, on the old/obscure version I'm testing on (id's itself as Safari 5.x but it isn't),
Array.apply(null, Array(n))
does work.
----EDIT-----
{length: N} also seems to fail in PhantomJS, whereas Array(N) doesn't
Hey, cool stuff, but can someone please explain me how's new Array(5) is equivalent to {length: 5}, and where's {length: 5} coming from in terms of Array constructor parameters if understand this right?
Regarding Array(n)
:
When Array is called as a function rather than as a constructor,
it creates and initialises a new Array
taken with courtesy from Annotated ECMAScript 5.1
@vsternbach {length: 5} is an array-like for apply function
Syntax
fun.apply(thisArg, [argsArray])
Parameters
thisArg
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray
An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
In case this helps anyone else: I was confused as to why you needed that last Number
passed in as the 2nd arg to map
. I know that map
invokes the given callback with 3 args: currentVal
, index
and array
, and that Number
just takes 1 arg, so instead of directly using Number
as the callback (it would receive the first arg currentVal
(undefined)), we use Number.call
as the callback, which swallows the first arg as the this
value and instead invokes Number
with the second arg index
, 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's this
value (I believe). Normally that will indeed be the function it was called on (it is effectively an Object method - see the MDN article on this
), but in this case, we pass Number.call
into map
, which is then stored internally simply as callback
, so it is no longer an Object method, it is now just a simple function, so (I assume) it's this
defaults to the global object, which it then tries to invoke as a function, which throws an error. So in this case the Number
from Number.call
is lost and never actually gets called. That is why we have to pass in Number
as the 2nd arg to map
because it is then set as the this
for the callback
(which in this case is Number.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 do map(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 run Array.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?
@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
http://twitter.com/cowboy/status/288373323690303489
http://twitter.com/cowboy/status/288374333200556034
Also
http://benalman.com/news/2012/09/partial-application-in-javascript/#extra-extra-credit