จะล้างวัตถุ JavaScript ได้อย่างรวดเร็วได้อย่างไร


170

ด้วย JavaScript Array ฉันสามารถรีเซ็ตเป็นสถานะว่างด้วยการมอบหมายครั้งเดียว:

array.length = 0;

สิ่งนี้ทำให้ Array "ปรากฏ" ว่างเปล่าและพร้อมที่จะนำมาใช้ใหม่และเท่าที่ฉันเข้าใจคือ "การดำเนินการ" เดียว - นั่นคือเวลาคงที่

มีวิธีที่คล้ายกันเพื่อล้างวัตถุ JS หรือไม่ ฉันรู้ว่าฉันสามารถย้ำฟิลด์ของมันเพื่อลบพวกเขา:

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

แต่นี่มีความซับซ้อนเชิงเส้น

ฉันยังสามารถโยนวัตถุออกไปและสร้างวัตถุใหม่ได้:

obj = {};

แต่การสร้าง "ใหม่" ของวัตถุใหม่จะทำให้เกิดปัญหากับ Garbage Collection ใน IE6 ( ตามที่อธิบายไว้ที่นี่ )


2
"array.length == 0 ... เป็น 'การดำเนินการ' เดียว - นั่นคือเวลาคงที่" - ฉันสงสัยว่า
ไมล์

1
ฉันไม่เชื่อว่าจะลบเนื้อหาใด ๆ - เพียงแค่ทำให้สิ่งต่าง ๆ เช่น push () ทำงานเหมือนว่าอาร์เรย์นั้นว่างเปล่า คุณมีการอ้างอิงถึงสิ่งที่ตรงกันข้ามจริงหรือไม่?
levik

2
@derobert: มันค่อนข้างเกรงใจ ปัญหาการรวบรวมขยะ IE6 ได้รับการบันทึกไว้เป็นอย่างดี
levik

รหัสในโพสต์ต้นฉบับไม่ถูกต้อง วงด้านในควรเป็น: ลบ 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
หมายความว่าแอปของคุณทำงานได้ไม่ดีสำหรับกลุ่มเป้าหมายของคุณ บางคนไม่มีตัวเลือกในการอัพเกรดเพราะพวกเขาอยู่ในสภาพแวดล้อมไอทีที่มีการควบคุม บริษัท ของพวกเขาอาจใช้ตัวควบคุม Active-X ที่ใช้งานได้กับ IE6 เท่านั้น
levik

2
@levik ไม่ทำงานกับ บริษัท ที่บังคับให้คุณสนับสนุน IE6 นี่ไม่ใช่ บริษัท ที่ดีเลย
low_rents

121

ความเสี่ยงของการทำสิ่งต่าง ๆ ง่ายเกินไป ...

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

... ดูเหมือนว่าจะมีประสิทธิภาพในการทำความสะอาดวัตถุในโค้ดหนึ่งบรรทัดโดยมีวงเล็บน่ากลัวน้อยที่สุด สมาชิกทั้งหมดจะถูกลบอย่างแท้จริงแทนที่จะเป็นขยะ

เห็นได้ชัดว่าหากคุณต้องการลบวัตถุเองคุณจะต้องทำการลบ () แยกต่างหากสำหรับสิ่งนั้น


2
การลบจะทำลายการอ้างอิงเท่านั้นเช่นทำให้ myObject [สมาชิก] ประเมินว่าไม่ได้กำหนดและในทางเทคนิคจะปล่อยวัตถุเป็นขยะเว้นแต่จะมีการอ้างอิงไปยังวัตถุอื่นอยู่
Joey Carson

คำตอบนี้มีความรับผิดชอบมาก หากนำไปใช้กับวัตถุที่ได้รับการจัดการทั้งหมดในระยะการกำจัดโดยทั่วไปแล้วจะดูแลห่วงโซ่วัตถุอุบัติเหตุจำนวนมาก ร็อคใน Wytze
deepelement

1
OP แนะนำให้ใช้hasOwnPropertyในวงนี้ ฉันอ่านเอกสารแล้ว แต่ฉันยังคงพยายามห้อมล้อมสิ่งที่มีความเสี่ยงถ้าเราไม่ออกhasOwnProperty()เช็ค?
logidelic

2
ฉันคิดว่ามันเป็นอันตรายที่จะลบคุณสมบัติในขณะที่วนซ้ำวัตถุ - ในภาษาส่วนใหญ่จะทำให้ตัววนซ้ำและทำลายสิ่งต่าง ๆ ไม่ว่าจะเป็นแบบย่อยหรือหายนะ - แต่เห็นได้ชัดว่านี่เป็นตกลงใน JavaScript ต่อ MDN: " มันถูกเยี่ยมชมแล้วจะไม่ถูกเยี่ยมชมในภายหลัง " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

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

