47

Looks like I've re-invented the wheel, but somehow this isn't working in Internet Explorer 9, but does in IE6.

function debug()
  if(!window.console) { 
    window.console = { log: function() { /* do something */ } };
  }
  console.log.apply(console, arguments);
}

Related: Apply() question for javascript

F12 Debugger tells me that this "object" (console.log) does not support method 'apply'. Is it not even recognized as a function? Any other pointers or ideas?

Community
  • 1
  • 1
line-o
  • 1,661
  • 3
  • 15
  • 29

7 Answers7

93

The second part of an answer I gave recently answers this question too. I don't consider this a duplicate of that one so, for convenience, I'll paste it here:

The console object is not part of any standard and is an extension to the Document Object Model. Like other DOM objects, it is considered a host object and is not required to inherit from Object, nor its methods from Function, like native ECMAScript functions and objects do. This is the reason apply and call are undefined on those methods. In IE 9, most DOM objects were improved to inherit from native ECMAScript types. As the developer tools are considered an extension to IE (albeit, a built-in extension), they clearly didn't receive the same improvements as the rest of the DOM.

For what it's worth, you can still use some Function.prototype methods on console methods with a little bind() magic:

var log = Function.prototype.bind.call(console.log, console);
log.apply(console, ["this", "is", "a", "test"]);
//-> "thisisatest"

So you could fix up all the console methods for IE 9 in the same manner:

if (Function.prototype.bind && window.console && typeof console.log == "object"){
    [
      "log","info","warn","error","assert","dir","clear","profile","profileEnd"
    ].forEach(function (method) {
        console[method] = this.bind(console[method], console);
    }, Function.prototype.call);
}

This replaces the "host" functions with native functions that call the "host" functions. You can get it working in Internet Explorer 8 by including the compatibility implementations for Function.prototype.bind and Array.prototype.forEach in your code, or rewriting the above snippet to incorporate the techniques used by those methods.

See also

Andy E
  • 311,406
  • 78
  • 462
  • 440
  • 3
    +1 The solution does, of course, introduce a new dependency: `Function#bind`, which one would have to supply on implementations that aren't quite up to ECMAScript5 spec yet... – T.J. Crowder Apr 04 '11 at 13:56
  • @TJC: yeah, that's right - I should have mentioned that this code should be targeted towards IE 9 only :-) – Andy E Apr 04 '11 at 13:59
  • 1
    Actually, it was targeted at IE6. But it helped me a lot by giving me deeper insight and answered my question. So, thank you very much @Andy – line-o Apr 05 '11 at 14:27
  • 1
    Thanks a bunch @Andy, I needed this to get the debugger in my framework working on MSIE. I've put credits in the source, thanks again! – Christian Jun 05 '11 at 19:30
  • 4
    The short way to do this is: `function debug() { Function.prototype.apply.call(console.log, console, arguments); }` which is essentially what this `bind`-ing code does. – Sophie Alpert Aug 14 '11 at 23:45
  • @Ben: "short way"? Character count puts `bind()` a tad shorter than `apply()` :-) However, the main difference is that your method is natively compatible with IE 8, whereas the compatibility implementation of `bind()` would be required for the method outlined in my answer. – Andy E Aug 15 '11 at 08:02
  • 1
    Yes, that's what I meant. :) Also when going through the implementation of `bind`, it wasn't as clear to me what it was doing and how it was avoiding `console.log.apply`; just wanted to post a comment in case anyone else was confused similarly in the future. – Sophie Alpert Aug 15 '11 at 08:10
  • 2
    I wish I could upvote this answer more than once. Amazingly informative and helpful! Thanks! – KOGI Feb 13 '13 at 22:45
  • You have `bind` and `call` reversed compared to your blog post. I *think* you mean to bind to `call` rather than calling `bind`, i.e., the blog post is reversed. Unfortunately, I don't have IE9 here to test. :( – David Harkness Jun 20 '13 at 17:20
  • @DavidHarkness: both work in IE9, but the solution I posted here also works for IE8 and lower with a compatibility implementation of `bind()` :-) See the update at the bottom of my blog post. – Andy E Jun 20 '13 at 23:45
  • But in Chrome `console.log == "object"` always returns `false`, I'm quite wondering if that is correct? – jiyinyiyong Dec 24 '13 at 06:17
  • Please delete the first see also link, it is down and got some malicious stuff in it. – OuuGiii Nov 26 '18 at 12:35
  • 1
    @OuuGiii done. For future reference, you can edit any answer yourself, or suggest edits that are reviewed by other users if your rep is not high enough. – Andy E Nov 27 '18 at 15:10
5

There is also Paul Irish's way of doing it. It is simpler than some of the answers above, but makes log always output an array (even if only one argument was passed in):

