In this article js log function, there is a statement:
Function.prototype.apply.call(console.log, console, arguments);
I'm really confused by this statement.
What does it do?
How can I analyse this statement?
Or with some thoughts or tools, I can figure it out step by step?
Can it be simplified to more statements to achieve the same result? for instance: var temp = Function.prototype.call(console.log, console, arguments); Function.prototype.apply(temp);
Thanks for the response.
Apply is a function on the function prototype. Call is also a function on the function prototype. Apply is a function, therefore, it has call on it's prototype. All this is doing is calling the apply function.
Read more about apply here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
What does it do?
It calls console.log with console as this during the call, passing along the arguments in the
pseudo-array arguments as discrete arguments to the function.
So if arguments had "hi", "there" in it, it would be the same as:
console.log("hi", "there");
How can I analyse this statement?
Or with some thoughts or tools, I can figure it out step by step?
Let's start with what the apply and call functions are: They each have the ability to call a function
using a specific this during the call and passing arguments to that function. apply gets those
arguments from a single array (or anything array-like). call gets those arguments as individual arguments.
Normally, you'd see apply or call used like this:
someFunction.apply(valueForThis, ["arg1", "arg2", "arg3"]);
// or
someFunction.call(valueForThis, "arg1", "arg2", "arg3");
The only difference between apply and call is how they expect to get their arguments (apply = in
an array-like thing, call = as individual arguments).
So why, then, isn't that code doing this?
console.log.apply(console, arguments);
It's being cautious: console.log is a function provided by the host. It may not be a true JavaScript
function, and so it may not have the apply property.
So that code is avoiding relying on console.log having the apply property.
Which is where Function.prototype comes in. It's a reference to the object that is the prototype of all true JavaScript functions.
That prototype is where the apply and call properties on JavaScript functions come from.
So if we're worried that console.log doesn't have it (e.g., in case it doesn't inherit from Function.prototype), we can grab apply from that prototype object directly.
So the code is using call to call apply, and using apply to call console.log.
Can it be simplified to more statements to achieve the same result?
Not really, there's not a lot we can separate. I'll try to use variable names to clarify:
var thisValue = console;
var functionToCall = console.log;
var applyFunction = Function.prototype.apply;
applyFunction.call(functionToCall, thisValue, arguments);
Let's take this one part at a time.
Function.prototype.apply, more commonly seen as myBigFatFunction.apply, lets you call a function with a different this context than it would normally have. The difference between apply and call is that the former takes an array of arguments, the latter takes direct arguments after the first. Example:
myStr.substring(5)
String.prototype.substring.apply(myStr, [5]);
String.prototype.substring.call(myStr, 5);
^ all equivalent
However, for reasons I can't fully explain myself, some browser-native functions accessible to JavaScript don't have this function as a property (eg, console.log.apply). It's still possible to manually call it in the same manner though; so that console is still this, when it calls log, and that's what the given function is doing.
The reason for all that complication is that they want to pass in the arguments special variable. This is a keyword that exists in all functions, which represents all arguments to the function as an array-like object (so, suitable for Function.prototype.apply)
Your variable suggestion would likely simply call console.log once with console as argument one, and arguments as variable two, and return the result, rather than give you a function in a variable. If you want a shortened reference, it's possible you could use Function.prototype.bind, but that could actually lengthen your code.
Related
I’d like to know both for regular all-in-the-family JS developer-defined functions, as well as predefined DOM methods: what happens if I try to call IE’s attachEvent with the signature of the WHATWG’s addEventListener? For instance:
elem.attachEvent('onbillgates\'mom', function(e){ this.mount(); }, false);
Specifically, note the third argument false. Will that trip anything up, even though the attachEvent method’s signature only calls for two arguments?
What about this example?
function foo(FirstOf2, SecondOf2) {
console.log(FirstOf2 + SecondOf2);
}
foo(1, 2, true);
JavaScript doesn't have the concept of a fixed parameter list. For your own functions you can always specify as many parameters as you want and pass in as many as you want which ever type you want.
For built-in functions, which correlate to native code, it depends.
You asked on what it depends:
Let's look at the ECMA-262
Section 15 about built-in (not to confuse with host) functions in general
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given fewer arguments than the function is specified to require, the function or constructor shall behave exactly as if it had been given sufficient additional arguments, each such argument being the undefined value.
Alright. If I pass in less arguments than needed, it depends on the spec of the function itself (scroll down section 15 to find the spec for each built-in function).
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given more arguments than the function is specified to allow, the extra arguments are evaluated by the call and then ignored by the function. However, an implementation may define implementation specific behaviour relating to such arguments as long as the behaviour is not the throwing of a TypeError exception that is predicated simply on the presence of an extra argument.
Passing in too many arguments should never raise a TypeError. But still it may raise other errors. Again, it depends on the function you talk about.
You were talking explicitly about the DOM and not about built-in functions. To be honest I can't find the corresponding parts of the spec. The ECMA spec is so much easier to read then the w3 website.
Won't hurt. You can even call a function with less parameters than it takes, as long as the function code is ok with a few undefined values.
I came across this important, however old, question; and I hope it'll be beneficial for future generations to share my experiments with it:
One can use the arguments object in order to access a function's arguments, regardless of the amount of arguments in the function's signature.
It's worth mentioning that this doesn't apply to arrow functions:
function singleArg(x) {
console.log(arguments);
}
singleArg(1, 2); // Called with 2 arguments
singleArg(); // Called with 0 arguments
// Results in an error, as 'arguments' isn't defined for arrow functions
((arg) => console.log(arguments))(1);
It's stated in the documentation that arguments isn't exactly an Array:
“Array-like” means that arguments has a length property and properties indexed from zero, but it doesn't have Array's built-in methods like forEach() and map().
Hence the following code results in an error:
(function singleArg(x) {
console.log(arguments); // This line works
arguments.forEach(x => console.log(x)); // This causes an error
})(1, 2);
Upon calling a function with less arguments than in its signature, they're assigned undefined:
(function twoArgs(a, b) {
console.log(`a: ${a}\nb: ${b}`);
})(1);
Try taking a look at this post and perhaps this one.
From MDN:
The arguments object is a local variable available within all
functions; arguments as a property of Function can no longer be used.
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, the first entry's index starting at 0.
You can use the arguments object if you call a function with more
arguments than it is formally declared to accept. This technique is
useful for functions that can be passed a variable number of
arguments.
function myConcat(separator) {
var result = "";
// iterate through non-separator arguments
for (var i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
I don't think it will mess anything up unless you are explicitly dealing with the implicit arguments array. Why are you doing this though?
Before asking, I have tried to do my homework and to avoid a duplicate. Thus, I have read about 20 questions and answers (mainly on SO) which all deal with toString(). But unfortunately, none of them did answer my actual question. So here we go ...
Many examples contain code like that:
Object.prototype.toString.call(someVariable);
I just would like to know why toString can be used like a property here. I have read the reference for Object.prototype at MDN and other places. All of them list a function toString() among the members of Object.prototype, but no property toString.
Furthermore, I am using a line like that shown above at several places in my code. For testing purposes, I have added parentheses to make it "clean":
Object.prototype.toString().call(someVariable);
Obviously, that did not make it "clean", but just made it return wrong results or even made the browser stall (I am currently in the process of researching what exactly is going on).
I already have read some questions and answers regarding calling functions without parentheses. But even if I accept that the first of the code lines shown above actually calls a function (although it looks like accessing a property), that still does not explain why it goes wrong horribly when I add the parentheses as shown in the second code line. Being able to call functions without parentheses should not mean being unable to call them with parentheses, should it?
I don't think that question has an answer already (if yes, I apologize), so could anybody please give a short explanation?
Is Object.prototype.toString a function or a property?
Object.prototype.toString is a property. The value of that property is a reference to a function. Exactly like this:
var obj = {f: function() { } };
There, obj.f is a property, the value of which is a reference to a function.
The initial value of Object.prototype.toString is the intrinsic function known in the spec as %ObjProto_toString%. It can be overwritten, but doing so would like break a lot of things.
The thing to remember is that in JavaScript, functions are just objects that inherit from Function.prototype* and are callable. Just like other objects, you can keep references to them in properties and variables (you can even add properties to functions themselves), pass those references around, etc. This is in marked contrast to many languages which treat "classes" and methods and other kinds of functions as special, non-object things.
* (host-provided objects aren't required to inherit from Function.prototype, but in modern environments most do; in some obsolete browsers, some don't.)
Functions are just values. toString is a property of the Object.prototype object whose value is a function.
() is the function call operator. Object.prototype.toString doesn't call a function; it just fetches the value of the Object.prototype.toString property (which happens to be a function).
Functions are also objects, with properties of their own. That's why you can do Object.prototype.toString.call(...): This gets the Object.prototype.toString function, then fetches its call property, then calls it (which is allowed because the value of call is another function).
This works even without involving properties:
var foo = function () { return "hello"; };
var bar = foo;
console.log(bar);
console.log(bar());
The first line assigns a function value to the foo variable.
The second line assigns the same value to bar, reading from foo. Note that we're not calling the function, we're just passing it around like any other value.
The first console.log displays the function itself.
The second console.log displays the result of calling the function, because we used ().
Welcome to JavaScript. It's true that functions can be called without () in some cases (specifically, new f), but not in this case. What you see is the reference to the function being used as an object but not called (yet). That's a common thing to do, although in this case it's probably a bit more obscure than usual so I'll explain why it's done like that.
The function finally gets called when you explicitly call its call method (every function inherits that from the Function prototype), which allows you to bind this in the function body to some arbitrary object. Your first example may do the same thing as someVariable.toString(). So why use the longer form ?
Well, someVariable may not have a toString method (if it's null or undefined, because they are not objects and can't be boxed into an object), in which case using someVariable.toString would throw a TypeError. Or its prototypal toString method may have a different behaviour than the one for basic Objects. In this case, I guess that the author wanted to use an old-school trick for getting the name of an object's "species", which involves the fact that Object.prototype.toString always returns "[Object whatever]" where "whatever" will be the constructor's name or Null or Undefined.
I've been reading a bunch of web pages that talk about JavaScript interview questions, and I noticed a great many of them say that it's important to know the difference between .call() and .apply().
For example:
http://www.sitepoint.com/5-typical-javascript-interview-exercises/
https://github.com/h5bp/Front-end-Developer-Interview-Questions
For the life of me, I cannot understand why this is considered so important. Does anyone know? To me, it's probably as important as knowing the difference between, say, a js long date and a js short date (that is, it doesn't seem very important to me). My gut instinct tells me someone influential at some point said it's critical to know the difference between .call() and .apply(), and everyone just copied what this person said. But maybe I misunderstand something about .call() and .apply()? I'm not sure.
Simply put, more advanced types of Javascript will occasionally need to use either .call() or .apply(), particularly code that is trying to proxy functions and pass on arguments or control the value of this.
If you're doing those types of things, then you have to know how both .call() and .apply() work so you can use the correct method (since they don't work identically). If you're not doing those types of advanced things, then a lot of Javascript can be written without using them.
As an interview question, I think it's a reasonable test on whether you understand some of the more advanced nuances of argument passing, calling methods and this handling in Javascript. If you want to do well on those types of questions, you will need to understand both .call() and .apply(), now how to explain what they do and how and when to use them and when you would use one vs. the other.
The whole concept of this and how it is controlled or set in Javascript is often not understood well by lesser experienced Javascript programmers (heck I've even seen some experienced developers that didn't understand it well) so quizzing a candidate on things related to that is a reasonable test and .call() and .apply() are somewhat central to that.
If you were developing library or framework code, you'd be even more likely to be using .call() or .apply() in the work you do.
Here's a basic summary:
Every function in Javascript is an object that has some properties. So, in addition to be able to call a function like func(), you can also reference the properties on it like func.length.
Two of the properties that every function has are .call() and .apply(). They both allow you to call the function in somewhat different ways than just calling it as func().
.call()
We all know, of course, that if you just want to call a function with a fixed number of arguments, that you can just execute func(arg1, arg2) and those arguments will be passed to the function, but what if you want to control what the this value will be when you pass those fixed arguments? Calling func as func(arg1, arg2) will cause the this value inside that function to be set to either to the global object which is window in a browser or if running in strict mode to undefined. Every function call in Javascript such as func(arg1, arg2) resets the this pointer in this way. This is where .call() comes in. You can execute:
func.call(someThisValue, arg1, arg2)
And it will do the same thing as func(arg1, arg2) except it will cause the this pointer to be set to someThisValue.
Note: you can also use .call() with just one argument and it will just set the this pointer and not pass any arguments.
func.call(someThisValue)
.apply()
But, what if your argument list is not fixed and your arguments are in a variable length array or array-like object. You can't really use .call() because you can't type out the right .call() statement for every possible list of arguments. This is where .apply() comes in. A canonical use for .apply() is when you're just trying to pass on the arguments from some other function call. This is common when you're proxying other function calls to slightly modify their behavior, but still call the original and the original either has various forms (so you don't exactly know the arguments that were passed) or lots of different types of function calls all go through this proxy. In this case, you can use .apply(), often with the arguments object. You can see an example of this in the MDN polyfill for .bind().
Let's say I want to hook some existing function named doIt() for logging purposes and doIt() has a number of different ways that it can be called. Using .apply(), I could do it like this:
// regular function already defined in your program
function doIt(arg1, arg2) {
// do something
}
// then actual usage elsewhere in the program would be just this:
doIt("foo", "bar");
// now install a proxy that can intercept all calls to doIt() and
// add some behavior before and after
(function(origDoIt) {
// replace doIt function with my own proxy
doIt = function() {
console.log("before doIt()");
// call the original function with all the arguments and this pointer
// that were passed
var retVal = origDoIt.apply(this, arguments);
console.log("after doIt()");
return retVal;
}
})(doIt);
FYI, .apply() can also be used to just set the this pointer as in:
func.apply(someThisValue)
In that particular case (and only that case), it works identically to .call().
Here's one of my favorite uses of .apply(). The Math.max() method accepts a variable number of arguments and it will return the largest number of any of those arguments. Thus:
Math.max(1,2,3,4)
will return 4.
But, using .apply(), we can find the max number in any array.
var list = [999,888,777,666,555,444,333,1000];
var m = Math.max.apply(Math, list);
console.log(m); // 1000
We're using .apply() to send the entire list array as the arguments to Math.max() so it will operate on the entire array.
Note, when ES6 is fully implemented everywhere or in your particular execution envirionment, you can also use the new spread operator to do something similar:
var list = [999,888,777,666,555,444,333,1000];
var m = Math.max(...list);
console.log(m); // 1000
which becomes kind of a shorthand for what we were doing with .apply()
Assume, for the sake of this question, that I want to be able to create a function in Javascript that appends all of the elements of one array to another array. One way to achieve this, if you have access to the destination array, is to say:
var destination = [1,2,3];
var source = [4,5];
Array.prototype.push.apply(destination, source);
console.log(destination); // [1,2,3,4,5]
Now, since Array.prototype.push.apply is pretty ugly, I want to alias it to something nicer, like:
var pushAll = Array.prototype.push.apply;
Which I should be able to call with two arguments, the context (destination) and an array of arguments (source). However, when I try to use the alias, this happens:
pushAll(destination, [6,7]);
TypeError: Function.prototype.apply was called on [object global], which
is a object and not a function
So clearly the apply function is not bound to push, which led me to try this:
var pushAll = Function.prototype.apply.bind(Array.prototype.push);
pushAll(destination, [6,7]);
console.log(destination); // [1,2,3,4,5,6,7,8]
Which clearly works fine. My question is, why do I have to bind the push method to apply? Shouldn't Array.prototype.push.apply already be bound to apply? Why does calling it under a different name result in calling it on an unbound context?
why do I have to bind the push method to apply?
It's the other way round: You have to bind the apply method to the Array push function - you can bind it to other functions as well! Otherwise apply doesn't know which method to apply with the arguments.
Function.prototype.apply.bind(Array.prototype.push); does call the bind function on the apply function with push as the argument, the argument on which apply is then bound. The resulting function pushAll will, when called, invoke apply on the push function, and pass it's argument (the array and the arguments array) to it.
Shouldn't Array.prototype.push.apply already be bound to apply?
Nope. JavaScript is designed to bind the context at the call of a function, not already when it's being referred to as a property - there is no implicit binding on property access. Otherwise Array.prototype.push would already be bound to Array.prototype, before you could call any Function methods like bind/apply on it and try to use it with a different context.
Why does calling it under a different name result in calling it on an unbound context?
It's not so much different name, but different style. (Unbound) Functions do get their this value set to the object when they are called as a method on it, i.e. when the reference to the called function is a property access: destination.push().
This allows for great flexibility, you can "borrow" functions from an object and call them on other objects, still being the same (unbound) function. This is rather impossible in languages where function objects are no first-class objects.
If functions (even though they were meant to be methods) are called as plain functions (pushAll()), their this value will be undefined (unless in sloppy mode). Read more on the this keyword at MDN.
I’d like to know both for regular all-in-the-family JS developer-defined functions, as well as predefined DOM methods: what happens if I try to call IE’s attachEvent with the signature of the WHATWG’s addEventListener? For instance:
elem.attachEvent('onbillgates\'mom', function(e){ this.mount(); }, false);
Specifically, note the third argument false. Will that trip anything up, even though the attachEvent method’s signature only calls for two arguments?
What about this example?
function foo(FirstOf2, SecondOf2) {
console.log(FirstOf2 + SecondOf2);
}
foo(1, 2, true);
JavaScript doesn't have the concept of a fixed parameter list. For your own functions you can always specify as many parameters as you want and pass in as many as you want which ever type you want.
For built-in functions, which correlate to native code, it depends.
You asked on what it depends:
Let's look at the ECMA-262
Section 15 about built-in (not to confuse with host) functions in general
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given fewer arguments than the function is specified to require, the function or constructor shall behave exactly as if it had been given sufficient additional arguments, each such argument being the undefined value.
Alright. If I pass in less arguments than needed, it depends on the spec of the function itself (scroll down section 15 to find the spec for each built-in function).
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given more arguments than the function is specified to allow, the extra arguments are evaluated by the call and then ignored by the function. However, an implementation may define implementation specific behaviour relating to such arguments as long as the behaviour is not the throwing of a TypeError exception that is predicated simply on the presence of an extra argument.
Passing in too many arguments should never raise a TypeError. But still it may raise other errors. Again, it depends on the function you talk about.
You were talking explicitly about the DOM and not about built-in functions. To be honest I can't find the corresponding parts of the spec. The ECMA spec is so much easier to read then the w3 website.
Won't hurt. You can even call a function with less parameters than it takes, as long as the function code is ok with a few undefined values.
I came across this important, however old, question; and I hope it'll be beneficial for future generations to share my experiments with it:
One can use the arguments object in order to access a function's arguments, regardless of the amount of arguments in the function's signature.
It's worth mentioning that this doesn't apply to arrow functions:
function singleArg(x) {
console.log(arguments);
}
singleArg(1, 2); // Called with 2 arguments
singleArg(); // Called with 0 arguments
// Results in an error, as 'arguments' isn't defined for arrow functions
((arg) => console.log(arguments))(1);
It's stated in the documentation that arguments isn't exactly an Array:
“Array-like” means that arguments has a length property and properties indexed from zero, but it doesn't have Array's built-in methods like forEach() and map().
Hence the following code results in an error:
(function singleArg(x) {
console.log(arguments); // This line works
arguments.forEach(x => console.log(x)); // This causes an error
})(1, 2);
Upon calling a function with less arguments than in its signature, they're assigned undefined:
(function twoArgs(a, b) {
console.log(`a: ${a}\nb: ${b}`);
})(1);
Try taking a look at this post and perhaps this one.
From MDN:
The arguments object is a local variable available within all
functions; arguments as a property of Function can no longer be used.
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, the first entry's index starting at 0.
You can use the arguments object if you call a function with more
arguments than it is formally declared to accept. This technique is
useful for functions that can be passed a variable number of
arguments.
function myConcat(separator) {
var result = "";
// iterate through non-separator arguments
for (var i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
I don't think it will mess anything up unless you are explicitly dealing with the implicit arguments array. Why are you doing this though?