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.
Related
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
I'm reading the MDN Article on slice in JavaScript. I understand everything except the 2nd example in the section titled Array-Like Objects.
It says we can simplify the first example by making slice our own function as so:
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
function list() {
return slice(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
What I don't understand is how call can come right after prototype on the second line.
I usually see it in the form of Array.prototype.slice.call(arguments) or something of that sort.
I don't understand the flow of the first two lines and how they generate this working slice function.
tl;dr:
var slice = Function.prototype.call.bind(unboundSlice);
is a short way of writing:
var slice = function(value, start, end) {
return unboundSlice.call(value, start, end);
};
Let's think about this line for second:
Array.prototype.slice.call(arguments)
.slice is an array method to extract a subset of the array. It operates on the value of this. .call is a method every function has, it lets you set the this value for a function execution. So, the above line lets us execute slice as a method of arguments, without having to mutate arguments itself. We could have done
arguments.slice = Array.prototype.slice;
arguments.slice();
but that is not as clean.
Now looking at
Function.prototype.call.bind(unboundSlice);
As said, .call is a method that every function has. It also operates on this, which is expected to be a function. It calls this and sets the this value of that function to the first argument. You could think of call as being similar to
function call(thisValue, arg1, arg2, ...) {
return this.apply(thisValue, [arg1, arg2, ...]);
}
Note how it calls this as a function.
.bind is also a method every function has. It returns a new function which has its this value fixed to the first argument you pass in.
Let's consider what the resulting function of call.bind(unboundSlice) would look like:
function boundCall(thisValue, arg1, arg2, ...) {
return unboundSlice.apply(thisValue, [arg1, arg2, ...]);
}
We simply replaced this with unboundSlice. boundCall will now always call unboundSlice.
The MDN article for Function.prototype.call() helped me wrap my head around this.
The most simplistic way I can answer:
In javascript, a Function has a method called call. A function is an
object, and all objects inherit methods and properties from their
prototype.
So your example of Array.prototype.slice.call(arguments) shows you calling the call method on the slice function.
The second line in the code that you are confused about: var slice = Function.prototype.call.bind(unboundSlice); shows the call method belonging to the Function prototype.
Checkout JavaScript Prototypes if you are still confused.
1 Functions are objects.
2 "Every JavaScript object has a prototype."
3 "The prototype is also an object."
4 "All JavaScript objects inherit their properties and methods from their prototype."
In other words, back to the most simplistic way to answer this: In javascript, a Function has a method called call.
As for understanding what bind does, the that = this vs .bind example in this article helps make sense of what is going on.
If that was confusing, then make sure you understand context and scope
slice is a property of Array.prototype, and it expects its this object to be Array-like. You can use it on Array-like objects (that have a length property and have properties that you can index) that don't have their own slice function like so:
Array.prototype.slice.call(arraylikething);
That's a lot of typing, so we can make a function to do the same thing:
var slice = function(arraylikething){
return Array.prototype.slice.call(arraylikething);
};
JavaScript provides Function.prototype.bind to bind functions to a specified this object. So we can accomplish the same thing a bit more easily:
var slice = Function.prototype.call.bind(Array.prototype.slice);
bind creates a new function that returns the result of call with its this object set to Array.prototype.slice, the same as what we did manually above, and equivalent to your code.
The answer from Chris Dillinger is correct and informative. But here's another way to think about it. You're being asked, in essence, to define
Function.prototype.call.bind(Array.prototype.slice)
Which you can look at this way:
fn.bind(context)
==> function(...args) {return context.fn(...args);}
// 1. definition of `bind` (oversimplified, but enough for this case)
fn.bind(unboundSlice)
==> function(...args) {return unboundSlice.fn(...args);}
// 2. substitute `unboundSlice` for `context`
Function.prototype.call.bind(unboundSlice)
==> function(...args) {return unboundSlice[Function.prototype.call](...args);}
// 3. substitute `Function.prototype.call` for `fn`.
Function.prototype.call.bind(unboundSlice)
==> function(...args) {return unboundSlice[.call(...args);}
// 4. walk the prototype chain
Function.prototype.call.bind(Array.prototype.slice)
==> function(...args) {return Array.prototype.slice.call(...args);}
// 5. substitue `Array.prototype.slice` for `unboundSlice`
The only step that's even slightly tricky is step 4, where you have to realize that all functions inherit the call method from their prototype chain, so invoking call on them is merely an alternative means of invoking the functions themselves.
In the first line, Array.prototype.slice (which is a method) is simply referenced via unboundSlice. You're essentially 'extracting' the slice method from Array.prototype.
In the second line, the same thing happens for Function.prototype.call, which is also a method of ALL functions. (it's defined in Function.prototype, and inherited by all functions).
Next, by using .bind(unboundSlice) the Call function's this value is bound to the reference to Array.prototype.slice, which essentially results in the same thing as Array.prototype.slice.call(), where call also has its this bound to slice, because of it being a method of it, AND because it's being called like that.
Lastly, the bound call method is referenced via var slice;
The general idea here is that you're able to use the functionality of an array method (slice) in another context (the global scope).
So now, instead of calling call when it was already a method of slice, you're binding slice to be the this value of call in order to achieve the same behaviour.
If I type this and check it out in my Chrome console:
function f(){}
console.dir(f);
What is displayed are these keys:
> arguments
> caller
> length
> name
> prototype
> __proto__
Now, I'm curious if the arguments key on the constructor function is there to aid me in some way visually to see the arguments that are passed to a function, but everytime I pass an argument to a function it fires it off automatically:
function f(a){alert(a)}
console.dir(f("test"));
So, it seems quite useless as an analytic tool. Is this key just here to temporarily hold the arguments and nothing more just for the sake of passing arguments? Or is there something else to this key? I'm sure this is probably a dumb question but I'm curious.
The arguments object is a local variable available within all functions.
You can refer to a function's arguments within the function by using the arguments object. This object contains an entry for each argument passed to the function.
if a function is passed three arguments, you can refer to the argument as follows:
arguments[0]
arguments[1]
arguments[2]
Reference link arguments.
arguments is an array like object that is available in function objects in javascript. It allows a function a way to account for arguments that were used while invoking a function but do not have a parameter specifically assigned to it.
var mul = function ( ) {
var i, total = 0;
for (i = 0; i < arguments.length; i += 1) {
total *= arguments[i];
}
return total;
};
document.writeln(mul(4, 8, 2)); //64
Source: http://goo.gl/hKpFGl
This explanation is pretty much ripped directly from here. If you want to get familiar with some good patterns javascript offers this isn't a bad place to start.
arguments is an Array-like object corresponding to the parameters passed to a function.
For Example:
function callMe (a, b, c){
console.log(arguments);
}
callMe(); // return empty array []
callMe(1,2); // return array [1, 2]
callMe(1,2,3); // return empty array [1, 2, 3]
callMe(1,2,3,4); // return empty array [1, 2, 3, 4]
For more help, read this doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments
Observe the following:
function array_map(array, callback) {
for (var i = 0; i < array.length; i += 1) {
callback(array[i]);
}
}
var a = [], b = [];
array_map([1, 2, 3], function (x) { a.push(x); });
// just gives a = [1, 2, 3] as expected
// but why does this not work: ?
array_map([1, 2, 3], b.push);
// Chrome: a = [], Firefox: can't convert undefined to object
I do understand why this happens, namely: push is no longer bound to b (but to the global object) if you pass it to array_map directly. I don't really understand why Chrome doesn't give an error, at least Firefox seems to give some kind of error.
How can I detect if a function like this is passed to array_map to avoid these kinds of bugs?
I'm hoping there are advanced reflection techniques available to trace the origin of a function. For instance b.push.constructor gives Function, but that's not what I'm looking for.
I'm not sure what you expect there to happen. Array.prototype.map requires a function as second parameter, which returns a new value for every iteration.
Passing in just a function reference (which you do in your second example) doesn't tell the function what it has to do anyway. So you're kinda expecting that .map() applies some black magic and calls the passed in method with the correct parameter, which it obviously, can't do.
I totally didn't get that you wrote your own mapping function. However, your problem there is that you're losing scope of that .push() function. Only if you call it on the Array / Object like xxx.push(), the this within the called function will correctly reference the target object. Once you just passed the reference, this will either point to global / window or undefined and won't work anymore.
So solve that issue you could call it like
array_map([1, 2, 3], b.push.bind(b));
which also would apply an ES5 function. You can't really detect for it within the array_map(). A function is a function, your best shot would be to detect whether or not the passed in method is a native or not, but I wouldn't recommend that.
Usually these sorts of functions would allow you to set the context of the callback.
If you change your array_map function to accept a context, then it will work like this.
function array_map(array, callback, context) {
for (var i = 0; i < array.length; i += 1) {
callback.call(context, array[i]);
}
}
var b = [];
// now the push method is called from the b context
array_map([1, 2, 3], b.push, b);
The problem is, that Javascript has no such thing like "methods" - a function exclusively bound to a certain object. In your first example you pass a function which invokes a.push. this is bound to a here because you invoke it directly on a.
In your second code you just pass the function push without the context of b - this will be bound to the execution context which is the gloabl object.
You need to bind the the context of the function like the following:
array_map([1, 2, 3], b.push.bind(b));
or jQuery's proxy(). I can't find another simple solution at the moment but the one to hand over the context directly in a third parameter: function array_map(array, callback, context)
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
**args.concat(Array.prototype.slice.call(arguments))**);
};
};
This function is in Prototype. Does it equal to:
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,**args**);
};
};
In my opinion, args.concat(Array.prototype.slice.call(arguments)) == args, since the anonymous
function haven't any arguments. What is the matter?
No, they aren't the same.
Tthe purpose of concatenating the arguments is to provide a way of partially apply (or curry) the function, pre-filling arguments when bind is used and being able to add more later when the function that bind returns is used, for example:
var obj = {
fx: function() {
alert(Array.prototype.join.call(arguments, ', '));
}
};
var fx2 = obj.fx.bind(obj, 1, 2, 3);
fx2(4, 5); // Alerts "1, 2, 3, 4, 5"
As you can see in the last two lines of code, when I declare fx2, I'm passing obj as the first argument (this will ensure the context, used as the object variable on the bind implementation), then I pass the values 1,2 and 3.
Those values are stored in the args variable of the outer closure of bind, then as you see in the bind implementation, another function is returned.
That function returned in my example is fx2 after the assignment, in the last line you see I call that function, passing two additionally arguments.
Finally the returned function will call obj.fx with the two argument lists, the arguments we pre-filled when calling bind (1,2,3) and the arguments when the function actually executed (4,5).
That is why makes sense concatenating the two argument objects.
the anonymous function haven't any arguments
The anonymous function can actually have arguments (as can the bind method itself, which also doesn't declare any arguments).
Type-checking in JavaScript is non-existent: you can pass fewer arguments into a function than are declared in the function(...) signature (in which case the arguments that aren't passed get received as undefined), and you can pass more than declared, in which case the only way to read them is through the arguments array, which always contains exactly how many arguments were passed in, regardless of what's in the function signature.
It's generally considered polite to put a comment in the signature (function(x, /* y, ... */)) to indicate that more arguments will be read using the arguments array.