Почему оператор присваивания возвращает значение, а не ссылку?
Я видел приведенный ниже пример на этом сайте и думал, что оба ответа будут 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
, конечно, но поведение похоже.)