console.log() called on object other than console - javascript

I remember that always when I wanted to pass console.log as a callback parameter to some function, it didn't work unless I used the bind() method to bind console to it.
For example:
const callWithTest = callback => callback('test');
callWithTest(console.log); // That didn't use to work.
callWithTest(console.log.bind(console)); // That worked (and works) fine.
See Uncaught TypeError: Illegal invocation in javascript.
However, recently I noticed that console.log() works fine even when called on object other than console. For example:
console.log.call(null, 'test');
logs 'test'.
When and why did it change? Does the specification say anything about it?

Editor's Draft of Console API used to say:
Logging APIs SHOULD all be callable functions allowing them to be passed as arguments to error handling callbacks, forEach methods, etc.
This is no longer included in the current version of the specification.
I thought that Chrome and Node.js changed it to work like in the specification, but it seems that it worked like that even before it.
I'm still curious when did it change and what was the reason of that.

I don't know when the change was made, but I have an idea about why it didn't work.
Consider the following code
callWithTest = callback => callback('test');
var Demo = function () {this.str = 'demo';}
Demo.prototype.getStr = function () { return this.str;}
demo = new Demo ();
demo.getStr(); // returns 'demo'
callWithTest(demo.getStr); // returns undefined
window.str = 'window';
callWithTest(demo.getStr); // returns 'window'
If you trace the code, you will see that when demo.getStr gets called through another function, this refers to window, and sine str is not defined within window, it returns undefined. If you called it directly or bind with demo, this refers to demo and thus it returns 'demo'.
In nodeJS (v6.6.0), there exists a class called Console within the console module which user can explicitly pipe logs into a file (or whatever stream a user like). According to Node.js v6.6.0 api specification,
console = new Console(process.stdout, process.stderr);
Console does not exist in browser as it isn't necessary. The output of console only exists in a canvas used for debugging, and there are exactly one instance of it. User can't, and should not be able to, pipe output of console to any other places as it will become a serious security issue. Because of this, developers can do something within the log function like var x = this.x || console.x as there is exactly one instance of the console object.

Related

Determine function file/line location given reference in NodeJS?

