数组过滤器更改主数组

时间:2016-08-07 14:41:17

标签: javascript arrays node.js

我注意到node.js上的数组过滤器中有一些奇怪的行为。 有一个简单的数组和一个循环:

var array = [
{
    name:"bob",
    planet:"earth"
},
{
    name:"mike",
    planet:"mars"
},
{
    name:"vlad",
    planet:"jupiter"
}];

var filtered = array.filter(function(x){
    return x.name !== "mike";
});

console.log(array); //lets print how normal array looks like
console.log("---");
console.log(filtered); //lets print how filtered one looks like

for(var i = 0; i < filtered.length; i++)
{
    delete filtered[i].planet; //remove planet
    filtered[i].name = filtered[i].name + "[NEW]"; //add NEW to the name
}

console.log("After replacement:");
console.log(array);//lets print how normal array looks like now
console.log("-----------");
console.log(filtered);//lets print how filtered array looks like now

理论上,array数组不应该被更改,因为我没有以任何方式操纵它。 Hovewer,这是我在控制台中得到的:

[ { name: 'bob', planet: 'earth' },
  { name: 'mike', planet: 'mars' },
  { name: 'vlad', planet: 'jupiter' } ] //this array is normal
---
[ { name: 'bob', planet: 'earth' },
  { name: 'vlad', planet: 'jupiter' } ] //this is good behavior, since I don't need "mike"

After replacement:

[ { name: 'bob[NEW]' },
  { name: 'mike', planet: 'mars' },
  { name: 'vlad[NEW]' } ] //this should not be changed in any way
-----------
[ { name: 'bob[NEW]' }, { name: 'vlad[NEW]' } ] //this is correct

为什么会这样?我需要array保持与开头相同。

感谢。

1 个答案:

答案 0 :(得分:7)

您的代码在这里:

for(var i = 0; i < filtered.length; i++)
{
    delete filtered[i].planet; //remove planet
    filtered[i].name = filtered[i].name + "[NEW]"; //add NEW to the name
}

...无法更改 数组。它改变了两个数组引用的对象的状态。

更简单的例子:

var a = [{answer:null}];
var b = a.filter(function() { return true; }); // Just a copy, but using `filter` for emphasis
a[0].answer = 42;
console.log(b[0].answer); // 42

这一行:

a[0].answer = 42;

不会更改ab,它会更改a[0]所指的状态,b[0] 指的是到。

让我们抛出一些 ASCII-art Unicode -art:

这一行之后:

var a = [{answer:null}];

这就是我们在记忆中所拥有的(忽略一些不相关的细节);

+−−−−−−−−−−−−−−+
| variable "a" |
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+
| Ref11542     |−−−−>|    array     |
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+
                     | 0: Ref88464  |−−−−>|    object    |
                     +−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+
                                          | answer: null |
                                          +−−−−−−−−−−−−−−+

a是指一个数组对象,它具有0属性,该属性引用具有answer属性的对象。我正在使用&#34; Ref11542&#34;和&#34; Ref88494&#34;表示aa[0]包含的对象引用,但当然我们实际上从未看到这些引用的值;他们是JavaScript引擎专用的。

然后我们这样做:

var b = a.filter(function() { return true; }); // Just a copy, but using `filter` for emphasis

现在我们有:

+−−−−−−−−−−−−−−+
| variable "a" |
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+
| Ref11542     |−−−−>|    array     |
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+   
                     | 0: Ref88464  |−−+
                     +−−−−−−−−−−−−−−+  |
                                       |
                                       |  +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−+                       +−>|    object    |
| variable "b" |                       |  +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+  |  | answer: null |
| Ref66854     |−−−−>|    array     |  |  +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+  |
                     | 0: Ref88464  |−−+
                     +−−−−−−−−−−−−−−+

请注意,两个数组都包含相同的对象引用(此处显示为&#34; Ref88464&#34;);他们指向相同的对象。

现在我们这样做:

a[0].answer = 42;

所有这一切都是改变对象的状态;它对ab或它们引用的数组没有影响:

+−−−−−−−−−−−−−−+
| variable "a" |
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+
| Ref11542     |−−−−>|    array     |
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+   
                     | 0: Ref88464  |−−+
                     +−−−−−−−−−−−−−−+  |
                                       |
                                       |  +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−+                       +−>|    object    |
| variable "b" |                       |  +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+  |  | answer: 42   |
| Ref66854     |−−−−>|    array     |  |  +−−−−−−−−−−−−−−+
+−−−−−−−−−−−−−−+     +−−−−−−−−−−−−−−+  |             ^
                     | 0: Ref88464  |−−+             +−−−−−−− only change is here
                     +−−−−−−−−−−−−−−+

很自然地

console.log(b[0].answer);

......输出42。

在评论中你曾问过:

  

但是,如何设置过滤对象的状态而不是主要对象?

请记住,对象在两个数组中都是相同的。你还没有复制这些对象,你刚刚创建了一个只包含其中一些的新数组。

如果您希望能够更改这些对象&#39;在不影响第一个数组中对象的属性的情况下,您需要创建对象的副本

如果对象只包含简单的原始值,那很容易;一般情况很难。 : - )

但是,在你的情况下,因为你的objets只有nameplanet属性,接下来要做的就是删除planet属性并更改名称,我们可以随时随地轻松创造物体;见评论:

&#13;
&#13;
var array = [
{
    name:"bob",
    planet:"earth"
},
{
    name:"mike",
    planet:"mars"
},
{
    name:"vlad",
    planet:"jupiter"
}];

var filtered = array.filter(function(x){
    return x.name !== "mike";
}).map(function(x) {
    // Create a *new* object, setting its `name` to the `name`
    // of the original object plus [NEW], and ignoring its
    // `planet` property entirely
    return {name: x.name + "[NEW]"};
});

console.log(array);
console.log("---");
console.log(filtered);
&#13;
&#13;
&#13;

或者,您可能只希望通过数组进行一次传递:

var filtered = [];
array.forEach(function(x){
    if (x.name !== "mike") {
        filtered.push({name: x.name + "[NEW]"});
    }
});