JavaScript返回数组项的引用

35

我有一个像这样的数组:

users = [{id:1, name:'name1'},{id:2, name:'name2'}]

我该如何获取到{id:2, name:'name2'}这个项的引用,以便我可以更改它的名称属性,例如:

user = get_item(users, 'id', 2);
user.name = "user2 name changed";

console.log(users)将得到以下结果:

[{id:1, name:'name1'},{id:2, name:'user2 name changed'}]

我尝试使用Array.filter()函数,但它返回的是一个新数组,而不是对原始数组的引用。这样我就无法使用它来改变原始数组了。

有什么想法吗?

7个回答

68
我尝试使用 Array.filter() 函数,但它返回一个新的数组而不是对原始数组的引用。我无法使用它来改变原始数组。 它返回一个新的数组,但是这个数组的条目仍然是指向相同对象的引用。所以使用 filter 函数也可以。
var filteredUsers = users.filter(function(entry) { return entry.id === 2; });
var item = filteredUsers[0];
item.name = "user2 name updated";
console.log(users[1].name) // "user2 name updated"
数组包含对对象的引用而不是副本。当我们执行item = users[1]或使用filter获取包含对象子集的新数组时,我们获得对象引用的副本,因此变量现在引用同一对象,如下所示:
                +−−−−−−−−−−+
users−−−−−−−−−−>| (array)  |          
                +−−−−−−−−−−+           +−−−−−−−−−−−−−−−+
                |   0      |−−−−−−−−−−>|    (object)   |
                |          |           +−−−−−−−−−−−−−−−+
                |          |           | id:   1       |
                |          |           | name: "name1" |
                |          |           +−−−−−−−−−−−−−−−+
                |   1      |−−+
                |          |  |
                |          |  |        +−−−−−−−−−−−−−−−+
                |          |  +−−−+−+−>|    (object)   |
                +−−−−−−−−−−+     / /   +−−−−−−−−−−−−−−−+
                                 | |   | id:   2       |
                +−−−−−−−−−−+     | |   | name: "name2" |
filteredUsers−−>| (array)  |     | |   +−−−−−−−−−−−−−−−+
                +−−−−−−−−−−+     | |   
                |   0      |−−−−−+ |
                +−−−−−−−−−−+       |   
                                   |
item−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
更改对象会改变该对象,无论使用哪个引用查看其属性,这些更改都是可见的。 现在,如果您有一个包含基元类型的数组:
var a = [1, 2, 3, 4, 5];

如果不是对象,那么你显然不能使用上面的方法。在这种情况下,你可以使用indexOf来查找索引:

var index = a.indexOf(3); // Find the first entry === 3

...然后修改这个条目

a[index] = 42;

这也适用于字符串(前提是它们是普通的字符串基元,而不是通过new String()创建的东西,几乎没有理由这样做)。


这个问题是我不知道索引,只知道id属性的值,所以我需要一个帮助函数来查找并引用该项。 - fuyi
@xiaopang:哦,我没有理解。我会修正回答。 - T.J. Crowder
@xiaopang:我已经再次编辑了,以解决你问题中的主要误解。 - T.J. Crowder
今天学到了新东西... - webmaster

43

为了与被接受的答案类似,但更加简洁,让我们使用Array.find,像这样:

var item = users.find(u => u.id === 2)
item.name = "user2 name updated"
console.log(users[1].name) // "user2 name updated"

解释与已接受的答案中提供的内容非常相似。


2
我相信这是正确的答案,简短易懂 :) - YouneL
这个不起作用a = [1,2,3]; -> (3) [1, 2, 3] b = a.find(i => i === 2); -> 2 b = 4; -> 4 a; -> (3) [1, 2, 3] - Matt Westlake
1
Matt,它只能在非原始值上运行。Javascript通过引用返回对象和数组这样的非原始值。 - Juha Vehnia
如果您知道该项存在,您可以直接执行以下操作:users.find(u => u.id === 2).name = "用户2的名称已更新"; - Vinorth
find 返回一个引用,我认为这是一个不好的设计模式,find 应该返回全新的对象以尊重可变性。 - Marwen Trabelsi

6
与 @zing-lee 的答案类似,您可以使用 find() 来删除一行代码:
var users = [{id:1, name:'name1'},{id:2, name:'name2'}]
var user = users.find(item => item.id === 2)
user.name = 'name change'

从文档中得知:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find 返回值: 满足提供的测试函数的数组中第一个元素的值。
由于它是一个对象数组(不是基本类型),所以返回的值将是对原始对象的引用,因此修改返回对象的属性将更新它所引用的对象的属性。
这里有一篇很好的文章,解释了Javasript中的值和引用:https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0

2
有趣的是,如果我使用展开运算符覆盖通过fund方法找到的对象,就像这样:let foundElement = collections.find(element => element.id === someId); foundElement = {..someData};它将无法正常工作。 - julio
2
这是预期的行为。扩展运算符的工作方式类似于 Object.assign()。它总是返回对象的浅拷贝。 - camslice
是的,我不是想抱怨,只是想向其他人提一下。 - julio

6
你可以使用 findIndex() 方法,例如:
var users = [{id:1, name:'name1'},{id:2, name:'name2'}]
var idx = users.findIndex(item => item.id === 2);
var u = users[idx]; // the reference of user2
u.name = "name changed."; // same as `users[idx].name = "name changed.";`

这种方法在找到匹配的单个项时停止搜索,这使得它可能比使用过滤器更有效,因为后者总是会处理整个数组。 - Roberg

1

要么

  users[1].name = "xx"

如果您想按照id搜索,就像您在问题中尝试的那样:

for (i = 0; i < users.length; i++) {
  if (users[i].id == "2") {
    users[i].name = "xxx";
  }
}

编辑:你想要更高级的东西,那么使用map如何:(思路是您可以更改与查询匹配的元素,并基于此创建一个新数组。您可以使用filter将它们过滤掉)
[{a: 1, b: 2}, {a:3, b:2}].map(function (elm) {
if (elm.b == 2) {
 elm.a += 5;
}
return elm;
});

这个返回:
[{a: 6, b: 2}, {a:8, b:2}]

有没有比循环更好的方法? - fuyi
@xiaopang 建议使用 Array.prototype.map。在这里阅读相关信息:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map - Ben

0

请检查以下函数是否解决了您的问题

注意:自定义对象始终作为引用传递到JavaScript函数。因此,自定义对象的数组也是通过引用传递的。

function updateUser(targetArray, keyField, targetField, key, new_val) {
var result = -1;
for (var i = 0; i < targetArray.length; i++) {
    if (targetArray[i][keyField] == key) {
        targetArray[i][targetField] = new_val;
        result = i;
        return result;
    }
}
return result;
}

示例


0

这是能更好地解释情境的代码。运行脚本,您就会理解。

var arr = [
 {"name": "James", "age": 34},
 {"name": "Peter", "age": 67}
];

var p1 = arr.filter(function(p){return p.name=="James"});
p1[0] = {"name": "James", "age": 50};
console.log(JSON.stringify(arr)); // Won't work

var p2 = arr.filter(function(p){return p.name=="James"});
p2[0].age = 50;
console.log(JSON.stringify(arr)); // Voila... this works


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接