`arguments` used in inner function/arraow function behave differently [duplicate] - javascript

(() => 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

window.onblur and console.log partial application with bind

I was reading through some javascript posts, and I came across this answer.
Basically, in the answer, the poster said that you could set
window.onblur = myBlurFunction
only if myBlurFunction is a function that doesn't need any arguments passed to it.
I was about to comment that it was possible to use bind to perform partial application for functions requiring arguments, but when I tried
var myBlurFunction = console.log.bind(console,'blur');
window.onblur = myBlurFunction;
blurring the window didn't print the string "blur", but instead printed what seems to be a blur object
blur blur { target: Window → window-onblur-not-working, …
Does anyone know why this approach doesn't work?
What I'm really looking for with my question is why is the event handler function given the event as an argument?
window.onblur = function(event){console.log(event)}
I've never seen any documentation that mentions or explains the event parameter.
Also, how is the bound parameter overridden? Typically once a value is bound to a function parameter, any additional arguments will be assigned to the subsequent parameters:
var f = function(arg1,arg2){console.log(arg1,arg2)};
g = f.bind(null,1);
g(); // 1 undefined
g(2); // 1 2
g.call(null,2); // 1 2
Quoting the bind() page on MDN:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Let's split those two concepts and then explain them together in your example.
Changing the this context
This is an adaptation of the example in the MDN page (I promise it's simple!):
// This assignment is equivalent to 'window.x' or 'var x' in the global scope
this.x = "global!";
var obj = {
x: "not global!",
getX: function() {
return this.x;
}
};
// Running inside the object's scope, returns obj.x
obj.getX();
//=> "not global!"
// Assign the scoped function (obj.getX) to a global variable
var retrieveX = obj.getX;
// Running in the global scope, returns window.x
retrieveX();
//=> "global!"
// Binds the 'retrieveX' function to run inside the object's scope
var boundedRetrieveX = retrieveX.bind(obj);
// Running inside the specified 'obj' scope, returns obj.x
boundedRetrieveX();
//=> "not global!"
From that, we gather that passing obj as an argument changes what context this refers to.
On your example, you're doing something like this:
console.log.bind(console); // The second argument doesn't matter for now
So you're telling console.log that any instances of this are a reference to the console context. Which I suppose is fine, shouldn't do much damage.
Prepending arguments
Again, adapting from the MDN page example:
function list() {
// Simply convers the arguments list into an Array, then returns it
return Array.prototype.slice.call(arguments);
}
// Example usage
list(1, 2, 3);
//=> [1, 2, 3]
// Using 'bind' to prepend to (append to the start of) the arguments list
// Note that, because 'this' context doesn't matter, the first argument is null
var betterList = list.bind(null, 98);
// Passing no arguments, it returns an array with only 98
// This is similar to '[98].concat([])'
betterList();
//=> [98]
// Passing arguments, it appends 98 to the start of the array
// Again, this is similar to '[98].concat([1,2,3])'
betterList(1, 2, 3);
// [98, 1, 2, 3]
// The parameters can go on indefinitely. They will all be added to the start of the arguments list in order
var bestList = list.bind(null, 98, 99, 100);
bestList(1, 2, 3);
//=> [98, 99, 100, 1, 2, 3]
The function list turns the Array-like object arguments, which contains all of the arguments passed to the function, into an actual Array.
With bind(), we append values to the start of that argument list, so that to the function, it seems as if they were already passed in that way in the first place.
Your code looks something like this:
console.log.bind(console, "blur");
Ignoring the first argument, you're prepending the arguments sent to console.log (which in this case is the event response) with "blur". Which also isn't harmful, just not very useful.
Final thoughts
So, here's a screenshot of me playing around with the arguments. The first argument, that indicates the context for this, is set to null, just like in the example above, because it doesn't actually matter here. And I've passed a long list of arguments afterwards to be prepended to the onblur event response.
As you can see, even though I added a bunch of stuff to the response, the Event object (not a blur object! haha) is still there.
So that is why it "doesn't" work. It works in its own way. That just might not be what you were expecting.
You can still go for approaches presented in the question you linked, such as
window.onblur = () => console.log("blur");
Solutions that are less complicated and actually do what you expect them to

Does JavaScript `arguments` contain `this`?

I'm trying to understand JavaScript's arguments implicit variable in functions. Some tutorials delete the 0th element of it and say that it contains this but some other tutorials don't delete the 0th element. I'm very confused.
I wrote this code example and it shows that arguments doesn't contain this:
function aaa () {
console.log(arguments[0])
}
aaa(1,2,3);
Is it possible that sometimes arguments contains this? I wonder why some tutorials slice away the 0th element before using arguments.
most likely you have a function like blah(x) In which case you take off the first argument because it is already captured as the variable x, and you want the rest of the arguments that have been passed in.
The first argument is not this.
Arguments is an array* of the original arguments passed into the function, and doesn't directly have anything to do with the "this" variable.
That said, different tutorials probably try to explain how function references work, etc, using the scope (scope => "this" variable) of the function. This could easily involve passing an array and shifting off the first argument.
Consider this simple snippet:
var sample = function(a,b,c){
console.log(arguments, this);
};
sample(1,2,3);
Outputs:
[1, 2, 3], window
As we know by now, "this" is a special variable that has to do with the scope of the function. There are plenty of articles describing what it does/how it works, but in this context specifically, you've probably seen things used in a manner with .call or .apply:
sample.call(sample, 1, 2, 3)
or
sample.apply(sample, [1,2,3])
Those snippets both do the same thing - they convert the "this" scope of the function from the window object (since "sample" was declared as a global function) to the "sample" function itself, and pass the parameters 1, 2 and 3. They output:
[1, 2, 3], [sample function]
The reason some tutorials will shift off the first argument in this context is that there are often "helper" functions to make it more obvious when scope is changed, and many times those helper functions take, as their first parameter, the scope in which the new function is to be executed. So, they shift off the first parameter, and use that when (essentially) calling apply. A common example is bind(), like so:
Function.prototype.bind = function(scope){
var me = this,
args = Array.prototype.slice.apply(arguments, [1]);
return function () {
var handlerArgs = [];
for (i = 0; i < args.length; i++) {
handlerArgs.push(args[i]);
}
for (var i = 0; i < arguments.length; i++) {
handlerArgs.push(arguments[i]);
}
me.apply(scope, handlerArgs);
};
};
Now, you can call:
var bound = sample.bind(sample, 1);
bound(2,3);
...and get the output:
[1, 2, 3] [sample function]
You can see we're passing some parameters (the scope and the first parameter) when we bind the function initially, at which point we slice off the first argument ("sample", because that's the "scope" and has to be handled differently than any other arguments), then later, when bound() is invoked, push the 1, as well as 2 and 3, into the final arguments list.
It's a bit confusing at first, but hopefully that helps a little.
*Technically array-like.

How do functions in Javascript "delay evaluation" when wanting to avoid eager evaluation?

This example is taken out of the book, Javascript Allonge. The topic regards control-flow operators and evaluation of function parameters.
const or = (a, b) => a || b
const and = (a, b) => a && b
const even = (n) =>
or(n === 0, and(n !== 1, even(n - 2)))
even(42)
//=> Maximum call stack size exceeded.
Here the book notes that this will lead to infinite recursion. I believe I understand this part. Because all parameters will be evaluated, even if the a parameter in or() is true, the b parameter will still be evaluated (when the or() function is called under even(), that is). The same holds for and(). The even(n - 2) parameter will end up being evaluated over and over with n being 2, 0, -2, -4...
As a solution, it says one can pass anonymous functions as parameters.
const or = (a, b) => a() || b()
const and = (a, b) => a() && b()
const even = (n) =>
or(() => n === 0, () => and(() => n !== 1, () => even(n - 2)))
even(7)
//=> false
Now I understand how the code was re-written to work with anonymous functions that contain the original expressions, but I don't get how this "delays evaluation." Because the anonymous functions are still parameters to the or() and even() functions, what's to prevent them from evaluating and reaching the same outcome as the previous code?
Because the anonymous functions are still parameters to the or() and even() functions, what's to prevent them from evaluating and reaching the same outcome as the previous code?
The arguments to the or()/and() calls are indeed evaluated. But it means that the function expression is evaluated (to a function) and then passed to or/and, not that the function is actually called. It is only called from a()/b() inside the function, and only when it is actually needed (because of the short-circuiting of the operators) - that's where the recursive call is actually made.
Btw, this concept is also called a thunk.
In both versions of even(), the arguments to the outer call to or() have to be evaluated before or() is invoked. Because of that, in the first version:
const even = (n) =>
or(n === 0, and(n !== 1, even(n - 2)))
the parameter expressions have to be evaluated, and the second argument will trigger infinite recursion.
In the second version, however, the or() and and() functions expect to be passed functions that return values, not the values themselves. Thus, until the code makes it inside the implementation of or() and and(), the functions aren't invoked. Because the or() and and() functions are written to take advantage of the JavaScript short-circuit logic operators, there's no infinite recursion.
Thus here:
const even = (n) =>
or(() => n === 0, () => and(() => n !== 1, () => even(n - 2)))
it's still the case that the actual parameters to or() have to be evaluated, but the parameters are just functions — they say what to do to get a value, but they don't actually do anything until they're called.
The operators || and && use short-circuit evaluation:
Short-circuit evaluation, minimal evaluation, or McCarthy evaluation
denotes the semantics of some Boolean operators in some programming
languages in which the second argument is executed or evaluated only
if the first argument does not suffice to determine the value of the
expression
For example, if you have a() || b() and the call to a returns true, the function b won't be called.
However, with your or and and functions you can't achieve this behavior, because they are functions, not operators. And the parameters passed to a function are evaluated before calling the function.
Therefore, or(a(), b()) will call both a and b, even if the first returns true.
Passing functions instead works because functions do not run until you call them. So, instead of comparing the values returned by them, you pass the functions themselves to or or and, and since those are implemented using the operators || or &&, the call of the functions will be short-circuit evaluated.

Javascript call and apply functions only called on first argument?

Edit: this question was asked due to my misunderstanding. Proceed with caution, as reading it might waste your time.
I thought call and apply would execute a function given a set of arguments, but I'm getting confusing test results. See my test code:
window.z = 0;
(function(){++(window.z)}).call(this, 1, 2, 3)
I would expect z to be 3 after execution. However, z is 1.
(function(){++(window.z)}).apply(this, [1, 2, 3])
Same here. z == 1;
I tried simply logging the input argument as well:
var x = function(y){console.log(y);}
x.call(this, 1, 2, 3);
Result? Only 1 is logged.
What am I doing wrong here?
(Tested in Chrome and Firefox with Firebug.)
Both call and apply only call the function once. The difference is how the arguments to the invocation are passed.
With call, each parameter after the context (first parameter), is a parameter. With apply, the second parameter should be an array like object of parameters (the first parameter still provides the context).
function foo(a, b, c) {
};
foo.call(this, 1, 2, 3); // a == 1, b == 2, c == 3;
foo.apply(this, [1,2,3]); // a == 1, b == 2, c == 3;
If you want to call the function multiple times, you can accomplish this by simply putting the call in a loop.
It seems that you are under the impression that the call and apply methods should call the functon once for each parameter. That is not the case, it only calls the function once.
The apply methods takes an array of arguments:
func.apply(this, [1, 2, 3]);
The call methods takes an argument list:
func.call(this, 1, 2, 3);
This is expected, the arguments you pass will be present in your increment function (as arguments see here for reference), but you are only calling the function once.

Passing arguments forward to another javascript function

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);

Categories