使用 Object.create 执行的浅克隆如何与描述符 setters/getters 一起使用?

How does shallow cloning that is carried out with Object.create work with descriptors, setters/getters?

据此source:

To make a “real copy” (a clone) we can use... for the so-called “shallow copy” (nested objects are copied by reference) or a “deep cloning” function.

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

那么,对象的所有内部属性(例如描述符)如何进行复制,getters/setters。它们是通过引用复制的吗?

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

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

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

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 不传输原型。

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

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

一致

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

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

例如:

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) );

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

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

我们需要对 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);

或者,我们可以克隆我们的 constructedObj,在它所有的荣耀中,使用 ES5 给我们带来的内置魔法:

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

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

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

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

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

所有美好的事物。