最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

firefox - How to assign builtin methods on console and document to variables in javascript? - Stack Overflow

programmeradmin2浏览0评论

I'm trying to implement conditional assignment of the console.log() method to a variable as follows:

var dbglevel = 1;
var dbg = (dbglevel > 0) ? console.log : function(){};

dbg('message'); // throws TypeError

TypeError: 'log' called on an object that does not implement interface Console.

It used to work but something changed in Firefox 30. Now I have no idea if it was ever supposed to work. The reason I have my doubts is that I've seen the same thing with the document object. Compare the results of these two function assignments, the first is a function wrapper and the second is direct assignment to the method:

function qs1(q) { return document.querySelector(q); }; // wrapper
qs1('head'); // works

var qs2 = document.querySelector;
qs2('head'); // throws TypeError

TypeError: 'querySelector' called on an object that does not implement interface Document.

What am I seeing here? Why does direct assignment of the method to a variable break its "interface" to it's parent object?

My reason for wanting to do this is twofold:

1.) The assignment syntax is shorter and I don't need to worry about declaring arguments, 2.) More importantly, I want my dbg() calls to report the correct file and line number to the console. If the function is a wrapper the console always shows the line number of the console.log call in that wrapper. I don't want to emulate the line number logging because the normal method of calling console.log directly gives you a clickable link to view source centered on the line that called it.

I'm not looking for a workaround involving plugins like FireBug, pre-processing (LESS/SASS), or third-party scripts. The solution only needs to work on vanilla Firefox 30 or later and the specific problem I'm trying to solve is how to compress the following code on every line I want to conditionally log:

if (typeof cfg.DEBUG != 'undefined' && cfg.DEBUG > 2) console.log(something);

... to this ...

dbg(something);

... where the dbg() function does any appropriate conditional evaluation and then shows the same line number as if I had called console.log directly.

I'm trying to implement conditional assignment of the console.log() method to a variable as follows:

var dbglevel = 1;
var dbg = (dbglevel > 0) ? console.log : function(){};

dbg('message'); // throws TypeError

TypeError: 'log' called on an object that does not implement interface Console.

It used to work but something changed in Firefox 30. Now I have no idea if it was ever supposed to work. The reason I have my doubts is that I've seen the same thing with the document object. Compare the results of these two function assignments, the first is a function wrapper and the second is direct assignment to the method:

function qs1(q) { return document.querySelector(q); }; // wrapper
qs1('head'); // works

var qs2 = document.querySelector;
qs2('head'); // throws TypeError

TypeError: 'querySelector' called on an object that does not implement interface Document.

What am I seeing here? Why does direct assignment of the method to a variable break its "interface" to it's parent object?

My reason for wanting to do this is twofold:

1.) The assignment syntax is shorter and I don't need to worry about declaring arguments, 2.) More importantly, I want my dbg() calls to report the correct file and line number to the console. If the function is a wrapper the console always shows the line number of the console.log call in that wrapper. I don't want to emulate the line number logging because the normal method of calling console.log directly gives you a clickable link to view source centered on the line that called it.

I'm not looking for a workaround involving plugins like FireBug, pre-processing (LESS/SASS), or third-party scripts. The solution only needs to work on vanilla Firefox 30 or later and the specific problem I'm trying to solve is how to compress the following code on every line I want to conditionally log:

if (typeof cfg.DEBUG != 'undefined' && cfg.DEBUG > 2) console.log(something);

... to this ...

dbg(something);

... where the dbg() function does any appropriate conditional evaluation and then shows the same line number as if I had called console.log directly.

Share Improve this question asked Jun 21, 2014 at 7:28 SpliFFSpliFF 39k16 gold badges93 silver badges121 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 24

There is a simple workaround:

var dbglevel = 1;
var dbg = (dbglevel > 0) ? function(msg){console.log(msg);} : function(){};

dbg('message'); // prints message

By the way, assigning a native function to a var throws TypeError in Chrome as well. The problem is binding: when you are aliasing functions like you did, they are called on the global object, and instead you need to have them bound to console or document in turn.

So the right way to your aliasing is like:

var dbg = console.log.bind(console);

or

var qs2 = document.querySelector.bind(document);

Assuming you are running ES5 at least. So, if you need back compatibility, you'd probably want to use something like the workaround above (perhaps something more elaborate to take into account a variable number of arguments, using apply and the arguments object).

If you are sure you have access to ES5 features, use:

var dbglevel = 1;
var dbg = (dbglevel > 0) ? console.log.bind(console) : function(){};

dbg('message'); // prints message

The answer of @mir is very good, and I just want to expand it with a bit of an explanation useful specially to a newbie or less experienced programmer that might miss a key point in the suggested code, which I quote here for reference: (note that, assuming ES5, I am just changing the use of var with let and const, which I would prefer in that case)

let dbglevel = 1;
const dbg = (dbglevel > 0) ? console.log.bind(console) : function(){};

dbg('message'); // prints message

So:

dbg('I am a debug message');
// --> I am a debug message

dbglevel = -1 // or dbglevel = 0, or whatever number, actually

dbg('I am NOT a debug message'); // or whatever text, it will print it
// --> I am NOT a debug message

When dbg was declared, it was dbglevel === 1; so at that moment, dbg was bound to console.log, with that value of dbglevel closed. Because of the closure of dbglevel, the "else" alternative function is not ever going to be invoked, regardless of any future change of dbglevel. The dbg function will print to console all subsequent messages that it receives as argument.

Conversely, if we would have defined dbg something like this:

let dbglevel;
const dbg = (dbglevel > 0) ? console.log.bind(console) :
    function(){console.log(`Sorry, debuglevel is ${dbglevel}`);};

...then dbg would always invoke the "else" function and print the 'sorry' message, including the current value of dbglevel, because the first alternative has a closure where dbglevel === undefined. The important difference with the previous example, is that the "else" function does not close the value of dbglevel, so it actually reads whatever is the current value of dbglevel:

dbglevel > 0
// --> false
dbg('I am a debug message');
// --> Sorry, debuglevel is undefined

dbglevel = 3 // or dbglevel = -99, or actually any number

dbg('I am a debug message');
// --> Sorry, debuglevel is 3

[Please note: because I don't have yet enough privileges in stackoverflow, I am not able to write or answer comments.]

发布评论

评论列表(0)

  1. 暂无评论