Почему не работает псевдоним функций JavaScript?

У меня есть некоторые вызовы функций консоли Firebug, которые я хотел отключить, когда Firebug не был включен, например. консоль не определена. Это отлично работает в IE6 и FF3, но не в Chrome:

var log;

if(console){
  log = console.log;
}else{
  log = function(){ return; }
}

Я получаю «Uncaught TypeError: Illegal Invocation» в Chrome =/

Я прочитал о проблеме здесь, где вам нужно применить контекст , что для меня в новинку... и я не могу понять, как сделать это во всех браузерах...


person qodeninja    schedule 16.04.2010    source источник
comment
Я должен отметить, что этот вопрос расширил мое понимание javascript!   -  person qodeninja    schedule 22.04.2010


Ответы (5)


Да, вы должны сохранить контекст:

var log;

if (window.console && typeof console.log === "function"){
  // use apply to preserve context and invocations with multiple arguments
  log = function () { console.log.apply(console, arguments); };
} else {
  log = function(){ return; }
}

Происходит то, что контекст (значение this) неявно устанавливается при вызове функции, например:

var obj = {
  method: function () { return this; }
};

obj.method() === obj; // true

В этом случае вы вызываете функцию, которая определена как свойство объекта, когда функция вызывается, значение this устанавливается для этого объекта.

Теперь, как и в вашем примере, если вы скопируете ссылку на этот метод в переменную:

var method = obj.method;
method() === window; // global object

Как видите, значение this относится к глобальному объекту.

Таким образом, чтобы избежать этого неявного поведения, вы можете явно задать контекст с помощью call или apply.

person Christian C. Salvadó    schedule 16.04.2010
comment
if (console) вызовет ошибку "консоль не определена", если она не определена для браузера или отключена. Лучше проверить (window.console) или даже (typeof console != "undefined)". - person Ates Goral; 16.04.2010
comment
@Ates, да, ReferenceError будет создано, если console не определено, if (window.console && typeof console.log === "function") это безопаснее, отредактировано. - person Christian C. Salvadó; 16.04.2010
comment
да, я проверяю typeof, я просто упрощал, так как это действительно не проблема - person qodeninja; 17.04.2010
comment
Также стоит упомянуть, что консоль не только определяется FireBug, но также существует в IE8 и Chrome. - person kim3er; 18.04.2010
comment
Забавная история, теперь я полностью понимаю все эти забавные вещи. - person qodeninja; 14.01.2013
comment
Я думаю, стоит отметить, что в этом ответе apply() вызывается внутри замыкания, что делает его ненужным, поскольку замыкание захватит console в свой собственный контекст выполнения. В этом ответе достаточно вызвать console.log( arguments ); в анонимной функции. Я думаю, что более четкая альтернатива — опустить замыкание и использовать bind() вместо apply(): log = console.bind( console ); - person sethro; 27.02.2014

Проблема с обертыванием функции (например, console.log) в функцию заключается в том, что она теряет свой контекст, т. е. не показывает правильный номер строки файла, в который мы поместили наш ярлык «журнал».

Вместо этого я предлагаю что-то вроде этого:

 window.log = ((window.console && window.console.log) ?
              console.log.bind(console) : 
              function(){});

Это работает с инструментами Firebug и Chrome dev и не выдает ошибок, когда консоль недоступна. И, самое главное, показывает правильный номер файла и строки.

person Dziad Borowy    schedule 21.06.2012
comment
Большой! Единственный ответ сохраняет номер строки. - person Daniel Ribeiro; 11.11.2012
comment
Я искал ЧАСЫ, пытаясь найти способ сделать это и сохранить имя файла и номер строки консоли. Это работает отлично! - person Justin; 09.01.2013

Это не работает:

log("hi");

Пока это делает:

log.call(console, "hi");

Очевидно, что вам нужно вызывать функцию с псевдонимом в правильном контексте, как вы сами упомянули.

Я думаю, вам придется использовать оболочку функции (закрытие, имеющее ссылку на исходный контекст), а не псевдоним...

Обновлять

Также обратите внимание, что если вы проверите console напрямую, вы можете получить ошибку времени выполнения, когда переменная не существует. Вам лучше проверить это явно как window.console. Вот один из способов реализации условной оболочки log:

var log = (function (console) {
    return console
        ? function () { console.log.apply(console, arguments); }
        : function () {}
})(window.console);
person Ates Goral    schedule 16.04.2010

Это решение изменяет предыдущий и отличный ответ от CMS для работы с IE8. Вам нужно будет открыть консоль IE8 (нажмите F12) перед выполнением этого. (Если вы забудете, вам придется полностью выйти из IE8 и начать заново, потому что даже если консоль существует, IE8 впоследствии не создаст объект console.)

Обратите внимание, что мы не устанавливаем контекст, что было первоначальной проблемой, но, как оказалось, IE8 не требует этого контекста. (Хорошо, потому что IE8 также не предоставляет метод apply для объекта console.log!).

Этот код работает с последними версиями Chrome, FireFox и MSIE. (Он совместим с MSIE6 и не выдает ошибку.)

if((typeof console !== "undefined") && ((typeof console.log) !== "undefined"))
{
  if ((typeof console.log.apply !== "undefined"))
  {
    log = function() { console.log.apply(console,arguments) };
  }
  else
  {
    log = console.log;
  }
}
else
{
  log = function() {};
  // alert("No debug console");
}
person danorton    schedule 04.09.2010

я сделал это

var log;

log = function() {
  if ((window.console != null) && (window.console.log.apply != null)) {
    return console.log.apply(console, arguments);
  } else {
    return function() {};
  }
};
person Lordking    schedule 22.06.2012