Abbreviating console.log in JavaScript [duplicate] - javascript

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
alias to chrome console.log
This is really silly, but I can't abbreviate console.log (I'm using Chrome). Here's my naive attempt:
var log = console.log;
log("Bingo"); // Uncaught TypeError: Illegal invocation
Should I be using apply()? But then I'd have to pass along the arguments, right?

This fails because references to Javascript methods do not include a reference to the object itself. A simple and correct way to assign the method console.log to a variable, and have the call apply to console, is to use the bind method on the log method, passing console as the argument:
var log = console.log.bind(console);
There is a hidden this argument to every method, and because of arguably bad language design, it's not closured when you get a reference to a method. The bind method's purpose is to preassign arguments to functions, and return a function that accepts the rest of the arguments the function was expecting. The first argument to bind should always be the this argument, but you can actually assign any number of arguments using it.
Using bind has the notable advantage that you don't lose the method's ability to accept more arguments. For instance, console.log can actually accept an arbitrary number of arguments, and they will all be concatenated on a single log line.
Here is an example of using bind to preassign more arguments to console.log:
var debugLog = console.log.bind(console, "DEBUG:");
Invoking debugLog will prefix the log message with DEBUG:.

The simple way will be to wrap it in a function:
var log = function (l) {
console.log(l);
}
But make note that console.log can take an unlimited number of arguments so the appropriate way will be do this:
var l = function () {
console.log.apply(console, arguments);
}

Related

Why console.log doesn't work as an argument? [duplicate]

This question already has answers here:
What is the difference between a function call and function reference?
(6 answers)
Closed 12 months ago.
In javascript there is a callback, and functions are first-class objects right? so i can pass a function as an argument however if i pass console.log() it doesn't work why is that isn't it a function as well?
setTimeout(function(){console.log("hello")},5000);
this code is valid however
setTimeout(console.log("hello"),5000);
produces an error, why is that?
When you call console.log with some argument, the argument is printed to console and the function returns undefined.
So when you do:
setTimeout(console.log("hello"),5000);
"hello" will be printed, but what you're essentially doing is:
setTimeout(undefined, 5000);
In the other example (that works) you create a new function, but you do not call it. So you pass this new function to setTimeout, and that is why it works.
The reason that the following code
setTimeout(console.log("hello"),5000);
fails is because console.log() is invoked directly inside the setTimeout parameter and it always returns undefined (more info: MDN Documentation on Console.log). This means you are essentially running setTimeout(undefined,5000);
You need to provide a function declaration that is not invoked. If you wrap the code inside of a function declaration and do not invoke it, setTimeout will invoke it for you after the specified time lapses. This is why your first statement works, you provide a function declaration:
setTimeout(function(){console.log("hello")},5000);
If you had invoked the function you provided in the first parameter it would also return undefined and "hello" would be output immediately.
So if you provide a function declaration it will work:
setTimeout(()=>{console.log("hello")},5000);
Another way to use console.log directly without a function declaration is to bind it to a variable (more info: MDN Documentation on Function.prototype.bind). An example using .bind prototype:
setTimeout(console.log.bind(null,"hello"),5000);
The code above binds "hello" to invocation of console.log. The first parameter is null in this example but it is the this context.
setTimeout also allows you to pass variables that you want the function to be invoked with. See MDN Documentation on setTimeout
For example to pass a variable:
setTimeout(console.log,5000,'hello');
In the above example you are telling setTimeout to invoke console.log in 5 seconds with the variable (in this case a sting) of 'hello'.
Invoking console.log('hello') will return undefined so you are not really passing it to setTimeout, It will print "hello" though but not inside a callback.
In most cases it wont throw an error (as you can see in the example below).
What you can do however is pass console.log (the function) and a 3rd argument the string "hello" in our case.
Running example with all 3 cases:
setTimeout(console.log("hello"),500);
setTimeout(function(){console.log("hello2")},500);
setTimeout(console.log,500,"hello3");
It produces an error since it evaluates console.log(...), which evaluates to undefined, and thus is not a function.
setTimeout accepts the function as a parameter, not the function call. console.log() is basically invoking the function/method but setTimeout requires the reference or more specifically called the callback.
In your example:-
setTimeout(function(){console.log("hello")},5000);
you can call it like
var callback = function(){console.log("hello")};
setTimeout(callback,5000);
The callback will be called by setTimeout later in future. I hope it clears everything.

Why doesn't Javascript bind my dot expression correctly? [duplicate]

