我在下面的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
答案 0 :(得分:12)
它与值与引用无关,它与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
处理不会踢进去。
答案 1 :(得分:8)
您应该了解内部Reference Type的工作原理。
注意:这不是语言数据类型,是处理引用的内部机制。
引用由两个元素组成,基础对象和属性名称。
在您的示例中,foo.bar
引用如下所示。
// pseudo-code
foo.bar = {
baseObject: foo,
propertyName: 'bar'
}
comma operator和simple 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
)
您可以通过调用foo
或myFunc.call(context, arg1, arg2)
来明确设置上下文。
您的两个示例都是对表达式进行常规调用,其值为myFunc.apply(context, argArray)
因此,foo.bar
是this
。
他们相当于
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
语句,但行为类似。)