javascript - 자바 스크립트 extend




JavaScript에서 객체를 깊이 복제하는 가장 효율적인 방법은 무엇입니까? (20)

참고 : 이 질문에 대한 적절한 응답이 아닌 다른 대답에 대한 회신입니다. 빠른 개체 복제를 원하시면이 질문 에 대한 답변에서 코반의 조언을 따르십시오.

jQuery.clone() 메서드는 DOM 요소 만 복제한다는 점에 유의하고 싶습니다. JavaScript 객체를 복제하려면 다음을 수행하십시오.

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

더 많은 정보는 jQuery 문서 에서 찾을 수있다.

또한 깊은 사본은 실제로 위에서 설명한 것보다 훨씬 똑똑하다는 점에 유의하십시오. 많은 트랩을 피할 수 있습니다 (예 : DOM 요소를 심층적으로 확장하려는 경우). jQuery 코어 및 플러그인에서 자주 사용되어 큰 효과를 얻습니다.

JavaScript 객체를 복제하는 가장 효율적인 방법은 무엇입니까? 나는 obj = eval(uneval(o)); 사용하고는 있지만 표준이 아니며 Firefox에서만 지원됩니다 .

obj = JSON.parse(JSON.stringify(o)); 와 같은 작업을 수행했습니다 obj = JSON.parse(JSON.stringify(o)); 그러나 효율성에 의문을 제기하십시오.

또한 여러 가지 결함이있는 재귀 복사 기능을 보았습니다.
나는 정식 해결책이 존재하지 않는다는 것에 놀랐다.


구조화 된 복제

HTML5는 개체의 딥 클론을 만들 수 있는 내부의 "구조화 된"복제 알고리즘 을 정의합니다. 여전히 특정 내장 유형에 국한되지만 JSON에서 지원하는 몇 가지 유형 외에도 Dates, RegExps, Maps, Sets, Blob, FileLists, ImageDatas, 희소 배열, 유형 지정 배열 등을 지원합니다. . 또한 복제 된 데이터 내에서 참조를 유지하므로 JSON에 오류를 일으킬 수있는 순환 및 순환 구조를 지원할 수 있습니다.

브라우저에서 직접 지원 : 곧 출시 될 예정입니까? 🙂

브라우저는 현재 구조화 된 복제 알고리즘에 대한 직접 인터페이스를 제공하지 않지만 GitHub의 whatwg / html # 793에서 global structuredClone() 함수가 활발하게 논의되고 있으며 곧 제공 될 예정입니다. 현재 제안 된대로 대부분의 용도로 사용하는 것은 다음과 같이 간단합니다.

const clone = structuredClone(original);

이것이 출시 될 때까지 브라우저의 구조화 클론 구현은 간접적으로 만 노출됩니다.

비동기 해결 방법 : 사용 가능. 😕

기존 API를 사용하여 구조화 된 복제본을 만드는 오버 헤드가 낮은 방법은 MessageChannels 한 포트를 통해 데이터를 게시하는 것입니다. 다른 포트는 첨부 된 .data 의 구조화 된 복제본과 함께 message 이벤트를 방출합니다. 불행히도 이러한 이벤트를 청취하는 것은 반드시 비동기 적이며 동기식 대안은 실용적이지 않습니다.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

사용 예 :

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

동기식 해결 방법 : 끔찍한 일! 🤢

구조화 된 클론을 동 기적으로 생성하기위한 좋은 옵션은 없습니다. 대신에 비실용적 인 몇 가지 해킹이 있습니다.

history.pushState()history.replaceState() 는 첫 번째 인수의 구조화 된 복제를 history.state 값을 history.state 할당합니다. 이것을 사용하여 다음과 같은 객체의 구조화 된 복제본을 만들 수 있습니다.

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

사용 예 :

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

동기식이지만 매우 느릴 수 있습니다. 브라우저 히스토리 조작과 관련된 모든 오버 헤드가 발생합니다. 이 메소드를 반복적으로 호출하면 Chrome이 일시적으로 응답하지 않을 수 있습니다.

Notification 생성자 는 관련 데이터의 구조화 된 복제본을 만듭니다. 또한 사용자에게 브라우저 알림을 표시하려고 시도하지만 알림 권한을 요청하지 않은 경우 자동으로 실패합니다. 다른 용도로 사용 허가를받은 경우 즉시 작성한 알림을 닫습니다.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

사용 예 :

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();


AngularJS

자네가 각도를 사용한다면 너도 이걸 할 수있어.

var newObject = angular.copy(oldObject);

JS에서 오브젝트를 Cloning 하는 것은 항상 중요한 문제 였지만, ES6 이전에는 모든 것이 었습니다. 아래 JavaScript에서 오브젝트를 복사하는 여러 가지 방법을 나열했습니다. 아래 오브젝트가 있고 그 사본을 가지고 싶다고 상상해보십시오.

var obj = {a:1, b:2, c:3, d:4};

