Как JavaScript определяет, когда дать вызов функции в контексте "this"?

По понятным причинам, в JavaScript, следующие два вызова отличаются:

foo.bar();

var bar = foo.bar;
bar();

А именно, при первом вызове this является объектом foo. Во-вторых, это ссылка на глобальную сферу. Однако следующий пример немного менее интуитивно понятен:

(foo.bar)();

Я бы ожидал, что он будет работать так же, как второй пример, но он фактически работает так же, как и первый. То есть this ссылается foo, а не глобальная область.

Каковы правила JavaScript для принятия решения о том, как сделать вызов функции "вызовом метода" и когда просто вызвать функцию без определенного this?

EDIT:

Как Феликс Клинг указывает в комментарии, мне интересно, почему третий пример не использует контекст window, когда теоретически он должен просто получить и вызывать его без дополнительного контекста. Его пример немного разъясняет мой вопрос:

(true && foo.bar)(); // 'this' refers to the global scope

Ответы

Ответ 1

Это сложная задача и сводится к внутренней работе стандарта ECMAScript. Определение оператора группировки:

Производственное первичное выражение: (выражение) оценивается следующим образом:

  • Возвращает результат вычисления выражения. Это может быть тип Reference.

С добавленным примечанием:

Этот алгоритм не применяет GetValue к результату оценки выражения. Главной мотивацией для этого является то, что операторы, такие как delete и typeof, могут применяться к заключенным в скобки выражениям.

Итак, это ключ: результат может иметь тип Ссылка. Ссылка - это внутренний тип данных, который состоит из базового значения и ссылочного имени.

Например, оценивая выражение-член foo.bar, получается ссылка с базовым значением foo (объект) и ссылочное имя "bar" (просто строковое представление идентификатора).

GetValue(ref) - это внутренняя функция, которая фактически обращается к свойству объекта и возвращает значение свойства (объект функции в этом Примеры). Большинство операторов, вызывающих GetValue в своих операндах, разрешают эти ссылки, но не оператор группировки.


Посмотрите, как Вызовы CallExpressions могут также дать представление о том, как работают this и ссылки. Например, один шаг:

Пусть thisValue является результатом вызова конкретного метода ImplicitThisValue GetBase (ref).

Итак, если у вас есть значение Reference и попытайтесь вызвать его, значение this будет установлено на базовое значение Reference (foo в приведенном выше примере).


Что касается моего примера (true && foo.bar)();: Оператор && вызывает GetValue() для обоих своих операндов, поэтому результат группировки оператор не является ссылкой.

Ответ 2

Все, что есть в форме foo.bar(), вызывает метод bar объекта foo.

Однако в случае var bar = foo.bar; bar(); вы создаете ссылку на этот метод, а затем вызываете метод без контекста - поэтому ему присваивается глобальный контекст по умолчанию.

Вы можете (кроме старого IE) сделать это: var bar = foo.bar.bind(foo); bar();, чтобы функция использовала контекст foo.