JavaScript 객체를 빠르게 지우는 방법은 무엇입니까?


170

JavaScript 배열을 사용하면 단일 할당으로 빈 상태로 재설정 할 수 있습니다.

array.length = 0;

이렇게하면 어레이가 비어 있고 재사용 할 준비가되며 하나의 "조작"즉, 일정한 시간이라는 것을 알 수 있습니다.

JS 객체를 지우는 비슷한 방법이 있습니까? 필드를 삭제하여 삭제할 수 있다는 것을 알고 있습니다.

for (var prop in obj) { if (obj.hasOwnProperty(prop)) { delete obj[prop]; } }

그러나 이것은 선형적인 복잡성을 가지고 있습니다.

객체를 버리고 새 객체를 만들 수도 있습니다.

obj = {};

그러나 새 객체를 "차단 성"으로 만들면 IE6의 가비지 수집에 문제가 발생합니다. ( 여기에 설명 된대로 )


2
"array.length == 0 ...은 단일 '작업'입니다. 즉, 일정한 시간입니다."— 의심합니다.
Miles

1
나는 그것이 내용을 삭제한다고 생각하지 않습니다-단지 배열이 비어있는 것처럼 push () 같은 것들을 작동시킵니다. 당신은 정반대 사실에 대한 언급이 있습니까?
levik

2
@derobert : 그건 조금 어지럽습니다. IE6 가비지 콜렉션 문제는 잘 문서화되어 있습니다.
levik

원래 게시물의 코드가 잘못되었습니다. 내부 루프는 다음과 같아야합니다. delete obj [prop]. 내 게시물을 참조하십시오 stackoverflow.com/questions/6780315/…
stackoverflowuser2010

6
해당 스 니펫을 사용하는 사람은 다음을 사용하십시오.for (var prop in obj)
bendytree

답변:


54

귀하의 질문에 대한 짧은 대답은 아니오라고 생각합니다 (새로운 객체를 만들 수 있습니다).

  1. 이 예제에서 길이를 0으로 설정하면 여전히 가비지 수집을위한 모든 요소가 남습니다.

  2. 자주 사용하는 Object.prototype에 추가 할 수 있습니다. 그렇습니다. 복잡하게 선형 적이지만 나중에 가비지 수집을 수행하지 않는 것이 될 것입니다.

  3. 이것이 가장 좋은 해결책입니다. 귀하의 질문과 관련이 없다는 것을 알고 있습니다. 그러나 IE6 지원을 얼마나 오래 지속해야합니까? 사용을 중단하는 캠페인이 많이 있습니다.

위에 잘못된 것이 있으면 언제든지 수정하십시오.


4
일부 회사는 정책 문제로 IE6를 지원해야하며 두 자리 수의 시장 점유율을 누릴 것입니다. IE GC 문제는 수집되지 않은 것이 아니라 컬렉션이 모든 X 할당을 실행하며 매번 더 오래 걸리는 것입니다. 따라서 객체를 재사용해야합니다.
levik

예, 회사 정책 등으로 인해 여전히 사용되는 경우가 많습니다. 나는 스프라이트에서 주제 순위를 벗어났습니다. :) 어떻게 obj.prop를 삭제합니까? 속성 자체가 객체 일 때 수행합니까? 당신이 거기에서 많은 효율성을 얻는 지 모르겠습니다.
jthompson

2
GC가 낮 으면 IE6이 느리게 실행되므로 업그레이드에 대한 인센티브가 훨씬 높아집니다. 당신은 여전히 ​​그것을 지원하고 있습니다, 그것은 단지 느리게 실행될 것입니다.
nickf

1
타겟 잠재 고객의 일부에 대해 앱의 성능이 저하되었음을 의미합니다. 일부 사람들은 통제 된 IT 환경에 있기 때문에 업그레이드 옵션이 없습니다. 아마도 그들의 회사는 IE6에서만 작동하는 Active-X 컨트롤을 사용합니다.
levik

2
@levik은 IE6를 지원하도록 강요하는 회사에서는 효과가 없습니다. 어쨌든 이것은 좋은 회사가 될 수 없습니다.
low_rents

121

글쎄, 물건을 너무 쉽게 만들 위험이 있습니다 ...

for (var member in myObject) delete myObject[member];

... 최소한 무서운 괄호를 사용하여 한 줄의 코드로 객체를 청소하는 데 꽤 효과적 인 것 같습니다. 모든 회원은 가비지로 남지 않고 실제로 삭제됩니다.

분명히 객체 자체를 삭제하려면 여전히 별도의 delete ()를 수행해야합니다.


2
삭제는 참조 만 끊습니다. 예를 들어 myObject [member]는 정의되지 않은 것으로 평가하고, 오브젝트에 대한 다른 참조가 없으면 기술적으로 오브젝트를 가비지로 남겨 둡니다.
Joey Carson

