2010-06-15 109 views
11

我看到下面这个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 
+1

'(foo.bar = foo.bar)()'是如此** **参加我的面试问题清单! ^。^ – 2010-06-15 21:18:34

+0

@Ben:不是'(foo.bar,foo.bar)()'? ;-)我的意思是,如果你想深奥... – 2010-06-15 23:21:40

+9

@Ben - 让我知道你的工作对象,所以我不会犯错采访:) – screenm0nkey 2010-06-16 09:12:12

回答

11

它不具有值与参考文献进行,它与this值做(因为你怀疑)。在JavaScript中,this设置为,完全由决定,函数如何被调用,而不是它被定义的地方。您可以设置三种方式之一this值:

  1. 呼叫通过使用属性访问符号的对象属性的功能,可以通过点表示法(obj.foo())或括号表示法(obj["foo"]())。
  2. 呼叫通过使用with语句中的对象属性的功能(其实只是#1变体,但值得呼唤独立,特别是因为它不是很明显从源代码)
  3. 利用的applycall功能函数实例。

在上面你的例子,你没有做任何这些,所以你最终调用该函数使用默认值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

它与引用类型有关,因为逗号运算符和简单赋值在内部都使用'GetValue',这会导致引用的*基对象*丢失。 – CMS 2010-06-15 17:51:42

+0

@CMS:对,这是我描述的行为背后的基本机制。我不认为需要钻研那些深度。 (我没有谈论[[Call]]方法或[[Scope]]属性,或者:-))。 – 2010-06-15 18:11:51

+0

@TJ&CMS - 随时按照技术要求进行回复。如果我不明白,我会问你一些问题。 另外CMS的回应对我来说更有意义,因为我仍然不明白为什么(foo.bar = foo.bar)()不等同于foo.bar()。 在你的例子中,你只使用f = foo.bar,这对我来说很有意义,因为f()是一个函数调用,但是如果它是f.bar = foo.bar,那么对我来说就等同于f.bar(),这意味着f将'this' – screenm0nkey 2010-06-15 20:50:05

2

你误会了。

这两个示例都返回windowx属性,因为它们没有在foo上直接调用。

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

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

您可以通过调用myFunc.call(context, arg1, arg2)myFunc.apply(context, argArray)明确地设置上下文。

这两个例子都是对表达式的正常调用,其值为foo.bar。因此,thiswindow

它们等同

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

我明白你写的所有内容,包括你的例子但在我的头(foo.bar = foo.bar)仍然等于foo.bar()而不是bar(),因为你告诉我(我知道这是正确的)。你们,CMS和TJ都给了我很好的回应,但我需要让自己的头脑清醒,因为我有点简单。 – screenm0nkey 2010-06-15 21:18:45

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)();不等同于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伪码,此内部方法返回一个其没有按” t确实有一个基本对象

+0

这会教我。我对T.J.说。 Crowder将它视为技术性的,但现在我无法理解它。如果没有问题,我需要考虑你写的内容并回复给你。 – screenm0nkey 2010-06-15 21:13:05

+0

@尼克,当然,想一想,我们正在处理一个完全抽象的类型,它存在于纯粹用于*说明目的*的规范中。这可能会让它有点难以喘口气。阅读关于它和'GetBase','GetPropertyName'和'GetValue'内部方法,然后我们可以更进一步,我可以向您解释如何使用Reference类型在设置函数时设置'this'值呼叫。 – CMS 2010-06-15 21:27:54

+0

谢谢你。你对JavaScript的理解是难以置信的。我会拿走你所说的话,并尝试去理解它,并进行一些研究。在这一点上,我必须授予我给TJ的正确答案,并不是因为他的回答比所有答案都好,而是因为我必须选择答案。我会+1你和其他人,因为我认为我们都是很好,有用的答案。再次感谢你的帮助。 – screenm0nkey 2010-06-16 10:00:33

2

这可能有助于将点运算符看作与with语句类似的行为。当您运行foo.bar(),结果是一样一样的,如果你跑:

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

这将foo覆盖在全局对象之上运行bar功能,允许它找到xfoo,因此回报20.

如果不立即拨打bar,不过,因为在(foo.bar = foo.bar),你反而得到:

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

然后将结果传递出括号,产生一个如<reference to bar>()这样的中间语句,它没有点运算符,因此没有with语句,因此没有访问foo,只能访问全局值x

(点运算符不实际上转换为with说法,当然,但行为是相似的。)

+0

谢谢你 - 帮助我很多。之前并没有真正使用过这个声明,但它给了我一些研究成果。所以在这个例子中(nick.foo.low.man = foo.man.tan.bar)()它将与 一样具有(foo.man.tan){ bar; //返回“bar”本身 } – screenm0nkey 2010-06-16 09:51:34

相关问题