4

Like all programmers, I'm lazy. So in my utils.js there's a simple line:

window.log = console.log

This works fine in firefox, but it makes Chrome cry like a little boy. I have to write console.log to make it work.

Any suggestions?

CamelCamelCamel
  • 5,200
  • 8
  • 61
  • 93
  • 6
    `window.log = function(x) { console.log(x); };` – nnnnnn Mar 08 '12 at 03:14
  • 4
    @nnnnnn: Almost. `console.log` is variadic and you're only paying attention to the first argument. – mu is too short Mar 08 '12 at 03:59
  • @muistooshort - That's why I posted it as a comment and not an answer: at the time I didn't have time to write the full solution with `.apply()` and I knew somebody would call me on it. Plus I almost always use `console.log()` with a single argument so for my own use I probably wouldn't bother dealing with more. (I've upvoted your answer since you did write it all out.) – nnnnnn Mar 08 '12 at 05:04

2 Answers2

17

Chrome's console.log apparently pays attention to what this is so if you do this:

window.log = console.log
log('pancakes')

Then you'll get nothing more than a "TypeError: Illegal invocation" exception for your efforts. However, if you force the appropriate context thusly:

log.apply(console, 'pancakes')

then you'll get your pancakes in the console. That's why you need to wrap console.log in a function if you want to be lazy and just say log: some console.log implementations need to be called with the appropriate context.

However, just window.log = (x) -> console.log(x) is not quite correct as console.log is a variadic function. A better implementation would be this:

window.log = -> console.log.apply(console, arguments)

or to be pedantic, since arguments isn't an array and Function#apply expects an array, "cast" arguments to a real array in the usual way:

window.log = -> console.log.apply(console, Array::slice.call(arguments))

That should work the same everywhere and preserve the variadic nature of console.log. I doubt you need to be that pedantic though, just sending arguments in and pretending it is an array should be fine. You could also use CoffeeScript splats as a short form of the [].slice and arguments stuff:

window.log = (args...) -> console.log(args...)

If you look at the JavaScript version of that you'll see that it is the slice and arguments stuff in disguise.

Of course if you're using plain JavaScript, then one of these will work:

window.log = function() { console.log.apply(console, arguments) };
window.log = function() { console.log.apply(console, Array.prototype.slice.call(arguments)) };

Another option is to use Function#bind to force log to be called in the context of console:

window.log = console.log.bind(console)

The downside is that not every browser supports bind.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 1
    Do you know if all browsers treat the `console` methods as JS functions that you can use `.apply()`, `.call()` or `.bind()` on? I had a vague idea there was at least one browser where that wouldn't work but I can't remember where I read that and I suspect I might be confusing it with something else like `alert()`. – nnnnnn Mar 08 '12 at 05:09
  • 1
    @nnnnnn: Firefox, Safari, Chrome, and Opera allow it. I don't feel like dragging my Windows box out of the dungeon (i.e. my closet) to check IE. – mu is too short Mar 08 '12 at 05:23
1

You have to define window.log as a function that contains console.log

window.log = function(x) { console.log(x); }
bretterer
  • 5,693
  • 5
  • 32
  • 53