【问题标题】:JavaScript Pass Argument to Function keeping Argument Data IntactJavaScript 将参数传递给保持参数数据完整的函数
【发布时间】:2019-11-09 07:30:10
【问题描述】:

我在 JavaScript 中遇到了一个奇怪的情况,我正在创建一个对象,然后将其作为参数传递给一个函数,该函数然后更新值,然后返回一个新的更新对象。

function objBuild() {

    var obj_old = {}; // original object
    var obj_new = {}; // updated object

    // set values for original object
    obj_old.val01 = "val01_old";
    obj_old.val02 = "val02_old";
    obj_old.val03 = "val03_old";

    // set values for new object using
    // the original object as a template
    obj_new = objUpdate(obj_old);

    console.log(obj_old); // this shows obj_new data which I don't want
    console.log(obj_new); // this shows obj_new data which I want

}

function objUpdate(obj) {

    obj.val03 = "val03_new";

    return obj;

}

我原以为新对象会使用新值更新,但旧对象也会更新。

也许该函数将旧对象作为参考,即使我返回一个单独的值,它也记得它发生了什么?

我不确定,但如果是这种情况,是否可以保持旧对象完好无损?

【问题讨论】:

  • 我原以为新对象会用新值更新,但旧对象也会更新。 javascript 中的对象是通过“参考”。从技术上讲,任何非原始值都是(例如,数组具有相同的行为)。您需要先克隆对象并更改克隆的对象值,这不是隐含的,这是设计使然。
  • "即使我返回一个单独的值" - 那会在哪里?不,没有单独的对象。

标签: javascript function scope parameter-passing


【解决方案1】:

请阅读:Is JavaScript a pass-by-reference or pass-by-value language?

在 javascript 中,对象是“技术上”通过“引用”传递的。因此,原始值被更改,因为 obj 实际上是原始对象。

您需要克隆对象,并且有很多关于“如何”执行此操作的场景,因为对象可能是浅拷贝或深拷贝。在此处阅读更多信息:What is the most efficient way to deep clone an object in JavaScript? 在这里:How do I correctly clone a JavaScript object?

无论如何,要解决您的问题,只需在一个全新的对象上使用 Object.assign 来复制原始对象的值,对于您的情况,这已经足够了,尽管我建议您阅读上述帖子以了解何时和如何正确复制对象。

function objBuild() {

    var obj_old = {}; // original object
    var obj_new = {}; // updated object

    // set values for original object
    obj_old.val01 = "val01_old";
    obj_old.val02 = "val02_old";
    obj_old.val03 = "val03_old";

    // set values for new object using
    // the original object as a template
    obj_new = objUpdate(obj_old);

    console.log(obj_old); // this shows obj_new data which I don't want
    console.log(obj_new); // this shows obj_new data which I want

}

function objUpdate(obj) {
    var _cloned = Object.assign({}, obj);
    _cloned.val03 = "val03_new";

    return _cloned;

}

objBuild();

【讨论】:

  • 这是一个非常好的、优雅的解决方案,我以前从不知道对象克隆。只是为了让我理解你的答案,这只发生在对象和数组上?如果是这样,数组是否有等效的 Object.assign 命令?
  • 实际上,不,函数也是通过引用传递的,就像许多其他的东西一样。通常,只有原语是按值传递的,因此您可以放心地认为数字、字符串和布尔值是,而数组、对象和函数实际上不是。关于克隆,请记住您应该考虑如何克隆对象。在链接的帖子中,有很多关于浅拷贝如何与深拷贝工作的信息(深拷贝意味着克隆对象的每个属性,包括原型、getter、setter)。例如,您可以安全地使用 object.assign。
  • 另外,作为“最后”的旁注,说这些通过引用传递在技术上是不正确的。在幕后,还有更多的事情发生,尽管您可以认为这个概念与“通过引用传递”非常相似,尽管实际上并不完全如此。
  • 关于数组:是的,你可以使用 array.slice(): jsfiddle.net/gqszwp4m ... 或者,和往常一样,还有许多其他替代方案,这正是我首先想到的 ;)
【解决方案2】:

您实际上并没有创建新对象,而是将旧对象设置为新对象。所以你是对的,旧的对象值仍在被引用。

如果你想创建一个新对象,并且没有任何嵌套对象,我会使用Object.assign()。这会生成对象的浅拷贝。你可以这样做:

obj_new = objUpdate(Object.assign({}, obj_old));

这将使用旧对象的可枚举属性创建一个新对象。

如果您有嵌套对象,这些对象仍将通过引用进行复制,因此我将遍历对象并以这种方式复制属性。

【讨论】:

    【解决方案3】:

    请记住,包括数组在内的对象是通过引用传递的,而字符串、布尔值和数字是通过值传递的。

    在这里,您正在传递对象(通过引用),因此它正在修改旧对象的值。新旧对象都指向同一个值。

    function objBuild() {
    
        var obj_old = {}; // original object
        var obj_new = {}; // updated object
    
        // set values for original object
        obj_old.val01 = "val01_old";
        obj_old.val02 = "val02_old";
        obj_old.val03 = "val03_old";
    
        // set values for new object using
        // the original object as a template
        obj_new = objUpdate(obj_old);
    
        console.log(obj_old); // this shows obj_new data which I don't want
        console.log(obj_new); // this shows obj_new data which I want
    
    }
    
    function objUpdate(obj_old) {
        var obj = JSON.parse(JSON.stringify(obj_old));
        obj.val03 = "val03_new";
    
        return obj;
    }
    
    objBuild();

    【讨论】:

      猜你喜欢
      • 2011-10-02
      • 2016-01-16
      • 2011-04-16
      • 2012-04-11
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 2019-07-15
      相关资源
      最近更新 更多