Почему оператор присваивания возвращает значение, а не ссылку?

Я видел приведенный ниже пример на этом сайте и думал, что оба ответа будут 20, а не 10, которые возвращаются. Он написал, что и запятая, и присваивание возвращает значение, а не ссылку. Я не совсем понимаю, что это значит.

Я понимаю это в отношении передачи переменных в функции или методы. При этом примитивные типы передаются по значению и объектам по ссылке, но я не уверен, как это применимо в этом случае.

Я также понимаю контекст и значение 'this' (после справки из stackoverflow), но я думал, что в обоих случаях я все равно буду ссылаться на него как на метод foo.bar(), который будет означать, что foo - это контекст, но Кажется, что оба они приводят к вызову функции().

Почему это и что все это значит?

var x = 10;
var foo = {
  x: 20,
  bar: function () {return this.x;}
};

(foo.bar = foo.bar)();//returns 10
(foo.bar, foo.bar)();//returns 10

Ответ 1

Это не связано со значениями по сравнению с ссылками, это связано с значениями this (как вы подозревали). В JavaScript this полностью определяется тем, как вызывается функция, а не там, где она определена. Вы устанавливаете значение this одним из трех способов:

  • Вызвать функцию через свойство объекта, используя нотацию атрибута свойства, либо пунктирную нотацию (obj.foo()), либо скобленое обозначение (obj["foo"]()).
  • Вызвать функцию через свойство объекта с помощью инструкции with (на самом деле это просто вариант # 1, но стоит вызывать отдельно, особенно, поскольку это не очевидно из исходного кода)
  • Используйте функции apply или call экземпляра функции.

В приведенных выше примерах вы ничего не делаете, поэтому вы вызываете функцию со значением по умолчанию this, глобальным объектом, и поэтому x приходит оттуда, а не из вашего foo объект. Здесь еще один способ подумать о том, что делает этот код:

var f = foo.bar; // Not calling it, getting a reference to it
f();             // Calls the function with `this` referencing the global object

Если вы не используете прямое свойство для фактического выполнения вызова (вместо этого возвращаете значение свойства и затем выполняете вызов с ним), обработка this не срабатывает.

Ответ 2

Вы должны понимать, как работает внутренний Reference Type.

Примечание: Это не тип данных на языке, является внутренним механизмом для обработки ссылок.

Ссылка состоит из двух элементов: базового объекта и имени свойства.

В вашем примере ссылка foo.bar выглядит следующим образом.

// pseudo-code
foo.bar = {
  baseObject: foo,
  propertyName: 'bar'
}

Оба, оператор запятой и простое назначение, полагайтесь на получение значения имени свойства, что приводит к потере базового объекта, поскольку возвращается одно значение (это делается через внутренний GetValue).

Так работает внутренняя операция GetValue:

// pseudo-code
GetValue(V) :
  if (Type(V) != Reference) return V;

  baseObject = GetBase(V); // in your example foo
  if (baseObject === null) throw ReferenceError;

  return baseObject.[[Get]](GetPropertyName(V)); 
  // equivalent to baseObject[v.PropertyName];

Как вы видите, возвращается значение, поэтому исходная ссылка теряется.

Изменить: Ключ, чтобы понять, почему (foo.bar = foo.bar)(); не эквивалентен foo.bar();, зависит от Simple Assignment Оператор, посмотрим на алгоритм:

11.13.1 Simple Assignment (`=`)
The production `AssignmentExpression` :
               `LeftHandSideExpression` = `AssignmentExpression`

is evaluated as follows:

1. Evaluate LeftHandSideExpression.

2. Evaluate AssignmentExpression.

3.Call GetValue(Result(2)).

4.Call PutValue(Result(1), Result(3)).

5.Return Result(3).

В основном, когда вы делаете (foo.bar = foo.bar), фактическое присвоение (Шаг 4.) не имеет эффекта, потому что PutValue получит только значение ссылки и вернет его с той же базой объект.

Ключ состоит в том, что оператор присваивания возвращает (Шаг 5) значение, полученное в Шаг 3, и, как я уже говорил в псевдокоде GetValue этот внутренний метод возвращает значение, которое на самом деле не имеет базового объекта.

Ответ 3

Ты не понимаешь этого.

Оба примера возвращают свойство window x, так как они не вызываются непосредственно на foo.

Значение ключевого слова this внутри функции зависит от контекста, в котором была вызвана функция.

В обычном вызове функции (например, myFunc()) this будет глобальным объектом, обычно window.
В вызове метода объекта (например, foo.bar()) this будет объектом, на который была вызвана функция. (в этом случае foo)

Вы можете установить контекст явно, вызывая myFunc.call(context, arg1, arg2) или myFunc.apply(context, argArray).

Оба из ваших примеров - это обычные вызовы выражения, которое оценивается как foo.bar.
Следовательно, this является window.

Они эквивалентны

var func = (some expression);
func();

Ответ 4

Это может помочь думать о том, что оператор точки работает так же, как и оператор with. Когда вы запустите foo.bar(), результат будет таким же, как если бы вы запустили:

with (foo) {
    bar(); // returns 20
}

Это запустит вашу функцию bar с помощью foo, наложенной поверх глобального объекта, позволяя ей найти x в foo и, таким образом, вернуть 20.

Если вы не вызываете bar сразу, хотя, как и в (foo.bar = foo.bar), вы получаете:

with (foo) {
    bar; // returns "bar" itself
}

Затем результат выводится из круглых скобок, создавая промежуточный оператор типа <reference to bar>(), который не имеет оператора-точки, поэтому оператор with не имеет доступа, поэтому не имеет доступа к foo, просто к глобальному значению x.

(Точечный оператор фактически не преобразовывает в оператор with, конечно, но поведение похоже.)