The fact that I can't think of how to do this makes me think it's some sort of anti-pattern, or impossible. If anyone has any better ideas of how to handle my situation, recommendations always welcome.
I have a legacy codebase and at one point I am retrieving function references and calling the function. These function calls are expected to return a result, but sometimes they are missing their return statement. Currently I throw an error if the returned value is nullish but this only gives me a stacktrace to the engine location that is calling the function, not the function itself.
Is there any way that I can determine the file name/number of the function, or force the function to throw an error such that a stacktrace to that actual function is generated?
There is no such feature in the Javascript language. It's not anti-pattern, just not something that the language supports. There is no requirement that a function have a return statement and there is no way to force it to throw an exception if it doesn't return a value.
Without seeing any of the relevant code, I can offer some suggestions:
Set a breakpoint at the line of your code that initiates the function call. Then, trace into the function in the debugger. You can go as far into it as you need to and each time you go step into a new function call, it will show you the file and line number that you're on. I use this technique regularly when I'm confused by some behavior by some module (either built-in to nodejs or an external module I'm using) and it's not immediately clear how to find the right code on Github for it. I just step into it and can immediately see the code and watch it execute line by line as needed for whatever problem I'm investigating.
Assuming this function you're calling expects some arguments, you can give it some sort of bogus arguments that would hopefully trigger it to throw some sort of exception and you could then see the stack trace from that exception. For example, if it was expecting a non-optional object as an argument, you could pass null and see if that triggers an exception. If it was expecting a callback, you could pass a non-function and see if that triggered an exception.
As for the name of the function, if the function has an actual name (it's not anonymous) and fn is your function reference, then you can do console.log(fn.name) and see if there is a name. You could also examine fn.toString() and see if it reveals the source code of the function. Sometimes it will and if the function is a named function that may show you its name. This won't show you what file it's in, but you could perhaps then grep for something you see in the source to find it.
Here's an example from point #3:
function hello() {
return "hi";
}
// create function reference that points to my function
const fn = hello;
// log info on that function reference
console.log(fn.name);
console.log(fn.toString());

How do I get chrome's console to display a function with its dynamically changed name?

When I do console.log on a function in chrome, it displays it using the function's original name, not the name at the time I called console.log. For example:
var someFunction = function genericFunction() {}
Object.defineProperty(someFunction, 'name', {writable: true});
someFunction.name = 'specificFunction';
console.log(someFunction);
in chrome 43 returns
function genericFunction()
which I find confusing. I'd expect to see
function specificFunction()
Where is it finding the old name of the function? Is there some other property which I need to change to get chrome to accept the new name?
If something in the function throws an error, then chrome's stack trace does display the new function name. This is what I'd expect, but seems inconsistent with the console.log behaviour. Am I missing something?
you'll slap yourself ...
console.log(someFunction.name)
I hope you realise you wont be able to call the function using
genericFunction()
if you want to "alias" (for want of a better term) someFunction to genericFunction, simply
var genericFunction = someFunction;

Get Javascript function name

Is there a way to obtain function's name from outside of it?
Lets say there is a js script on web page that we cannot modificate, just read. The script contains object, which contains objects and functions. Lets say that we want to find function named "HelloWorld".
With firebug, we loop through these objects and methods with a script, which looks something like this
// Parameter is target object.
function getFunctionNames(obj) {
// For each objects / functions
for (var id in obj) {
// Focus only on functions
if (typeof(obj[id]) == "function") {
// Get name of the function.
// console.log("Function: " + obj[id].toString());
// Code above returns a block of code without the name. Example output:
// Function: function(name) { alert("Hello World! Hello " + name + "!"); }
//
// Expected output would be
// Function: HelloWorld
}
}
}
obj[id].toString() returns a block of code instead of a name.
obj[id].name returns an empty string. Anonymous function(?).
I cannot use arguments.callee.name because I cannot modify the target code.
I could just browse objects and functions in firebug or just read source code, but I'm looking a way to do it with Javascript.
Edit
For real world example, head to Youtube and try to get the name of function "setMsg()" from "yt" object via Javascript.
Edit2
Accepting Simon's answer for being kinda closest what I was looking for. It appears that I was seeking variable name, rather than function name. While answer didn't help me on original problem, it surely answered to original question. Paul Draper's comments helped me to right direction.
Thanks!
Use obj.name
Note that arguments.callee returns a function. name is property on every function (though it's empty for anonymous functions), so that's why arguments.callee.name works.
This works for webkit (Chrome and Safari), Firefox, and possibly others. It does not work for IE: function.name not supported in IE.
As mentioned, the function doesn't have any intrinsic name other than the "" it gets from being an anonymous function. Some browsers (Firefox, probably Chrome, maybe others) do however perform some limited form of static analysis to figure out names of declared functions, to help with error stack traces. You can get to it in an relatively cross-browser way by getting setMsg to throw an exception and then parse exc.stack:
// cheat with .% in Firebug; there might be other ways of doing this, I dunno:
yt.setMsg.%m.za.__defineSetter__('a', function() { throw new Error(); });
try { yt.setMsg('a', 'a'); }
catch(e) { alert(e.stack.split('\n')[2].split('#')[0]); }
... On the other hand, this is a pretty terrible hack and dependent on the actual function involved (and if you know the function, you probably know its name already). It does work a bit more reliably when done from inside the function.
If you restrict yourself to just Firefox and are doing this for debug purposes, there are better ways of getting to it. Set devtools.chrome.enabled to true in about:config, open a Scratchpad (Shift+F4), set it to environment: browser, and run the following:
Components.utils.import("resource://gre/modules/jsdebugger.jsm");
window.addDebuggerToGlobal(window);
dbg = new Debugger();
dw = dbg.addDebuggee(content);
f = content.wrappedJSObject.yt.setMsg;
name = dw.makeDebuggeeValue(f).displayName;
dbg.removeDebuggee(content);
alert(name);
In both cases, you will note that it alerts "m.ya" instead of "setMsg", and indeed this is because the function was originally declared as m.ya = function() { ...; }. There is no reason why "setMsg" would be a preferable name, from the point of the browser.

Is it acceptable to add an attribute to a JavaScript function?

Is it acceptable to add an attribute or value to a JavaScript function?
Example:
var f = 1;
function foo (param) {
f++;
}
var fooFunc = foo;
fooFunc.dummy = f;
console.log('fooFunc: ' + fooFunc);
console.log('fooFunc.dummy: ' + fooFunc.dummy);
The above example creates a function (foo), then assigns it to a new variable (fooFunc) and then adds a dummy attribute to fooFunc.
When run, this example prints the text of the function first, and then it prints the expected value (1 in this case). When printing the function, it doesn't show any indication of the dummy value:
fooFunc: function foo(param) {
f++;
}
fooFunc.dummy: 1
JsFiddle here - open the browser's JavaScript console to see the log messages: http://jsfiddle.net/nwinkler/BwvLf/
Why does this work? And where is the dummy attribute stored, and why isn't it printed when I log the function?
Lastly, even if this works, is it a good idea (or an acceptable practice) to use this? I don't want to start an open ended discussion on this, but rather see if there's documented uses of this, or people discouraging this in JavaScript coding guidelines.
Everything except primitives ( null, undefined, number, string, boolean ) in JavaScript are objects. So functions are basically objects.
Objects in JavaScript can have properties and methods, hence functions too.
all functions inherit from Function.prototype and has certain properties ( name, length ) and methods ( .call, .apply ) coming through this chain.
It is sometimes very useful to keep properties attached to the function itself, like cache information, number of invocations etc. There is nothing wrong out in using it this way.
More details : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
Let's have a look at ECMAScript documentation (Which is the standard JavaScript is based on). Here's the 3rd. version of it:
http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf
Go to chapter 15, Native ECMAScript Objects.
15.3 > Function objects.
There's a lot of interesting information there concerning your question, but the first thing worth noticing is that function is an object.
As an object, it has attributes (predefined and that you can assign yourself).
For example, try:
console.log('fooFunc.name: ' + fooFunc.name);
It should display "foo" in your case.
Since it's documented quite well, you can use it as a standard way, though it is not so well-spread and may seem a bit unusual.
Hope this helps.
It is normal object behavior, whether "acceptable" or not.
By using the function keyword you are actually calling the native predefined Function() constructor. Like any object constructor it returns an object after building it. Like any object, the returned object can have properties, including other functions as method properties.
var adder = function(a, b){return a+b};
adder.subtracter = function(a, b){return a-b};
console.log(adder(1,2)); // 3
console.log(adder.subtracter(1,2)); // -1
TIP: if you want to see the adder object and its subtracter method, switch to DOM view from Console view after running the above code in console and then search for "adder". You'll see the object there, and then you can collapse to see what it's made from, including a subtracter object.
Of course, a function object is a special native object, which makes it possible to make calls like this: adder() and actually run some code. The fact that a function object is harder to inspect for custom attached properties, combined with its native special object treats (read built-in restrictive behavior), should give you a hint that, while it's possible, attaching custom properties is not the intended nor a good use of a function object.

Why does window.location.reload need to be wrapped in a function(){}?

I've written some code to add a button to the page:
var myButt = document.createElement('button');
myButt.onclick = window.location.reload;
myButt.innerText = 'Reload';
document.body.appendChild(myButt);
But it throws an error:
TypeError: Type error
Wrapping it in a function fixes the problem:
myButt.onclick = function(){ window.location.reload(); };
But my question is why the former doesn't work?
Executing air.trace(typeof(window.location.reload)); outputs function.
An answer from a high-rep user suggests that it should be possible. It's definitely more succinct.
I am running Adobe AIR 3.6 (which runs Webkit), if that makes a difference.
The this value is incorrect when using it like that.
this turns out to be myButt when you need it to be window.location.
To fix this (hehe), either wrap it as you've done, or bind a new this value to it:
myButt.onclick = window.location.reload.bind(window.location);
I just tested that, and it works on Firefox 23.0.1.
For future reference, he was using the Function.prototype.bind compatibility code found here.
With my code above, he was getting the error
TypeError: instanceof called on an object with an invalid prototype
property
To fix this, I changed line 18 of that compatibility code to the following:
fNOP.prototype = this.prototype || {};
This is so that the prototype gets set to a blank object, because window.location.reload doesn't have a prototype naturally.

Categories