console.log.apply not working in IE9
Asked Answered
J

7

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?

Jill answered 4/4, 2011 at 13:13 Comment(0)
H
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

Hathor answered 4/4, 2011 at 13:44 Comment(13)
+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...Alexiaalexin
@TJC: yeah, that's right - I should have mentioned that this code should be targeted towards IE 9 only :-)Hathor
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 @AndyJill
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!Amniocentesis
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.Nestor
@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.Hathor
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.Nestor
I wish I could upvote this answer more than once. Amazingly informative and helpful! Thanks!Sleepless
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. :(Hardy
@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.Hathor
But in Chrome console.log == "object" always returns false, I'm quite wondering if that is correct?Manumission
Please delete the first see also link, it is down and got some malicious stuff in it.Offering
@Offering 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.Hathor
B
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) );
  }
};
Berlyn answered 10/9, 2012 at 19:52 Comment(1)
Thanks, for bringing up his nice approach.Jill
A
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]);
  }
}
Alexiaalexin answered 4/4, 2011 at 13:15 Comment(4)
Exactly. Not everything in JS that is callable is required to be a Function object.Amphictyon
I thought that too, at first. But then again, it wasn't defined before.Jill
@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.Alexiaalexin
@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.Hathor
P
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();
}
Peridium answered 2/2, 2013 at 22:15 Comment(0)
J
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.

Jill answered 4/4, 2011 at 13:34 Comment(1)
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.Alexiaalexin
D
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);
  }
};
Datha answered 21/1, 2013 at 19:42 Comment(0)
T
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.

Travers answered 3/10, 2017 at 12:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.