This question already has answers here:
Cases where 'this' is the global Object in Javascript
(4 answers)
Closed 5 years ago.
I am wondering whether dot-abstraction methods (e.g. dog.bark) bind at runtime or at compile-time. My question concerns the following code, which throws an error:
(true ? ''.toLowerCase : ''.toUpperCase)()
But the following does not:
true ? ''.toLowerCase() : ''.toUpperCase()
Why is my string literal '' not getting resolved in the first example?
(true ? ''.toLowerCase : ''.toUpperCase)()
is equivalent to:
String.prototype.toLowerCase.call()
// or:
String.prototype.toLowerCase.call(undefined)
However,
true ? ''.toLowerCase() : ''.toUpperCase()
is equivalent to:
String.prototype.toLowerCase.call('')
In both cases, the first argument to call is converted to an object, which the this in String.prototype.toLowerCase will reference to.
undefined can't be converted to an object, but the empty string can:
function logThis () { console.log(this); }
logThis.call('');
The SO snippet console only shows {}, but it's actually the same thing that you get from new String(''). Read about the string wrapper on MDN.
This is actually quite simple once you get how methods work in javascript behind the scenes.
toUpperCase is a method. This is a function that operates on a specific object... usually via the this variable.
Javascript is a prototypal language... meaning that the functions attached to objects are just functions and can be copied. There is some work behind the scenes that makes sure this is set to the right thing when you call a method, but this work only happens when you call it as a method... as in the obj.method() form.
In other words: ''.toUpperCase() makes sure that this is set to the string '' when you call it.
When you call it as toUpperCase() this is not set to anything in particular (different environments do different things with this in this case)
What your code does could be rewritten as this:
var function_to_call;
if (true) {
function_to_call = ''.toLowerCase;
} else {
function_to_call = ''.toUpperCase;
}
function_to_call();
Because your function call: function_to_call() is not in the object.method() syntax, the thing that sets this to the correct object is not done, and your function call executes with this not set to what you want.
As other people have pointed out, you can use func.call(thing_to_make_this) or func.apply() to attach the correct thing to this explicitly.
I find it much more helpful to use .bind() - which is extremely under-used in my opinion. function_name.bind(this_object) gives you a new function that will always have this attached to the correct thing:
// assuming function_to_call is set
function_that_works = function_to_call.bind(my_object)
function_that_works(); // equivalent to my_object.function_to_call()
and this means you can pass around the function you get back from bind() as you would a normal function, and it will work on the object you want. This is especially useful in callbacks, as you can create an anonymous function that is bound to the object it was created in:
// this won't work because when this runs, 'this' doesn't mean what you think
setTimeout(function() { this.display_message('success'); }, 2000);
// this will work, because we have given setTimeout a pre-bound function.
setTimeout(function() { this.display_message('success'); }.bind(this), 2000);
TL;DR: You can't call a method as a function and expect it to work, because it doesn't know what this should be. If you want to use that function, you have to use .call(), .apply() or .bind() to make sure this is set correctly by the time the function executes.
Hope that helps.
Because these methods apply on the this context, and in your example the this is undefined
One way to override this variable by using bind method:
(true ? ''.toLowerCase : ''.toUpperCase).bind('Hello')();
this will return hello
Because when you do (true ? ''.toLowerCase : ''.toUpperCase)() you are not calling the function that is bound to a string. You are simply calling the function without any context.
Consider the following example:
var obj = {
objname: "objname",
getName: function() {
return this.objname;
}
}
When you call it with obj.getName(), it correctly returns the value, but when you do something like this:
var fn = obj.getName
fn() // returns undefined because `fn` is not bound to `obj`
In your first example the toLowerCase function is detached from its context (the empty string object) and then it's invoked. Since you don't reattach the function to anything it has undefined as its context.
This behavior exist to enable code reuse through mix-ins:
var obj1 = {
name: "obj1",
getName: function() { return this.name; }
}
var obj2 = {
name: "obj2",
}
obj2.getName = obj1.getName //now obj2 has the getName method with correct context
console.log(obj2.getName())

String.call.call type? [duplicate]

