将console.log分配给另一个对象(Webkit问题)

时间:2010-04-11 23:51:55

标签: javascript logging console webkit firebug

我想让我的日志记录语句尽可能短,同时防止在不存在时访问控制台;我提出了以下解决方案:

var _ = {};
if (console) {
    _.log = console.debug;
} else {
    _.log = function() { }
}

对我而言,这似乎相当优雅,并且在Firefox 3.6中运行良好(包括保留使console.debugconsole.log更有用的行号)。但它在Safari 4中不起作用。[更新:或在Chrome中。所以问题似乎是Firebug和Webkit控制台之间的区别。]如果我按照

进行上述操作
console.debug('A')
_.log('B');

第一个语句在两个浏览器中都能正常工作,但第二个语句在Safari中生成“TypeError:Type Error”。这只是Firebug和Safari Web开发人员工具如何实现控制台之间的区别吗?如果是这样,那么 Apple的 Webkit就非常烦人了。将控制台功能绑定到原型然后实例化,而不是直接将其绑定到对象,这没有用。

我当然可以从分配给console.debug的匿名函数中调用_.log,但之后我会丢失我的行号。还有其他想法吗?

2 个答案:

答案 0 :(得分:8)

首先,如果console确实未定义(因为它在IE等浏览器中),则会出现错误。您应该将其检查为全局对象的属性,即浏览器中的window。一般来说,在使用它之前测试一个功能也是个好主意,所以我为debug方法添加了一个测试。

Safari中console.debug的实施可能依赖于this作为console的引用的价值,如果您使用_.log调用它,情况就不会如此(this将改为_)的引用。完成快速测试后,情况确实如此,以下问题解决了这个问题:

var _ = {};
if (typeof window.console != "undefined"
       && typeof window.console.debug == "function") {
    _.log = function() {
        window.console.debug.apply(window.console, arguments);
    }
} else {
    _.log = function() { }
}

答案 1 :(得分:0)

我一直在寻找解决方案(这就是我发现你的问题的方法)。

Tim指出,在这种情况下,webkit浏览器(Safari,Chrome)依赖于this console。但是,Firefox没有。所以在FF中你可以重新分配函数并保留行号(否则所有日志看起来都像是在日志函数中发起的,这不是很有用)。检查您的浏览器的最佳方法是执行此操作并检查结果。以下是如何检查它(在Coffeescript中):

# Check to see if reassigning of functions work
f = console.log
assignSupported = true
try
  f('Initializing logging...')
catch e
  assignSupported = false

稍后,当您执行这些功能时,请检查assignSupported并采取相应措施:

levels =
  ERROR: 1
  WARN:  2
  LOG:   3
  INFO:  4
  DEBUG: 6

log.setLevel = (newLevel) ->
  for label, level of levels
    if level > newLevel # Skip low levels
      continue

    name = label.toLowerCase()
    f = -> # Fallback - empty function. In Js: var f = function () {}
    if console?[name]
      if assignSupported
        f = console[name] # Wee, we'll have line numbers.
      else
        # Webkit need the this of console.log (namely, console)
        # preserved, so we use a wrapper.
        #
        # Calling console[name] within the returned wrapper
        # makes [name] a subject of the closure, meaning
        # that it's the last value in the iteration -
        # we need to preserve it.
        f = ((n) ->
          return (-> console[n].apply(console, arguments)))(name)
    log[name] = f

log.setLevel levels.DEBUG

行:

f = ((n) ->
  return (-> console[n].apply(console, arguments)))(name)

可能看起来有点奇怪。这是因为name是循环变量并且是词法绑定的,这意味着将使用执行时的值,这将始终是最后的level。它编译为这个javascript(如果它更容易阅读):

f = (function(n) {
  return (function() {
    return console[n].apply(console, arguments);
  });
})(name);