원점을 변경하지 않고이 오브젝트를 복사하는 데는 몇 가지 방법이 있습니다.

1) ES5 +, 간단한 기능을 사용하여 복사본을 만듭니다.

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }
    throw new Error("Unable to copy obj this object.");
}

2) JS5.parse 및 JSON.stringify를 사용하여 ES5 +.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) AngularJs :

var  deepCopyObj = angular.copy(obj);

4) jQuery :

var deepCopyObj = jQuery.extend(true, {}, obj);

5) UnderscoreJs & Loadash :

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

희망이 도움이 ...


내장 된 것이 없다면 시도해 볼 수 있습니다 :

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

변수 만 있고 객체에 함수가 없다고 가정하면 다음을 사용할 수 있습니다.

var newObject = JSON.parse(JSON.stringify(oldObject));

암호:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

테스트:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);

위의 ConroyP의 대답은 생성자가 매개 변수를 필요로하는 경우에도 작동합니다.

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

이 함수는 내 simpleoo 라이브러리 에서도 사용할 수 있습니다.

편집하다:

보다 강력한 버전이 있습니다 (Justin McCandless 덕분에 이제 순환 참조를 지원합니다).

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

이것이 제가 사용하고있는 것입니다 :

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

라이브러리 ( "복제본"이라고 함) 가 있습니다. 그것은 내가 아는 임의의 객체의 가장 완전한 재귀 적 복제 / 복사를 제공합니다. 또한 순환 참조를 지원하며 다른 응답에서는 아직 다루지 않습니다.

npm 에서도 찾을 수 있습니다. Node.js뿐만 아니라 브라우저에서도 사용할 수 있습니다.

다음은이를 사용하는 예제입니다.

함께 설치

npm install clone

Ender 포장하십시오.

ender build clone [...]

소스 코드를 수동으로 다운로드 할 수도 있습니다.

그런 다음 소스 코드에서 사용할 수 있습니다.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(면책 조항 : 저는 도서관의 저자입니다.)


자바 스크립트에서 오브젝트를 딥 복사하는 것 (가장 좋고 간단한 방법이라고 생각합니다)

1. JSON.parse (JSON.stringify (object)) 사용;

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2. 생성 된 메소드 사용

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Lo-Dash의 _.cloneDeep 링크 lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Object.assign () 메소드 사용하기

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

그러나 때때로

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5.Underscore.js 사용 .clone 링크 Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

그러나 때때로

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

매체 닷컴 참조

JSBEN.CH 성능 벤치마킹 놀이터 1 ~ 3 http://jsben.ch/KVQLd


Crockford는이 함수를 사용하여 다음과 같이 제안합니다.

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

간결하고 예상대로 작동하며 라이브러리가 필요하지 않습니다.

편집하다:

이것은 polyfill 용 Object.create이므로 이것을 사용할 수도 있습니다.

var newObject = Object.create(oldObject);

참고 : 이 중 일부를 사용하면 일부 반복 사용시 문제가 발생할 수 있습니다 hasOwnProperty. 왜냐하면 create상속받은 새로운 빈 객체를 생성 하기 때문 oldObject입니다. 그러나 객체를 복제하는 것은 여전히 ​​유용하고 실용적입니다.

예를 들어 if oldObject.a = 5;

newObject.a; // is 5

그러나:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

AngularJS 언급 한 것을 보지 못했고 사람들이 알고 싶어 할지도 모른다고 생각 했기 때문에 ...

angular.copy 또한 객체와 배열을 딥 복사하는 방법을 제공합니다.


JSON.parse(JSON.stringify(obj))버전 을 사용 하지만 Date 객체를 잃지 않고 사용하려는 사람들 parsemethod두 번째 인수를 사용 하여 문자열을 Date로 다시 변환 할 수 있습니다 .

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}

나는 here 가장 큰 표를 던지는 대답에 동의하지 않는다 . 재귀 깊은 클론 입니다 훨씬 빠르게JSON.parse (JSON.stringify (OBJ)) 언급 한 방법.

다음은 빠른 참조 기능입니다.

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}

다음은 동일한 객체의 두 인스턴스를 만듭니다. 나는 그것을 발견했고 그것을 현재 사용하고있다. 간단하고 사용하기 쉽습니다.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

배열과 같은 객체에 대해서는 아직 이상적인 딥 클론 연산자가없는 것 같습니다. 아래의 코드에서 알 수 있듯이 John Resig의 jQuery 복제자는 배열이 아닌 객체로 숫자가 아닌 속성의 배열을 변환하고 RegDwight의 JSON 복제자는 비 숫자 속성을 삭제합니다. 다음 테스트는 여러 브라우저에서 이러한 점을 보여줍니다.

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)

얕은 사본 한 줄짜리 ( ECMAScript 5th edition ) :

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

그리고 얕은 복사 원 라이너 ( ECMAScript 6th edition , 2015) :

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};

function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }




clone