This question already has answers here:
"object is not a function" when saving function.call to a variable
(3 answers)
a is a function, then what `a.call.call` really do?
(2 answers)
Closed 5 years ago.
This is true (run it in the console in Google Chrome):
typeof String.call.call === 'function'
But calling it as
String.call.call() throws an error saying it's not a function:
Uncaught TypeError: String.call.call is not a function
at :1:13
Why trying to call a function (as per its typeof definition) throws an error saying it's not a function? Is it actually a function or not?
Note this happens to other classes such as Number, Function or others that inherit the .call method.
Let's try to break this down into more manageable pieces.
String is a constructor (or a static function / constructor hybrid, whatever you want to call it), and as such, it inherits from Function.prototype, because it is a function.
call is a function that executes its context (the this) with the context given to it in the first parameter:
console.log(String.call(true));
console.log(Array.call(null));
console.log(Function.call('so?'));
console.log(Object.call(/notice me/));
Let's look at a more official definition for Function.call():
The call() method calls a function with a given this value and arguments provided individually.
function.call(thisArg, arg1, arg2, ...)
Now, the interesting part here is noting that these three are identical:
'use strict';
var callRef = String.call; // Function.call, Array.call, Boolean.call, all of them are ===
console.log(String(/make me into a string/));
console.log(String.call(undefined, /make me into a string/));
console.log(callRef.call(String, undefined, /make me into a string/));
The callRef.call(String) means, call callRef() with the context of String, which is to say, execute String() with the context and parameters provided in the following arguments.
As we recall, the context doesn't really matter, but now we know that the first parameter of callRef.call() does matter, because it determines what function to execute, because it's telling callRef() what its context is, which gets executed as a function with the context provided in the next parameter.
Now reflecting on the initial question, what happens when we attempt to execute String.call.call()? Well, if a parameter is not specified, it is undefined, and we know that typeof undefined !== 'function'.
So here's my final answer:
String.call.call() is indeed a function... but all it's doing is attempting to execute undefined as a function, which obviously isn't.
String.call.call();
I hope this has been an interesting and informative explanation.

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using Function.prototype.apply and .bind.
function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy
This code works, it successfully spies on target.method.
//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here
This code, however, does not. It gives an error message: TypeError: CreateListFromArrayLike called on non-object.
And then the biggest surprise is, this method works perfectly fine.
//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here
So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?
In this version:
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.
You can change it to:
target[method] = function (arg){ //only one argument specified
obj.count++
original.apply(this,[arg]) //one argument passed here
}
Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:
target[method] = function (arg){ //only one argument specified
obj.count++
original.call(this,arg) //one argument passed here
}
Now bind is a totally different animal. It partial applies functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:
var self = this;
return function() {
self.method(a, b);
}
Well. bind does this for you:
return this.method.bind(this, a, b);
When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a partial applied version of that function and does not call it like call or apply does.
bind is called the same way as call is, even though they do very different things.
If you really wanted to use bind in this way. You could use the spread operator (ES2015) to expand the arguments 'array' to individual arguments:
original.bind(null, ...args);
That will bind the original function with the array values as individual arguments.

JavaScript Binding

