如何克隆一个包括 getter 和 setter 的 JavaScript 对象?

15
在我正在处理的项目中,我需要将对象克隆到一个变量中。我最初尝试了似乎是最显然的解决方案 - var obj2 = obj1,但我很快意识到这会使obj2引用obj1,因此每当我在obj2中设置属性时,属性也会更新在obj1上。好吧,我不能这么做。因此,我开始寻找在JavaScript中克隆对象的方法 - 我找到了多种解决方案,主要是var obj2 = JSON.parse(JSON.stringify(obj1)) - 但这并没有保留我为对象定义的所有getter和setter!现在对我来说最明显的解决方案似乎是首先使用上述JSON技巧,使obj2具有obj1的所有属性,然后循环遍历对象的所有getter和setter,并使用Object.defineProperty()将它们添加回去,但我还没有找到一种方法来获取对象的所有getter/setter。

1
请查看 https://lodash.com/docs#clone - AlexD
你需要在想要的对象上创建自己的 clone 方法,该方法将使用相同的构造函数创建一个新对象并将相同的值分配给它。 - Louay Alakkad
@AlexD,lodash的clone无法工作 - 就像任何不具备ES5兼容性的克隆函数一样,当它尝试复制由“getter”提供的属性时,它只是读取该属性的当前值并将其存储在克隆中,而不是复制getter函数本身。 - Alnitak
@ALexD - 是的 - Alnitak 完全正确。它不会复制 getter,只使用它来访问当前值并直接克隆该值... - Moonwalker
2个回答

38

在实际层面上,完全准确地克隆一个对象是不可能的,因为getter和setter(以及其他函数)可能通过闭包访问词法作用域私有变量。访问这样的方法将引用原始对象,而不是克隆。

如果(且仅如果)没有这种情况,你可以枚举所有属性(包括非枚举属性),使用Object.getOwnPropertyNames()找到每个名称,然后简单地使用Object.getOwnPropertyDescriptor获取每个属性的描述符,然后将得到的字段传递给Object.defineProperty,例如:

function shallowClone(obj) {
    var clone = Object.create(Object.getPrototypeOf(obj));

    var props = Object.getOwnPropertyNames(obj);
    props.forEach(function(key) {
        var desc = Object.getOwnPropertyDescriptor(obj, key);
        Object.defineProperty(clone, key, desc);
    });

    return clone;
}

在 ES2017 中,你可以改为采用这种方式

function shallowClone(obj) {
    return Object.create(
        Object.getPrototypeOf(obj), 
        Object.getOwnPropertyDescriptors(obj) 
    );
}

非常感谢!这正是我在寻找的 - 我正在与另外两个人一起工作,而领导有一个严格的“无库”规则。 ;) - Arthur F.
@ArthurF. 请注意,此代码仅适用于 ES5 - 它将无法在 IE9 之前的版本中运行(据我记忆)。 - Alnitak
@ArthurF。我刚刚在Object.create行中纠正了一个小错误。 - Alnitak
嗨 - 很抱歉回复晚了一点,作为一个JS新手,什么是“词法作用域私有变量”?你能举个例子吗?谢谢。 :) - Arthur F.
@ArthurF,请查看“JavaScript闭包如何工作”的相关链接,该链接位于右侧的“相关”下方。 - Alnitak
显示剩余4条评论

1

根据@Alnitak的说法,我认为这是一种捷径方式

function shallowClone(obj){
 var clone = Object.create(Object.getPrototypeOf(obj));
 var descriptors = Object.getOwnPropertyDescriptors(obj);
 Object.defineProperties(clone, descriptors);
 return clone;
}

是的,但这仅适用于ES2017及更高版本,因此仅在存在Object.gteOwnPropertyDescriptors()时才有效,例如Edge < 15中不存在。 - Alnitak
抱歉,我无法追踪所有Edge版本...以上是正确的答案,希望Edge有一天能够赶上。 - pery mimon

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