// usage: log('inside coolFunc',this,arguments);
// http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if(this.console){
    console.log( Array.prototype.slice.call(arguments) );
  }
};
BishopZ
  • 5,909
  • 7
  • 38
  • 57
2

Several of IE's host object functions aren't really JavaScript functions and so don't have apply or call. (alert, for example.)

So you'll have to do it the hard way:

function debug()
  var index;

  if(!window.console) { 
    window.console = { log: function() { /* do something */ } };
  }
  for (index = 0; index < arguments.length; ++index) {
      console.log(arguments[index]);
  }
}
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • Exactly. Not everything in JS that is callable is required to be a `Function` object. – Tim Down Apr 04 '11 at 13:18
  • I thought that too, at first. But then again, it wasn't defined before. – line-o Apr 04 '11 at 13:28
  • 1
    @line-o: Note that you're using `window.console` in some places, and `console` in others. Now, all else being equal, those should come to the same thing, but this is IE we're talking about and I wouldn't be *at all* surprised if it plays magic games with `console.log`. – T.J. Crowder Apr 04 '11 at 13:47
  • @Tim Down: true, much like the non-standard extension to RegExp instances that makes them callable. @TJC, @line-o: the `console` object doesn't exist until the first time you launch the developer tools for a particular tab. – Andy E Apr 04 '11 at 13:53
1

I came across the same IE trouble and made a routine for it. It is not as fancy as all the above implementations, but it works in ALL modern browsers.

I tested it with Firefox (Firebug), IE 7,8,9 Chrome and Opera. It makes use of the evil EVAL, but you will only want to debug in development. Afterwards you will replace the code with debug = function () {};

So here it is.

Regards, Hans

(function(ns) {
  var msgs = [];

  // IE compatiblity
  function argtoarr (args,from) {
    var a = [];
    for (var i = from || 0; i<args.length; i++) a.push(args[i]);
    return a;    
  }

  function log(arg) {
    var params = "", format = "", type , output,
        types = {
            "number" : "%d",
            "object" : "{%o}",
            "array" : "[%o]"
        };
    for (var i=0; i<arg.length; i++) {
        params += (params ? "," : "")+"arg["+i+"]";
        type = types[toType(arg[i])] || "%s";
        if (type === "%d" && parseFloat(arg[i]) == parseInt(arg[i], 10)) type = "%f";
        format += (format ? "," : "")+type;
    }
    // opera does not support string format, so leave it out
    output = "console.log("+(window.opera ? "" : "'%f',".replace("%f",format))+"%p);".replace("%p",params);
    eval(output);
  }

  ns.debug = function () {
    msgs.push(argtoarr(arguments));
    if (console !== undefined) while (msgs.length>0) log(msgs.shift());
  }

})(window);

Oops forgot my toType function, here it is.

function toType(obj) {
    if (obj === undefined) return "undefined";
    if (obj === null) return "null";
    var m = obj.constructor;
    if (!m) return "window";
    m = m.toString().match(/(?:function|\[object)\s*([a-z|A-Z|0-9|_|@]*)/);
    return m[1].toLowerCase();
}
0

The reason I came to this question was that I as trying to 'spicy' the console.log function for a specific module, so I'd have more localized and insightful debug info by playing a bit with the arguments, IE 9 broke it.

@Andy E answer is great and helped me with lots of insight about apply. I just don't take the same approach to support IE9, so my solution is running the console only on "modern browsers" (being that modern means whatever browsers that behave the way I expect =)

var C = function() {
  var args = Array.prototype.slice.call(arguments);
  var console = window.console;
  args[0]  = "Module X: "+args[0];
  if( typeof console == 'object' && console.log && console.log.apply ){
    console.log.apply(console, args);
  }
};
Fabiano Soriani
  • 6,581
  • 8
  • 40
  • 57
0

Try:

function log(type) {
  if (typeof console !== 'undefined' && typeof console.log !== 'undefined' &&
    console[type] && Function.prototype.bind) {
    var log = Function.prototype.bind.call(console[type], console);
    log.apply(console, Array.prototype.slice.call(arguments, 1));
  }
}
log('info', 'test', 'pass');
log('error', 'test', 'fail');

Works for log, debug, info, warn, error, group or groupEnd.

Shobhit Sharma
  • 564
  • 1
  • 8
  • 18
0

Ok, it works when you write it this way:

function debug()
  if(!window.console) { 
    window.console = {};
    console.log = function() { /* do something */ };
  }
  console.log.apply(console, arguments);
}

Odd behaviour... but if you write it this way 'console.log' gets recognized as a function.

line-o
  • 1,661
  • 3
  • 15
  • 29
  • See my reply to your comment on my answer. You're being inconsistent in a way that should be fine, but I bet IE is playing silly games. – T.J. Crowder Apr 04 '11 at 13:48