thank you if you can help. Source of code http://ejohn.org/apps/learn/#84
1) in line 3 of the program below, where it says return context[name] what does this mean? Im guessing that it means name is bound to the context as a result of the apply function? Is that correct?
2)If my guess in 1 is correct, why does it use the [] brackets? Is that just the syntax. When I look at it, it makes me think array or object?
3) When it says apply(context, arguments) is arguments not the same as name or is arguments both context and name together? to put it another way, in the language of the call bind(Button, "click") is arguments only "click" or is it both button and click?
4) I tried to rewrite line 3 by substituting name for arguments like this
return context[name].apply(context, name);
but it didn`t work anymore, which raises the questions
a)if it is returning name bound to context (i.e. context[name]), why isn`t it sufficient to just have apply(context,name)?
b) if arguments includes both name and context, is the third line of the function essentially
return context[name].apply(context, [context, name]);
c) if my assumption in 4(b) is correct, why would we effectively have to have context passed twice in order to bind name to context? which is to say, I dont understand why line 3 doesnt work if you just write apply(context, name) instead of apply(context,arguments)
function bind(context, name){
return function(){
return context[name].apply(context, arguments);
};
}
var Button = {
click: function(){
this.clicked = true;
}
};
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
elem.onclick = bind(Button, "click");
document.getElementById("results").appendChild(elem);
elem.onclick();
assert( Button.clicked, "The clicked property was correctly set on the object" );
Click me!
It may be helpful to understand the basics of JavaScript objects before diving into the specifics. Any JavaScript property can be accessed with the bracket notation, or the dot notation (if it is a valid identifier). It can be confusing since arrays also use this notation. Say there is an object of cars and their makes,
var cars = { Ford: 2007, Honda: 2010, BMW: 2011 };
Then we can access their keys using the dot notation or the bracket notation
cars.Ford // 2007
cars["Honda"] // 2010
Next, remember that functions are first class citizens in JavaScript. So you could use them as ordinary variables including storing them as object property values, in arrays, etc. Let's replace the years in the previous cars example with actual functions,
var cars = {
Ford: function() { alert("2007"); },
Honda: function() { alert("2010"); },
BMW: function() { alert("2011"); }
};
The keys Ford, Honda, and BMW can still be accessed as in the previous example with the dot or bracket notation, with the only difference that this time a function will be returned instead of the integer year.
cars["BMW"] now returns a function which can be directly invoked as
cars["BMW"](); // or
cars.BMW(); // or
var name = "BMW";
cars[name]();
That's not all. There are still two more ways to execute a function - through apply and call. The difference between apply and call is subtle but you should read up more about them.
Finally, arguments represents an array-like object that contains the arguments passed in to a function. This is best demonstrated by an example,
function whatever() {
console.log(arguments);
}
whatever(1, 2); // [1, 2]
whatever("foo", "bar", 23, [4, 6, 8]); // ["foo", "bar", 23, [4, 6, 8]]
whatever(); // undefined
Without giving any names to the function parameters, we were able to log all the arguments passed to the function. Since it is an array like object, access each argument individually as arguments[0], arguments[1], etc.
And now to answer your questions,
1) in line 3 of the program below, where it says return context[name] what does this mean? Im guessing that it means name is bound to the context as a result of the apply function? Is that correct?
context[name] is similar to the cars['Ford'] example above. It is supposed to give a function which is then invoked by calling apply on it. When that function is called, inside the function this will refer to the object - context.
2) If my guess in 1 is correct, why does it use the [] brackets? Is that just the syntax. When I look at it, it makes me think array or object?
Hopefully this was answered above.
3) When it says apply(context, arguments) is arguments not the same as name or is arguments both context and name together? to put it another way, in the language of the call bind(Button, "click") is arguments only "click" or is it both button and click?
arguments has nothing to do with either context or name. It is simply a list of the arguments/parameters that the function was called with. Hopefully the above description cleared this as well.
4) I tried to rewrite line 3 by substituting name for arguments like this
return context[name].apply(context, name);
but it didn`t work anymore
It didn't work because apply expects the second argument to be an Array, and you passed it a String. Try return context[name].apply(context, [name]); instead.
which raises the questions
a) if it is returning name bound to context (i.e. context[name]), why isn`t it sufficient to just have apply(context,name)?
b) if arguments includes both name and context, is the third line of the function essentially
return context[name].apply(context, [context, name]);
arguments has nothing to do with the context, or name. Hopefully this was cleared up in the above examples.
c) if my assumption in 4(b) is correct, why would we effectively have to have context passed twice in order to bind name to context? which is to say, I dont understand why line 3 doesnt work if you just write apply(context, name) instead of apply(context,arguments)
The above answers already answer this part.
1) context[name] just means the property of the "context" object with that name. In the case of:
bind(Button, "click");
that works out to Button["click"], which is the click() function inside the Button object
2) All objects in Javascript are a collection of properties, which can be accessed by their names. Given the definition:
var Button = {
click: function(){
this.clicked = true;
}
};
both Button.click and Button["click"] would refer to the same thing - the function click() inside the Button object.
3) The arguments keyword refers to an array-like object containing all of the arguments passed to a function. In the example, bind() is returning a newly-created function. The "arguments" referred to in that function are whatever arguments that function gets called with. In this case, it's neither context nor name, it's whatever the onclick mechanism passes to the event handler.
Here's a slightly different way to write the code that sets up the event handler:
var elem = document.createElement("li");
elem.innerHTML = "Click me!";
var boundFunction=bind(Button, "click");
elem.onclick=boundFunction;
document.getElementById("results").appendChild(elem);
Maybe this makes it more clear that when you call bind(), it returns a new function. If you were to call the boundFunction like this:
boundFunction("these", "are", "arguments")
The use of arguments is inside the returned function, so arguments would be ["these", "are", "arguments"] in this case. The arguments that were passed to "bind" are used to construct the function that bind returns, so they're no longer relevant when the bound function gets called.
4) Until you understand the basics of how returning a function from another function works, this'll be pretty confusing. The purpose of apply() is to set the "this" keyword for a particular function invocation. Given the definition of Button, you might expect to be able to do this to set up the event handler:
elem.onclick = Button.click;
This doesn't work correctly, because when the event handling code calls the Button.click function, "this" is set to the global context, rather than to the Button instance. The purpose of the bind() function is to make a function that sets "this" appropriately, then calls the function you originally passed to bind().
I have a half-completed blog entry on this which might be a simpler example:
http://codemines.blogspot.com/2010/01/javascript-by-example-functions-and.html

Categories

Resources