(() => console.log(arguments))(1,2,3);
// Chrome, FF, Node give "1,2,3"
// Babel gives "arguments is not defined" from parent scope
According to Babel (and from what I can tell initial TC39 recommendations), that is "invalid" as arrow functions should be using their parent scope for arguments. The only info I've been able to find that contradicts this is a single comment saying this was rejected by TC39, but I can't find anything to back this up.
Just looking for official docs here.
Chrome, FF, and node seem to be wrong here, Babel is correct:
Arrow functions do not have an own arguments binding in their scope; no arguments object is created when calling them.
looking for official docs here
Arrow function expressions evaluate to functions that have their [[ThisMode]] set to lexical, and when such are called the declaration instantiation does not create an arguments object. There is even a specifc note (18 a) stating that "Arrow functions never have an arguments objects.".
As noted by Bergi, arrow functions do not have their own arguments variable.
However, if you do want to capture the args for your arrow function, you can simply use a rest parameter
const myFunc = (...args) =>
console.log ("arguments", args)
myFunc (1, 2, 3)
// arguments [1, 2, 3]
Rest parameters can be combined with other positional parameters, but must always be included as the last parameter
const myFunc = (a, b, c, ...rest) =>
console.log (a, b, c, rest)
myFunc (1, 2, 3, 4, 5, 6, 7)
// 1 2 3 [ 4, 5, 6, 7 ]
If you make the mistake of writing a rest parameter in any other position, you will get an Error
const myFunc = (...rest, a, b, c) =>
console.log (a, b, c, rest)
myFunc (1, 2, 3, 4, 5, 6, 7)
// Error: Rest parameter must be last formal parameter
Related
(() => console.log(arguments))(1,2,3);
// Chrome, FF, Node give "1,2,3"
// Babel gives "arguments is not defined" from parent scope
According to Babel (and from what I can tell initial TC39 recommendations), that is "invalid" as arrow functions should be using their parent scope for arguments. The only info I've been able to find that contradicts this is a single comment saying this was rejected by TC39, but I can't find anything to back this up.
Just looking for official docs here.
Chrome, FF, and node seem to be wrong here, Babel is correct:
Arrow functions do not have an own arguments binding in their scope; no arguments object is created when calling them.
looking for official docs here
Arrow function expressions evaluate to functions that have their [[ThisMode]] set to lexical, and when such are called the declaration instantiation does not create an arguments object. There is even a specifc note (18 a) stating that "Arrow functions never have an arguments objects.".
As noted by Bergi, arrow functions do not have their own arguments variable.
However, if you do want to capture the args for your arrow function, you can simply use a rest parameter
const myFunc = (...args) =>
console.log ("arguments", args)
myFunc (1, 2, 3)
// arguments [1, 2, 3]
Rest parameters can be combined with other positional parameters, but must always be included as the last parameter
const myFunc = (a, b, c, ...rest) =>
console.log (a, b, c, rest)
myFunc (1, 2, 3, 4, 5, 6, 7)
// 1 2 3 [ 4, 5, 6, 7 ]
If you make the mistake of writing a rest parameter in any other position, you will get an Error
const myFunc = (...rest, a, b, c) =>
console.log (a, b, c, rest)
myFunc (1, 2, 3, 4, 5, 6, 7)
// Error: Rest parameter must be last formal parameter
I know how call and apply methods works, but coming to this question, it's some what tricky for me to understand the flow. Can someone help me to understand this.
console.log.call.call.call.call.call.apply(a => a, [1, 2]);
First off, note that in JavaScript, functions are objects, and can have their own properties (which you can access with the . notation just like any other object). Of the properties that exist on all functions are .apply and .call, which are themselves functions.
Second, both .call and .apply provide a mechanism to invoke a function with a specified this value. To understand that, this normally refers to whatever is on the left-hand side of the . when you call a function as a method of an object, e.g. when you call foo.bar(1, 2, 3), within the context of bar, this will refer to foo. So it's also possible to use bar.call(foo, 1, 2, 3) or bar.apply(foo, [1, 2, 3]) to achieve a similar effect; in both cases the first argument becomes this.
So basically, the console.log and all the .call's except the last one don't actually matter. The initial part of the code is just trying to apply .call on some function, and could just as easily be replaced with Function.prototype.call.apply
Skipping ahead a bit, a => a is an arrow function, short-hand for function(a) { return a; }. It creates an anonymous function that accepts one argument and returns that same argument as result. Note that this function doesn't actually refer to this so all the previous calls to .call and .apply are pretty much irrelevant.
Last [1, 2] is just an array literal containing two items, 1 and 2 which be unrolled as arguments by .apply.
So breaking it down:
console.log.call.call.call.call.call.apply(a => a, [1, 2]);
Can be reduced to:
Function.prototype.call.apply(a => a, [1, 2]);
Can be reduced to:
var fn = a => a;
fn.call(1, 2);
.call will use its first argument as the this value of the function and pass subsequent values as parameters, and since fn doesn't use this, we can further reduce it to:
var fn = a => a;
fn(2);
Since fn is just a simple identity function, the result is just:
2;
It doesn't do anything, apply will return 1 and 2 as separate values which call won't do anything with since it's not a function. Even if you were to do console.log.call.call(a => a, [1,2]) you wouldn't get anything since the first call returns just the array [1,2] which isn't a function the second call can do anything with...
console.log.call.call./*and so on*/call./*returns nothing eval({1, 2}) =
undefined*/call./*returns {1, 2} up the chain*/apply(a => a, [1,2]);
//You can try this which also won't return anything:
console.log.call([1,2]);
Although oddly enough I was expecting this sort of code to throw an error, which it didn't, it just didn't log or throw an error. So it really doesn't return anything...
The task (actually the problem) I'm facing is the following. I have an array (let’s call it arr). The first element of the array is a function and the rest are arguments. For example:
arr = [(a, b) => a + b, 5, 7];
So, I need to call function (a, b) => a + b with arguments 5 and 7. However, I don't want to use arr[0](...arr.slice(1)), instead I want to do it using Function.prototype.apply and Function.prototype.call (I can also use Function.prototype.bind if needed). So basically, I am allowed to access arr only once and no other variables may be used. Also, I'm not allowed to modify arr or it's properties, or to store any other data somewhere (like as property of some global object, etc). Is that even possible?
My attempt
I tried to figure it out, and this is what I came up with (it doesn't work):
Function.prototype.apply.call(this, ...arr);
However, it throws an error saying:
Uncaught TypeError: Function.prototype.apply was called on #Global, which is an object and not a function
Question
What is the correct way to do this?
instead I want to do it using Function.prototype.apply and Function.prototype.call. Is that even possible?
Sure, but we have to repeat arr:
arr[0].call(...arr)
Live Example:
const arr = [(a, b) => a + b, 5, 7];
console.log(arr[0].call(...arr));
That wouldn't work if the function required a specific this, but your function doesn't, for two reasons, either of which would be sufficient: 1. It's an arrow function, so it doesn't care what this we call it with, it closes over the one where it was created; and 2. It doesn't use this anyway.
That said, probably better just to give yourself a helper function that does it instead.
You can use destructuring to get the elements of arr array as variables, then call the function at index 0 with elements at indexes 1-N
let arr = [(a, b) => a + b, 5, 7];
let [fn, a, b] = arr;
console.log(fn(a, b));
For any number of elements at indexes 1-N you can use rest element
let arr = [(a, b) => a + b, 5, 7];
let [fn, ...rest] = arr;
console.log(fn.apply(null, rest));
Alternatively, using Function, template literal to convert array to string, .replace() with RegExp /\s/g with .replace() to remove space characters, .match() with RegExp /(\(.*\)=>[^,]+(?=,))|[^,]{1}[^\1]+/g to capture arrow function, negate first comma of characters that are not arrow function and arrow function captured at first group, we can reference arr once and not create additional variables save for immediately invoked arrow function parameters
let arr = [(a, b) => a + b, 5, 7];
console.log(
(([a, b]) => new Function(`return (${a})(${b.split(/,/)})`)())
(`${arr}`.replace(/\s/g, "").match(/(\(.*\)=>[^,]+(?=,))|[^,]{1}[^\1]+/g))
)
I am reading info about array destructuring and the spread syntax from MDN and I have stumbled upon the following example that left me a bit cold, despite going over the material progressively.
The code giving me trouble is this:
function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
I get that v==-1; w==0; x==1; and y==2... but what is ...[3]?
The example is from the spread syntax hyperlink above.
The example you've found is a bit contrived, and you probably wouldn't see it in real code. It's just meant to illustrate that the spread argument syntax ... works with any iterable expression, including standard array literals like [1, 2, 3]. z will be 3 because
myFunction(-1, ...args, 2, ...[3]);
is equivalent to
myFunction(-1, ...args, 2, 3);
The ...[ and ] essentially have no effect in this case; the values are pulled out of the array so it's as though you'd just written them directly in the argument list. As another example,
myFunction(-1, ...args, 2, ...[3, 4, 5]);
is equivalent to
myFunction(-1, ...args, 2, 3, 4, 5);
although z would still be 3 (4 and 5 would be ignored because they're unexpected extra arguments).
Let's break this down a bit: the behaviour of the spread/rest syntax ... in an argument list is defined in section 12.3.6.1 "Runtime Semantics: ArgumentListEvaluation" of ECMAScript 6. To paraphrase, it essentially says "when you see ...X in an argument list, evaluate X, then go through the values it contains and add each of them as a separate argument".
So, when JavaScript sees , 3 in an argument list, it says "add 3 to the argument list".
But when JavaScript sees , ...[3] in an argument list, it says "create a new array containing 3, then go through each of its values (only 3) and add them to the argument list".
You're doing the same thing in both cases, but the simpler way is probably faster.
MDN specifies a polyfill bind method for those browsers without a native bind method: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
This code has the following line:
aArgs.concat(Array.prototype.slice.call(arguments))
Which is passed as the args to the apply method on the function:
fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
However, this line actually repeats the arguments, so that if I called the bind method as:
fnX.bind({value: 666}, 1, 2, 3)
the arguments passed to fnX are:
[1, 2, 3, Object, 1, 2, 3]
Run the following example and see the console output http://jsfiddle.net/dtbkq/
However the args reported by fnX are [1, 2, 3] which is correct. Can someone please explain why the args are duplicated when passed to the apply call but don't appear in the function arguments variable?
The arguments are in two different contexts there. Each time a function is invoked, among other things, an arguments object is set to all the arguments passed (if the arguments is accessed).
The first mention of arguments are the arguments to bind(), which will become initial arguments.
The second mention are the next set of arguments called on the bound proxy function. Because the arguments name would shadow its parent arguments (as well as the this context needing to be separated), they are stored under the name aArgs.
This allows for partial function application (sometimes referred to as currying).
var numberBases = parseInt.bind(null, "110");
console.log(numberBases(10));
console.log(numberBases(8));
console.log(numberBases(2));
jsFiddle.
No. As you log from x, x() arguments: are [1, 2, 3].
From bind2 you console.log(aArgs.concat(Array.prototype.slice.call(arguments)));, where aArgs = Array.prototype.slice.call(arguments, 1). So what else than [1, 2, 3, {value: 666}, 1, 2, 3] do you expect? Those are not the arguments passed to fnX, but those passed to bind: [{value: 666}, 1, 2, 3].
Inside the bound function, the aArgs variable still contains [1, 2, 3], while the arguments now are empty - you did call x().
If you instead check the value of aArgs.concat(Array.prototype.slice.call(arguments)) from within fBound you'll see what you would expect. The key is that arguments refers to the additional arguments called on an already bound function.