【问题标题】:How does shallow cloning that is carried out with Object.create work with descriptors, setters/getters?使用 Object.create 执行的浅层克隆如何与描述符、setter/getter 一起使用?
【发布时间】:2021-09-20 15:14:46
【问题描述】:

据此source

要制作“真实副本”(克隆),我们可以使用... 所谓的“浅拷贝”(嵌套对象通过引用复制) 或“深度克隆”功能。

所以,据我所知,浅克隆假设如果内部还有其他内部对象,那么它们将通过引用进行复制。他们没有克隆。

那么,如何复制对象的所有内部属性,例如描述符、getter/setter。它们是通过引用复制的吗?

【问题讨论】:

  • 这不是“强大的克隆”。它是“更强大的克隆”。比复制可枚举的属性更强大。 (我有 10 美元。你有 5 美元。我“更富有”,但我并不“富有”。)
  • 因为如果一个属性包含一个对象,制作一个精确的副本意味着两个副本都持有精确的相同对象。不知道为什么深拷贝会“更强大”。
  • 你的标题没有意义。 Obect.create 不会复制/克隆任何东西。它也没有在您链接的文章中的任何地方引用。

标签: javascript object clone


【解决方案1】:

考虑在浅克隆只有一个原始属性的标准对象时所采取的基于 ES3 的步骤:

  1. 创建一个新的空对象。
  2. 为该空对象添加一个键。
  3. 为该键分配一个值。
  4. 返回对象。

这些步骤可以使用手动功能完成,如下所示:

function es3ShallowClone(incomingObj){

    var cloneOfObj = {}; // 1
    
    for(var key in incomingObj)
        cloneOfObj[key] = incomingObj[key]; // 2 and 3

    return cloneOfObj; // 4
}

使用 es3ShallowClone 函数,您可以这样进行克隆:

var obj = {a:"b"};
var newObj = es3ShallowClone(obj);

Object.assign( {}, obj ) 是(自 2009 年 ES5 发布以来)一种生成与 es3ShallowClone 相同的输出的内置方式:

let newAssignedObj = Object.assign( {}, obj );

Object.assign 仅复制可枚举的属性,并且 Object.assign 不传输原型。

///////////////////////////////////////

更多“权力:” 如果您希望克隆具有不可枚举属性的其他标准对象,则需要使用 Object.getOwnPropertyDescriptors 和 Object.defineProperty;可能与 Object.create 一致。

手动浅克隆具有不可枚举属性的标准对象时所采取的基于 ES5 的步骤:

  1. 创建一个新的空对象。
  2. 从传入对象中获取包含描述符的数组。
  3. “浅克隆”描述符,然后应用描述符克隆您的空对象。
  4. 返回对象。

例如:

function es5ShallowClone(incomingObj){

    let cloneOfObj = {}; // 1
    
    let descriptors = Object.getOwnPropertyDescriptors(incomingObj); // 2

    for(var key in descriptors)
        Object.defineProperty( cloneOfObj, key, descriptors[key] ); // 3

    return cloneOfObj; // 4
}

首先,让我们创建一个具有不可枚举属性的示例对象:

制作描述符:

let someDescriptor = {
  'a': {
    value: 'b',
    writable: true,
    configurable:true,
    enumerable:false
};

创建一个对象并将描述符分配给它:

let obj2 = Object.create( {}, someDescriptor );

然后克隆它:

let newObj2 = es5ShallowClone(obj2);

代替 es5ShallowClone,您可以改为:

let newObjToo = Object.create( {}, Object.getOwnPropertyDescriptors(obj2) );

///////////////////////////////////////

为了获得更多“力量”,您还需要转移原型;请注意,我对“转移”一词的使用有点笨拙,因为原型并没有离开原始对象……两个对象最终都引用了同一个原型。

我们需要对 es5ShallowClone 函数进行的唯一更改是第 1 步;以便它根据传入对象的原型创建一个对象:

function es5ShallowCloneWithPrototype(incomingObj){
    
    let cloneOfObj = new incomingObj.constructor(); // 1
    
    let descriptors = Object.getOwnPropertyDescriptors(incomingObj); // 2

    for(var key in descriptors)
        Object.defineProperty( cloneOfObj, key, descriptors[key] ); // 3

    return cloneOfObj; // 4
}

首先,我们将定义一个构造函数,它可以创建一个具有不可枚举属性和原型的对象:

function objConstructor(){

    let someDescriptor = {
        'a': {
            value: 'b',
            writable: true,
            configurable:true,
            enumerable:false
        };
    }

    let returnObj = Object.create( {}, someDescriptor );
}
objConstructor.prototype.extraInfo = “javascript rocks”;

然后我们将使用该构造函数来创建一个花哨的新对象:

let constructedObj = new objConstructor();

现在,我们可以克隆该ConstructedObj,在它的所有荣耀中,因此:

let newCon = es5ShallowCloneWithPrototype(constructedObj);

或者,我们可以使用 ES5 带给我们的内置魔法来克隆我们的ConstructedObj:

let newCon2 = Object.create(
    Object.getPrototypeOf(constructedObj),
    Object.getOwnPropertyDescriptors(constructedObj)
);

///////////////////////////////////////

希望这个简短的概述有助于澄清描述符的处理方式与克隆过程中处理常规 ol 对象的方式不同。

要仔细查看 ES3 或自 ES5 以来可用于克隆函数的信息,并查看在枚举期间如何呈现 getter 和对象值,请查看以下 codepen 链接,然后打开浏览器的控制台。 .. 您可能想清除控制台,然后再次单击运行按钮以查看捕获信息的最佳表示。 PS:使用 ES3 风格的克隆,setter 的名称被添加到你的克隆中并保持 undefined 的值。使用 ES5 样式的克隆时,如果 getter 和 setter 引用了不可克隆器可访问范围内的值,则这些 getter 和 setter 可能会导致错误。)

https://codepen.io/Ed_Johnsen/pen/GRmjajr

所有的好东西。

【讨论】:

  • 我不明白“老派”和“新派”之间的区别。
  • ES3 v ES5+。特别是,区别在于描述符的使用;循环遍历描述符数组,并使用 defineProperty 分配键值对 - 用于访问不可枚举。关于如何更好地区分手卷方法的任何想法?
  • 哦,您可能想在答案中明确说明这一点。我会假设“老派”大约是 ES5 时代,而任何“新派”都是 ES6+。
猜你喜欢
  • 2021-08-31
  • 2019-08-11
  • 2012-07-13
  • 2021-01-14
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
  • 2021-10-22
相关资源
最近更新 更多