이 답변은 매우 책임이 있습니다. 폐기 단계에서 모든 관리 대상 개체에 적용되는 경우 일반적으로 실수로 많은 개체 체인을 처리합니다. Wytze에 rock
deepelement

1
OP는 hasOwnProperty이 루프에서의 사용 을 제안 합니다. 나는 문서를 읽었지만 hasOwnProperty()수표를 생략하면 위험이 무엇인지 주위에 머리를 감 으려고 여전히 노력하고 있습니까?
logidelic

2
객체를 반복하는 동안 속성을 삭제하는 것이 위험하다고 생각했습니다. 대부분의 언어에서는 이터레이터를 무효화하고 미묘하게 또는 치명적으로 일을 중단시킬 것입니다. 방문한 날짜는 나중에 방문하지 않습니다. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Piotr

46

ES5

ES5 솔루션은 다음과 같습니다.

// for enumerable and non-enumerable properties
Object.getOwnPropertyNames(obj).forEach(function (prop) {
  delete obj[prop];
});

ES6

ES6 솔루션은 다음과 같습니다.

// for enumerable and non-enumerable properties
for (const prop of Object.getOwnPropertyNames(obj)) {
  delete obj[prop];
}

공연

사양에 관계없이 가장 빠른 솔루션은 일반적으로 다음과 같습니다.

// for enumerable and non-enumerable of an object with proto chain
var props = Object.getOwnPropertyNames(obj);
for (var i = 0; i < props.length; i++) {
  delete obj[props[i]];
}

// for enumerable properties of shallow/plain object
for (var key in obj) {
  // this check can be safely omitted in modern JS engines
  // if (obj.hasOwnProperty(key))
    delete obj[key];
}

for..in얕은 객체 나 일반 객체에서만 수행해야하는 이유 는 삭제 가능한 고유 속성이 아니라 프로토 타입으로 상속 된 속성을 통과하기 때문입니다. 경우 개체가 일반과 특성, 열거 것을 확실히 알려져 있지 않다 for으로 Object.getOwnPropertyNames더 나은 선택입니다.


7

당신은 이것을 시도 할 수 있습니다. 아래 함수는 객체 속성의 모든 값을 undefined로 설정합니다. 중첩 된 객체와 함께 작동합니다.

var clearObjectValues = (objToClear) => {
    Object.keys(objToClear).forEach((param) => {
        if ( (objToClear[param]).toString() === "[object Object]" ) {
            clearObjectValues(objToClear[param]);
        } else {
            objToClear[param] = undefined;
        }
    })
    return objToClear;
};

필드는 정의되지 않은 상태로만 설정되므로 가비지 수집기에서 필드를 지우지 않으므로 시간이 지남에 따라 메모리 누수가 발생합니다.
Alexis Tyler

7

그래서 당신의 질문을 요약하면 : 가능한 한 IE6 GC 버그와 관련된 문제를 피하고 싶습니다. 이 버그에는 두 가지 원인이 있습니다.

  1. 가비지 콜렉션은 많은 할당 마다 한 번씩 발생합니다 . 따라서 할당을 많이할수록 GC가 더 자주 실행됩니다.
  2. '공중'에있는 오브젝트가 많을수록 가비지 콜렉션 실행에 걸리는 시간이 길어집니다 (가비지로 표시된 오브젝트를보기 위해 전체 오브젝트 목록을 크롤링하기 때문).

1을 일으키는 해결책은 다음과 같습니다. 할당 수를 줄입니다. 가능한 한 적은 새 객체와 문자열을 할당하십시오.

2를 일으키는 해결책은 다음과 같습니다. '실제'객체 수를 유지하십시오. 더 이상 필요하지 않은 문자열과 객체를 삭제하고 필요할 때 새로 만듭니다.

어느 정도까지는 이러한 솔루션이 모순됩니다. 메모리에있는 객체 수를 낮게 유지하려면 더 많은 할당 및 할당 해제가 수반됩니다. 반대로, 동일한 객체를 지속적으로 재사용한다는 것은 꼭 필요한 것보다 더 많은 객체를 메모리에 유지하는 것을 의미 할 수 있습니다.


이제 질문하십시오. 새 객체를 생성하거나 모든 속성을 삭제하여 객체를 재설정할지 여부는 나중에 수행하려는 작업에 따라 다릅니다.

아마도 새로운 속성을 할당하고 싶을 것입니다.

  • 그렇게하면 즉시 새 속성을 할당하고 먼저 삭제 또는 지우기를 건너 뛰는 것이 좋습니다. (그러나 모든 속성을 덮어 쓰거나 삭제 해야합니다 !)
  • 객체가 즉시 사용되지 않지만 나중에 다시 채워지면 객체를 삭제하거나 null을 할당하고 나중에 새 객체를 생성하는 것이 좋습니다.

