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.
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...
I've tried the following with no success:
function a(args){
b(arguments);
}
function b(args){
// arguments are lost?
}
a(1,2,3);
In function a, I can use the arguments keyword to access an array of arguments, in function b these are lost. Is there a way of passing arguments to another javascript function like I try to do?
Use .apply() to have the same access to arguments in function b, like this:
function a(){
b.apply(null, arguments);
}
function b(){
console.log(arguments); //arguments[0] = 1, etc
}
a(1,2,3);
You can test it out here.
Spread operator
The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) are expected.
ECMAScript ES6 added a new operator that lets you do this in a more practical way: ...Spread Operator.
Example without using the apply method:
function a(...args){
b(...args);
b(6, ...args, 8) // You can even add more elements
}
function b(){
console.log(arguments)
}
a(1, 2, 3)
Note This snippet returns a syntax error if your browser still uses ES5.
Editor's note: Since the snippet uses console.log(), you must open your browser's JS console to see the result - there will be no in-page result.
It will display this result:
In short, the spread operator can be used for different purposes if you're using arrays, so it can also be used for function arguments, you can see a similar example explained in the official docs: Rest parameters
The explanation that none of the other answers supplies is that the original arguments are still available, but not in the original position in the arguments object.
The arguments object contains one element for each actual parameter provided to the function. When you call a you supply three arguments: the numbers 1, 2, and, 3. So, arguments contains [1, 2, 3].
function a(args){
console.log(arguments) // [1, 2, 3]
b(arguments);
}
When you call b, however, you pass exactly one argument: a's arguments object. So arguments contains [[1, 2, 3]] (i.e. one element, which is a's arguments object, which has properties containing the original arguments to a).
function b(args){
// arguments are lost?
console.log(arguments) // [[1, 2, 3]]
}
a(1,2,3);
As #Nick demonstrated, you can use apply to provide a set arguments object in the call.
The following achieves the same result:
function a(args){
b(arguments[0], arguments[1], arguments[2]); // three arguments
}
But apply is the correct solution in the general case.
If you want to only pass certain arguments, you can do so like this:
Foo.bar(TheClass, 'theMethod', 'arg1', 'arg2')
Foo.js
bar (obj, method, ...args) {
obj[method](...args)
}
obj and method are used by the bar() method, while the rest of args are passed to the actual call.
This one works like a charm.
function a(){
b(...arguments);
}
function b(){
for(var i=0;i<arguments.length;i++){
//you can use arguments[i] here.
}
}
a(1,2,3);
(() => 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
Previous posts have talked about how Array.prototype.slice.call(arguments) work but I don't get why you're using call instead of apply when apply is used for array-like objects whereas call is used on lists of objects separated by commas. Isn't arguments an array-like object that should used apply instead of call?
If you'd like to pass arguments to slice in array instead of one by one, then there is a difference. You could do it this way
[1, 2, 3, 4, 5, 6, 7] ---- our example arguments
Array.prototype.slice.call(arguments, 2, 5);
here you pass to slice method 2 and 5 and these are slice arguments to indicate that you want to get items at index 1 to item at index. 4. So most likely it will be 3, 4, 5.
Array.prototype.slice.apply(arguments, [2, 5]);
This does the same but arguments for slice can be passed in an array.
If x is an array, then these are the same:
// find the "slice" method of an array and call it with "x" as its "this" argument
x.slice();
// find the "slice" method of an array and call it with "x" as its "this" argument
Array.prototype.slice.call(x);
The first argument of func.call is this this argument, or, the value of this inside the function. The remaining arguments are the arguments of the function (in this case there are none).
The slice method, called with no arguments, simply creates a new array with the same contents. That's what we want to do when we do Array.prototype.slice.call(arguments), however, since arguments isn't an array and therefore has no slice method attach to itself, we have to use the second of the two methods to get the slice method of an array, and then apply that to something that's not an array:
// find the "slice" method of an array and call it with "arguments",
// which is *not* an array, as its "this" argument
Array.prototype.slice.call(arguments);