views:

323

answers:

3

I have some Firebug console function calls that I wanted to disable when Firebug wasn't enabled, e.g. console isn't defined. This works fine in IE6 and FF3, but not in Chrome:

var log;

if(console){
  log = console.log;
}else{
  log = function(){ return; }
}

I get an "Uncaught TypeError: Illegal Invocation" in Chrome =/

I read about the issue here, where you have to apply a context, which is kind of new to me... and I can't seem to figure how to accomplish the above in all browsers...

+10  A: 

Yes, you should persist the context :

var log;

if (window.console && typeof console.log === "function"){
  // use apply to preserve context and invocations with multiple arguments
  log = function () { console.log.apply(console, arguments); };
} else {
  log = function(){ return; }
}

What is happening is that the context (the this value), is implicitly set when you call a function, for example:

var obj = {
  method: function () { return this; }
};

obj.method() === obj; // true

In this case, you are calling a function that is defined as a property of an object, when the function is invoked, the this value is set to that object.

Now as in your example, if you copy a reference of that method to a variable:

var method = obj.method;
method() === window; // global object

As you can see, the this value refers to the global object.

So, to avoid this implicit behavior you can set the context explicitly, with the call or apply functions.

CMS
`if (console)` will cause a "console is not defined" error, if it's not defined for the browser or if it's disabled. You're better of checking `(window.console)` or even `(typeof console != "undefined)"`.
Ates Goral
CMS
yeah I do check for typeof, i was just simplifying since that really isnt the issue
codeninja
kim3er
+1  A: 

This doesn't work:

log("hi");

While this does:

log.call(console, "hi");

It is obvious that you need to call the aliased function with the correct context -- as you yourself have mentioned.

I think you'll have to use a function wrapper (a closure that has a reference to the original context) rather than an alias...

Update

Also note that if you check for console directly, you may get a run-time error when the variable doesn't exist. You're better off checking it explicitly as window.console. Here's one way to implement a conditional log wrapper:

var log = (function (console) {
    return console
        ? function () { console.log.apply(console, arguments); }
        : function () {}
})(window.console);
Ates Goral
+1  A: 

This solution modifies the earlier and excellent answer from CMS to work with IE8. You’ll need to open the IE8 console (press F12) before executing this. (If you forget, you’ll need to exit IE8 entirely and start again because even if the console exists, IE8 won’t subsequently create the console object.)

Note that we don’t set the context, which was the original problem but, as it turns out, IE8 doesn’t require that context. (Good thing, because IE8 also doesn’t provide the apply method on the console.log object!).

This code works with the latest versions of Chrome, FireFox, and MSIE. (It is compatible with MSIE6 and doesn’t throw an error.)

if((typeof console !== "undefined") && ((typeof console.log) !== "undefined"))
{
  if ((typeof console.log.apply !== "undefined"))
  {
    log = function() { console.log.apply(console,arguments) };
  }
  else
  {
    log = console.log;
  }
}
else
{
  log = function() {};
  // alert("No debug console");
}
danorton