问题 在推送对象时,Array.push()使所有元素都相同


我是节点和javascript的新手,并且一直在敲打以下内容。我创建了一个对象如下:

var Subscriber = {
'userID': String,
'email': String,
'name': String,
'stage': String,
'poster': Boolean,
'canEmail': Boolean,
'stage': String, }

我有一个函数,我查询mongodb,并循环结果,尝试加载一组订阅者,我已声明为:

var s = Subscriber;
var subscribers = [];

循环看起来像这样:

//load array of users that are subscribed to the group
        async.forEach(g.subscribers, function(item, callback) {     
            //load user document for this user
            User.findOne({ _id: item}, function(err, u) {
                if(!err && u) {                 
                    //var s = new Subscriber();
                    console.log('Sub load, found user %s, building array item', u.email);
                    console.log('Subs @ loop start');
                    console.log(util.inspect(subscribers));

                    console.log('Heres foo: ' + util.inspect(foo));


                    s.userID = u._id;
                    s.email = u.email;
                    s.name = u.firstName + ' ' + u.lastName;
                    s.stage = u.stage;
                    s.poster = false; //we're just loading subscribers at this point'
                    if(s.stage != 'new') s.canEmail = true;

                    //push new subscriber onto the array
                    console.log('Pushing ' + util.inspect(s));
                    subscribers.push(s);

                    console.log('At end ' + util.inspect(subscribers));

                    foo.push(s.email);
                    console.log('Heres foo now: ' + util.inspect(foo));

                    callback(null, item);
                }

在每次调用subscribers.push(s)之后,数组具有正确数量的元素,但所有元素都匹配s的最后一个值,如下所示(从数据库中提取两个不同的用户):

[ { userID: 4fc53a71163006ed0f000002,
email: 'test@test.com',
name: 'undefined undefined',
stage: 'new',
poster: false,
canEmail: true },
  { userID: 4fc53a71163006ed0f000002,
email: 'test@test.com',
name: 'undefined undefined',
stage: 'new',
poster: false,
canEmail: true } ]

推动单个元素而不是整个对象似乎没问题。我添加了“foo”数组作为测试,它工作正常:

Heres foo now: [ 'email1@foo.com', 'test@test.com' ]

这里发生了什么?!?!??!


7077
2018-06-07 13:15


起源

是什么 g.subscribers 看起来像? - alessioalex
对象和数组(对象)在JavaScript中通过引用传递。如果 s 是一个对象,您只是通过更改属性然后将相同的对象推送到循环中的数组来重新使用它,然后数组中的对象都是对同一对象的引用。 - Steve
谢谢!这非常有帮助。我当时认为它可能是引用的东西,但却无法绕过它。我想这就是20年前您最后一次开发工作时所发生的事情,如Pascal和C等尖端语言! - pat


答案:


问题不在于 push 的方法 Array.prototype 但是你的绑定。 你正在修改它 s 在你的每一次迭代中的对象 async.foreach 块实际上是与先前定义的相同的对象 Subscriber

首先你应该移动声明 s 变量到foreach块。

而且如果你想用默认值创建一个对象,它应该是一个 function,返回一个新对象:

function Subscriber() {
  return {
    'userID':   '',
    'email':    '',
    'name':     '',
    'stage':    '',
    'poster':   false,
    'canEmail': false,
    'stage':    ''
  };
};

然后你可以实例化一个 Subscriber 像这样的对象:

var s = Subscriber();

看到 这个答案 要么 MDN上的闭包 更多解释。


16
2018-06-07 13:24



我犯了什么新手的错误。这正是我没有看到的。 - Ken Ingram


在推入阵列之前克隆对象,也解决了问题。

temp = clone(s);
subscribers.push(temp);

得到 https://www.npmjs.com/package/clone


0
2017-10-16 18:29





您必须每次都复制订阅者。否则,每次都修改同一个对象。只是用 s = copy(Subscriber)


-1
2018-06-07 13:26



copy 未在node.js环境中定义。 - KARASZI István