为什么赋值运算符返回值而不是引用?

时间:2010-06-15 17:34:49

标签: javascript

我在下面的site中看到了下面的示例,并且认为两个答案都是20而不是返回的10。他写道,逗号和赋值都返回一个值,而不是引用。我不太明白这意味着什么。

我理解它与将变量传递给函数或方法有关,即原始类型通过引用传递给值和对象,但我不确定它在这种情况下是如何应用的。

我也理解上下文和'this'的值(在stackoverflow的帮助之后),但我认为在这两种情况下我仍然会调用它作为方法,foo.bar()这意味着foo是上下文但是它似乎都导致函数调用bar()。

为什么会这样,这是什么意思?

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

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

4 个答案:

答案 0 :(得分:12)

它与值与引用无关,它与this值(如您所怀疑的)有关。在JavaScript中,this完全按 设置函数的方式,而不是它的定义位置。您可以通过以下三种方式之一设置this值:

  1. 使用属性访问符号(通过点分表示法(obj.foo())或括号表示法(obj["foo"]())通过对象属性调用该函数。
  2. 使用with语句通过对象属性调用该函数(实际上只是#1的变体,但值得单独调用,特别是因为源代码中不明显)
  3. 使用功能实例的applycall功能。
  4. 在上面的示例中,您没有执行任何这些操作,因此您最终使用默认的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处理不会踢进去。

答案 1 :(得分:8)

您应该了解内部Reference Type的工作原理。

注意:这不是语言数据类型,是处理引用的内部机制。

引用由两个元素组成,基础对象属性名称

在您的示例中,foo.bar引用如下所示。

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

comma operatorsimple assignment都依赖于获取属性名称的值,这会导致基础对象丢失,因为返回了一个值(这是通过内部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)();与[{1}}不等同的原因的关键在于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();时,实际分配(第4步。)无效,因为(foo.bar = foo.bar)只会获取引用的值并将其放回原位,使用相同的基础对象。

关键是赋值运算符返回(步骤5 步骤3 中获得的值,正如我之前在PutValue伪中所说的那样代码,此内部方法返回,它实际上没有基础对象

答案 2 :(得分:2)

你误解了它。

这两个示例都返回window的{​​{1}}属性,因为它们不会在x上直接调用。

函数内foo关键字的值取决于调用函数的上下文。

在普通的函数调用中(例如this),myFunc()将是全局对象,通常为this
在对象方法调用(例如,window)中,foo.bar()将是调用该函数的对象。 (在这种情况下,this

您可以通过调用foomyFunc.call(context, arg1, arg2)来明确设置上下文。

您的两个示例都是对表达式进行常规调用,其值为myFunc.apply(context, argArray) 因此,foo.barthis

他们相当于

window

答案 3 :(得分:2)

将点运算符视为与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语句,但行为类似。)