JScript 객체를 새 객체를 만들지 않고 마치 새로운 객체 인 것처럼 재사용 할 수있는 빠르고 쉬운 방법은 없습니다. jthompson이 말하는 것처럼 귀하의 질문에 대한 짧은 대답은 '아니요'입니다.


4

ES7에서 Object.observe를 기대하고 일반적으로 데이터 바인딩을 고려할 때 고려해야 할 새로운 사항. 치다:

var foo={
   name: "hello"
};

Object.observe(foo, function(){alert('modified');}); // bind to foo

foo={}; // You are no longer bound to foo but to an orphaned version of it
foo.name="there"; // This change will be missed by Object.observe()

따라서 이러한 상황에서 # 2가 최선의 선택이 될 수 있습니다.


객체를 뷰에 바인딩 할 수있는 AngularJS와 같은 프레임 워크를 사용하는 경우 (일방 또는 양방향 바인딩) 객체를 지우면 obj = {}프레임 워크가 객체 에 대한 추가 변경 사항을 인식하지 못하므로 템플릿이 올바르게 렌더링되지 않습니다. 그러나 옵션 # 2는 제대로 작동합니다.
Ivan Hušnjak

좋은 지적. 동일한 힙 객체를 유지해야하며 동일한 참조를 유지하려는 또 다른 이유를 보여줍니다.
Cody

1

소품을 삭제할 수는 있지만 변수는 삭제할 수 없습니다. delete abc;ES5에서는 유효하지 않으며 엄격하게 사용합니다.

GC에 삭제하도록 설정하려면 null로 할당 할 수 있습니다 (속성에 대한 다른 참조가있는 경우는 아닙니다)

설정 length개체에 대한 특성은 아무것도 변경되지 않습니다. (그것은 단지 속성을 설정합니다)


분명히하기 length위해 배열에서 특정 객체 유형 인 0으로 설정할 때 (믿지 않는 경우 실행을 시도 typeof []하십시오) 속성을 설정 할뿐만 아니라 실제로 배열의 내용을 지 웁니다. . 참조 stackoverflow.com/questions/1232040/...
숀 빈이

글쎄, 당신은 delete window.abc이 경우에 소품이기 때문에 말할 수 있습니다 .
shaedrich

0

이것은 오랫동안 나를 괴롭 혔습니다. 여기서 빈 객체를 원하지 않는 내 버전이 있습니다. 모든 속성을 가진 것을 원했지만 기본값으로 재설정했습니다. 클래스의 새로운 인스턴스화와 같은 종류.

let object1 = {
  a: 'somestring',
  b: 42,
  c: true,
  d:{
    e:1,
    f:2,
    g:true,
    h:{
      i:"hello"
    }
  },
  j: [1,2,3],
  k: ["foo", "bar"],
  l:["foo",1,true],
  m:[{n:10, o:"food", p:true }, {n:11, o:"foog", p:true }],
  q:null,
  r:undefined
};

let boolDefault = false;
let stringDefault = "";
let numberDefault = 0;

console.log(object1);
//document.write("<pre>");
//document.write(JSON.stringify(object1))
//document.write("<hr />");
cleanObject(object1);
console.log(object1);
//document.write(JSON.stringify(object1));
//document.write("</pre>");

function cleanObject(o) {
  for (let [key, value] of Object.entries(o)) {
    let propType = typeof(o[key]);

    //console.log(key, value, propType);

    switch (propType) {
      case "number" :
        o[key] = numberDefault;
        break;

      case "string":
        o[key] = stringDefault;
        break;

      case "boolean":
        o[key] = boolDefault;    
        break;

      case "undefined":
        o[key] = undefined;   
        break;

      default:
        if(value === null) {
            continue;
        }

        cleanObject(o[key]);
        break;
    }
  }
}

// EXPECTED OUTPUT
// Object { a: "somestring", b: 42, c: true, d: Object { e: 1, f: 2, g: true, h: Object { i: "hello" } }, j: Array [1, 2, 3], k: Array ["foo", "bar"], l: Array ["foo", 1, true], m: Array [Object { n: 10, o: "food", p: true }, Object { n: 11, o: "foog", p: true }], q: null, r: undefined }
// Object { a: "", b: 0, c: undefined, d: Object { e: 0, f: 0, g: undefined, h: Object { i: "" } }, j: Array [0, 0, 0], k: Array ["", ""], l: Array ["", 0, undefined], m: Array [Object { n: 0, o: "", p: undefined }, Object { n: 0, o: "", p: undefined }], q: null, r: undefined }

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.