Почему я не могу передать console.log в качестве аргумента обратного вызова в Chrome (и Safari)?
Следующий фрагмент выдаст ошибку в Chrome (и Safari), пока он работает в Firefox.
Я ожидаю, что в консоли javascript будет показано 2 числа, но в Chrome я получаю только первое, а затем Uncaught TypeError: Illegal invocation
// a generic promise that return a random float
var makePromise = function() {
return $.Deferred().resolve(Math.random());
}
// This works in all browsers
makePromise().then(function(d) {
console.log(d);
});
// This works in firefox only
makePromise().then(console.log);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Почему это происходит?
Примечание: мой вопрос не совпадает с этим вопросом.
Обновить
Спасибо за комментарии и ответ на использование console.log
в качестве обратного вызова нужно сделать
makePromise().then(console.log.bind(console));
2 ответа
В Chromebook я могу продублировать проблему следующим образом:
function do4(cb){ cb(1); cb(2); cb(3); cb(4); }
do4(console.log)
VM1491:2 Uncaught TypeError: Illegal invocation
at do4 (<anonymous>:2:19)
at <anonymous>:2:12
at Object.InjectedScript._evaluateOn (<anonymous>:905:140)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:838:34)
at Object.InjectedScript.evaluate (<anonymous>:694:21)do4 @ VM1491:2(anonymous function) @ VM1552:2InjectedScript._evaluateOn @ VM1288:905InjectedScript._evaluateAndWrap @ VM1288:838InjectedScript.evaluate @ VM1288:694
Но это прекрасно работает и действительно указывает на проблему:
do4(console.log.bind(console))
VM1491:2 1
VM1491:2 2
VM1491:2 3
VM1491:2 4
Это почему?
В хроме, console
сам по себе возвращает Object
прототипа Console
, смотреть:
console
Console {} memory: MemoryInfo__proto__: Console
Это может показаться странным console
как Object
, но это. console
имеет несколько других менее используемых методов, которые используются не так часто, как console.log
но документированы в документах консоли MDN и документах консоли Chrome
И здесь мы получаем большой Javascript-изм, который может сбить людей с толку:
Методы Javascript являются несвязанными методами. То есть методы не привязаны ни к какому конкретному объекту.
Так console.log
является функцией, но это только функция и не сохраняет привязку this
в console
,
Привязка переменной упоминается внутри кода функции магией this
переменная, которую можно установить с помощью function.bind
или же function.apply
,
когда console.log()
JS выполняет привязку кода функции this
к console
объект. Но когда console.log
просто передается как функция, она не выполняет привязку, так что другой код может использовать ее более гибко. Такое поведение неудобно с console.log и многими другими методами, но в некоторых случаях добавляет необходимую гибкость.
Попробуйте использовать deferred.resolveWith()
// a generic promise that return a random float
var makePromise = function() {
// set `this` to `window.console` ,
// pass arguments within array
return $.Deferred().resolveWith(window.console, [Math.random()]);
}
// This works in all browsers
makePromise().then(console.log)
//.then(function(d) {
// console.log(d);
//});
// This works in firefox only
makePromise().then(console.log);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>