คุณสามารถลองสิ่งนี้ ฟังก์ชั่นด้านล่างตั้งค่าทั้งหมดของคุณสมบัติของวัตถุเป็นไม่ได้กำหนด ทำงานได้ดีกับวัตถุที่ซ้อนกัน

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 oftener จะทำงาน
  2. ยิ่งคุณมีวัตถุ 'ลอยอยู่ในอากาศ' มากเท่าไหร่เวลาของการรวบรวมขยะก็จะยิ่งมากขึ้นเท่านั้น

วิธีแก้ปัญหาสำหรับสาเหตุที่ 1 น่าจะเป็น: รักษาจำนวนการจัดสรรลง กำหนดวัตถุและสตริงใหม่ให้น้อยที่สุด

วิธีแก้ปัญหาที่ทำให้ 2 ดูเหมือนจะเป็น: รักษาจำนวนวัตถุ 'สด' ลง; ลบสตริงและวัตถุของคุณทันทีที่คุณไม่ต้องการอีกต่อไปและสร้างพวกเขาอีกครั้งเมื่อจำเป็น

ในระดับหนึ่งการแก้ปัญหาเหล่านี้ขัดแย้งกัน: เพื่อให้จำนวนวัตถุในหน่วยความจำเหลือน้อยจะทำให้เกิดการจัดสรรและยกเลิกการจัดสรรมากขึ้น ในทางกลับกันการนำวัตถุเดิมมาใช้ซ้ำอาจหมายถึงการเก็บรักษาวัตถุไว้ในหน่วยความจำมากกว่าที่จำเป็นอย่างเคร่งครัด


ตอนนี้สำหรับคำถามของคุณ ไม่ว่าคุณจะรีเซ็ตวัตถุโดยการสร้างขึ้นใหม่หรือลบคุณสมบัติทั้งหมด: ขึ้นอยู่กับสิ่งที่คุณต้องการจะทำในภายหลัง

คุณอาจต้องการกำหนดคุณสมบัติใหม่ให้:

  • หากคุณทำทันทีฉันแนะนำให้กำหนดคุณสมบัติใหม่ทันทีและข้ามการลบหรือล้างข้อมูลก่อน (ตรวจสอบให้แน่ใจว่าคุณสมบัติทั้งหมดเขียนทับหรือลบ แต่!)
  • หากวัตถุจะไม่ถูกใช้งานทันที แต่จะได้รับการ repopulated ในระยะต่อมาฉันแนะนำให้ลบหรือกำหนดให้เป็นโมฆะและสร้างใหม่ในภายหลัง

ไม่มีวิธีที่รวดเร็วและใช้งานง่ายในการล้างวัตถุ JScript เพื่อนำมาใช้ใหม่ราวกับว่าเป็นวัตถุใหม่ - โดยไม่ต้องสร้างใหม่ ซึ่งหมายความว่าคำตอบสั้น ๆ สำหรับคำถามของคุณคือ 'ไม่' อย่างที่ jthompson พูด


4

สิ่งใหม่ที่คิดเกี่ยวกับการรอคอยที่จะ Object.observe ใน ES7 และมีผลผูกพันข้อมูลโดยทั่วไป พิจารณา:

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

จุดดี. ฉันต้องการเก็บวัตถุฮีปเดียวกันไว้และนี่แสดงให้เห็นเหตุผลอื่นที่คุณต้องการเก็บข้อมูลอ้างอิงเดียวกัน
โคดี้

1

คุณสามารถลบอุปกรณ์ประกอบฉาก แต่ไม่ต้องลบตัวแปร delete abc;ไม่ถูกต้องใน ES5 (และใช้งานอย่างเข้มงวด)

คุณสามารถกำหนดให้เป็นโมฆะเพื่อตั้งค่าการลบให้กับ GC (ไม่ใช่หากคุณมีการอ้างอิงอื่น ๆ ถึงคุณสมบัติ)

การตั้งค่าlengthคุณสมบัติบนวัตถุจะไม่เปลี่ยนแปลงอะไร (มันเท่านั้นดีตั้งค่าคุณสมบัติ)


เพื่อความชัดเจนเมื่อตั้งค่าlengthเป็น 0 ในอาเรย์ (ซึ่งเป็นประเภทของวัตถุเฉพาะ - ถ้าคุณไม่เชื่อฉันลองใช้typeof []) มันจะไม่เพียง แต่ตั้งค่าคุณสมบัติมันจะล้างเนื้อหาของอาร์เรย์ . ดูstackoverflow.com/questions/1232040/…
Sean the Bean

คุณสามารถพูดลบได้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.