Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/417.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 多重赋值混淆_Javascript_Operator Precedence_Associativity_Multiple Assignment - Fatal编程技术网

Javascript 多重赋值混淆

Javascript 多重赋值混淆,javascript,operator-precedence,associativity,multiple-assignment,Javascript,Operator Precedence,Associativity,Multiple Assignment,我知道赋值运算符是右关联的 A['x'] = foo = {a:1} 例如,x=y=z=2相当于(x=(y=(z=2)) 在这种情况下,我尝试了以下方法: foo.x = foo = {a:1} var foo = {}; foo.x = foo = {a:1}; 我希望对象foo将使用值{a:1}创建,然后在foo上创建属性x,该属性将只是对foo对象的引用 (如果我将多重赋值语句分成两个单独的语句,就会出现这种情况foo={a:1};foo.x=foo;) 结果实际上是: Refere

我知道赋值运算符是右关联的

A['x'] = foo = {a:1}
例如,
x=y=z=2
相当于
(x=(y=(z=2))

在这种情况下,我尝试了以下方法:

foo.x = foo = {a:1}
var foo = {};
foo.x = foo = {a:1};
我希望对象
foo
将使用值
{a:1}
创建,然后在
foo
上创建属性
x
,该属性将只是对
foo
对象的引用

(如果我将多重赋值语句分成两个单独的语句,就会出现这种情况
foo={a:1};foo.x=foo;

结果实际上是:

ReferenceError:未定义foo(…)

因此,我尝试了以下方法:

foo.x = foo = {a:1}
var foo = {};
foo.x = foo = {a:1};
现在我不再得到异常,但是
foo.x
是未定义的

为什么作业没有按我预期的那样进行?
免责声明:“重复”问题似乎与我提出的问题大不相同,因为问题在于分配中创建的变量是全局变量,与使用
var
关键字创建的变量相同。这不是这里的问题。

编辑了答案以使其简单化

首先,您必须了解参考-值-类型之间的区别

foo
变量保存对内存中对象的引用,比如
a

现在,有两种访问器:变量访问器和属性访问器

因此
foo.x=foo={a:1}
可以理解为

[foo_VARIABLE_ACCESSOR][x_PROPERTY_ACCESSOR] = [foo_VARIABLE_ACCESSOR] = {a:1}
首先计算访问器链以获取最后一个访问器,然后对其进行关联计算

A['x'] = foo = {a:1}
属性访问器分为setter和getter

var foo = { bar: {} };
foo.bar.x = foo = {a:1}
这里有两个嵌套对象
foo
bar
。在内存中,我们有两个对象
A
B

[foo_VAR_ACCESSOR][bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}

> A[bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B[x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B['x'] = foo = {a: 1}
这里你有一个小例子

var A = {};
var B = {}
Object.defineProperty(A, 'bar', {
    get () {
        console.log('A.bar::getter')
        return B;
    }
})
Object.defineProperty(B, 'x', {
    set () {
        console.log('B.x::getter')
    }
});

var foo = A;
foo.bar.x = foo = (console.log('test'), 'hello');

// > A.bar.getter
// > test
// > B.x.setter

好问题。这里需要记住的是,JavaScript对所有内容都使用指针。很容易忘记这一点,因为在JavaScript中无法访问表示内存地址的值(请参阅)。但是要理解JavaScript中的许多东西,认识到这一点非常重要

所以声明

var foo = {};
在内存中创建一个对象,并将指向该对象的指针指定给
foo
。现在,当此语句运行时:

foo.x = foo = {a: 1};
属性
x
实际上被添加到内存中的原始对象,而
foo
被分配一个指向新对象的指针,
{a:1}
。比如说,

var foo, bar = foo = {};
foo.x = foo = {a: 1};
显示如果
foo
bar
最初指向同一对象,
bar
(仍将指向该原始对象)将看起来像
{x:{a:1}
,而
foo
只是
{a:1}

那么为什么
foo
看起来不像
{a:1,x:foo}

虽然您正确地认识到赋值是右关联的,但您还必须意识到解释器仍然从左向右读取。让我们举一个深入的例子(抽象出一些位):

好的,在内存位置47328(或其他位置)中创建一个对象,将
foo
分配给指向47328的指针

好的,抓取
foo
当前指向内存位置47328的对象,向其添加一个属性
x
,然后准备将
x
分配给下一个对象的内存位置

好的,抓取指针
foo
,准备将其分配到下一步的内存位置

好的,在47452位置的内存中创建一个新对象。现在回到链的上游:分配
foo
指向内存位置47452。将内存位置47328处对象的属性
x
指定给
foo
现在指向的对象--内存位置47452

简言之,没有速记方法

var foo = {a: 1};
foo.x = foo;

关联性评估顺序之间有一个重要的区别

在JavaScript中,即使赋值运算符从右向左分组,操作数也会在执行实际赋值(从右向左)之前从左向右求值。考虑这个例子:

var a = {};
var b = {};
var c = a;

c.x = (function() { c = b; return 1; })();
变量
c
最初引用
a
,但赋值的右侧将
c
设置为
b
。指定哪个属性,
a.x
b.x
?答案是
a.x
,因为当
c
仍然引用
a
时,首先计算左侧

通常,表达式
x=y
的计算如下:

  • 计算
    x
    并记住结果
  • 计算
    y
    并记住结果
  • 将步骤2的结果指定给步骤1的结果(并将前者作为表达式
    x=y
    的结果返回)
  • 多个赋值会发生什么情况,如
    x=(y=z)
    ?重现

  • 计算
    x
    并记住结果
  • 计算y=z的值并记住结果。为此:
  • 计算
    y
    并记住结果
  • 计算
    z
    并记住结果
  • 将步骤2.2的结果指定给步骤2.1的结果(并将前者作为表达式
    y=z
    的结果返回)
  • 将步骤2的结果指定给步骤1的结果(并将前者作为表达式
    x=(y=z)
    的结果返回)
  • 现在让我们看一下您的示例,稍加编辑:

    var foo = {};
    var bar = foo;         // save a reference to foo
    foo.x = (foo = {a:1}); // add parentheses for clarity
    
    foo.x
    是在
    foo
    被分配到
    {a:1}
    之前计算的,因此
    x
    属性被添加到原始
    {}
    对象中(您可以通过检查
    bar
    进行验证)。

    a
    var fo
    
    var a = {};
    var b = {};
    var c = a;
    
    c.x = (function() { c = b; return 1; })();
    
    var foo = {};
    var bar = foo;         // save a reference to foo
    foo.x = (foo = {a:1}); // add parentheses for clarity