Почему я не могу передать 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>

Другие вопросы по тегам