Назначение console.log другому объекту (проблема с Webkit) - PullRequest
7 голосов
/ 12 апреля 2010

Я хотел сделать свои записи в журнале как можно короче, не допуская доступа к консоли, когда она не существует; Я придумал следующее решение:

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

Мне это кажется довольно элегантным и прекрасно работает в Firefox 3.6 (включая сохранение номеров строк, которые делают console.debug более полезным, чем console.log). Но это не работает в Safari 4. [Обновление: или в Chrome. Таким образом, проблема заключается в разнице между Firebug и консолью Webkit.] Если я последую выше с

console.debug('A')
_.log('B');

первый оператор отлично работает в обоих браузерах, но второй генерирует «TypeError: Type Error» в Safari. Разница только в том, как Firebug и Safari Web Developer Tools реализуют консоль? Если это так, то это ОЧЕНЬ раздражает со стороны Apple Webkit. Привязка функции консоли к прототипу и последующее создание экземпляра, а не привязка его непосредственно к объекту, не помогает.

Я мог бы, конечно, просто вызвать console.debug от анонимной функции, назначенной на _.log, но тогда я потерял бы свои номера строк. Есть другие идеи?

Ответы [ 2 ]

8 голосов
/ 12 апреля 2010

Во-первых, если console действительно не определено (как в браузерах, таких как IE), вы получите ошибку. Вместо этого вы должны проверить его как свойство глобального объекта, который в браузерах равен window. В общем, также неплохо проверить функцию перед ее использованием, поэтому я добавил тест для метода debug.

Возможно, реализация console.debug в Safari полагается на его значение 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() { }
}
0 голосов
/ 24 июня 2012

Я сам искал решение этой проблемы (вот как я нашел ваш вопрос).

Как отметил Тим, браузеры 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);
...