Làm cách nào để nhanh chóng xóa một đối tượng JavaScript?


170

Với Mảng JavaScript, tôi có thể đặt lại nó về trạng thái trống bằng một nhiệm vụ duy nhất:

array.length = 0;

Điều này làm cho Mảng "xuất hiện" trống rỗng và sẵn sàng để sử dụng lại, và theo như tôi hiểu là một "thao tác" duy nhất - đó là thời gian không đổi.

Có cách nào tương tự để xóa đối tượng JS không? Tôi biết tôi có thể lặp lại các trường của nó xóa chúng:

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

nhưng điều này có độ phức tạp tuyến tính.

Tôi cũng có thể ném vật thể đi và tạo một cái mới:

obj = {};

Nhưng việc tạo ra các đối tượng mới "bừa bãi" dẫn đến các vấn đề với Bộ sưu tập rác trên IE6. ( Như được mô tả ở đây )


2
"Array.length == 0 ... là một 'hoạt động' duy nhất - nghĩa là thời gian không đổi" - tôi nghi ngờ điều đó.
Miles

1
Tôi không tin rằng nó xóa bất kỳ nội dung nào - chỉ làm cho những thứ như Push () hoạt động như thể mảng trống. Bạn có một tham chiếu đến điều ngược lại là đúng?
levik

2
@derobert: Đó là một chút tự phụ. Vấn đề thu gom rác IE6 được ghi lại rõ ràng.
levik

Mã trong bài viết gốc là không chính xác. Vòng lặp bên trong phải là: xóa obj [prop]. Xem bài đăng stackoverflow.com/questions/6780415/
stackoverflowuser2010

6
đối với bất kỳ ai sử dụng đoạn mã đó, hãy sử dụngfor (var prop in obj)
bentytree

Câu trả lời:


54

Câu trả lời ngắn cho câu hỏi của bạn, tôi nghĩ là không (bạn chỉ có thể tạo một đối tượng mới).

  1. Trong ví dụ này, tôi tin rằng việc đặt độ dài thành 0 vẫn để lại tất cả các yếu tố cho bộ sưu tập rác.

  2. Bạn có thể thêm nó vào Object.prototype nếu đó là thứ bạn thường xuyên sử dụng. Vâng, đó là sự phức tạp tuyến tính, nhưng bất cứ điều gì không làm bộ sưu tập rác sau này sẽ là.

  3. Đây là giải pháp tốt nhất. Tôi biết nó không liên quan đến câu hỏi của bạn - nhưng chúng ta cần tiếp tục hỗ trợ IE6 trong bao lâu? Có nhiều chiến dịch ngừng sử dụng nó.

Hãy sửa tôi nếu có gì không đúng ở trên.


4
Một số công ty phải hỗ trợ IE6 như một vấn đề chính sách - và trong khi nó sẽ thích thị phần hai chữ số. Vấn đề về IE IE không phải là mọi thứ không bị ảnh hưởng, đó là bộ sưu tập chạy mọi phân bổ X và mất nhiều thời gian hơn mỗi lần. Do đó, cần phải sử dụng lại các đối tượng.
levik

Vâng, có nhiều trường hợp nó vẫn được sử dụng do chính sách của công ty / v.v. Tôi đã bị loại khỏi chủ đề xếp hạng mặc dù vậy :) Làm thế nào để xóa obj.prop; thực hiện khi tài sản là một đối tượng? Tôi không biết nếu bạn đạt được nhiều hiệu quả ở đó.
jthompson

2
GC kém có nghĩa là IE6 sẽ chạy chậm hơn, có nghĩa là thậm chí còn có nhiều động lực để nâng cấp. Bạn vẫn đang hỗ trợ nó, chỉ là nó sẽ chạy chậm.
nickf

1
Nó có nghĩa là ứng dụng của bạn hoạt động kém đối với một phần đối tượng mục tiêu của bạn. Một số người không có tùy chọn nâng cấp vì họ ở trong môi trường CNTT được kiểm soát. Có lẽ công ty của họ sử dụng điều khiển Active-X chỉ hoạt động với IE6.
levik

2
@levik chỉ không làm việc cho một công ty buộc bạn phải hỗ trợ IE6. Dù sao đây cũng không thể là một công ty tốt.
low_rents 10/07/2015

121

Chà, có nguy cơ khiến mọi thứ trở nên quá dễ dàng ...

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

... dường như khá hiệu quả trong việc làm sạch đối tượng trong một dòng mã với tối thiểu các dấu ngoặc đáng sợ. Tất cả các thành viên sẽ thực sự bị xóa thay vì để lại như rác.

Rõ ràng nếu bạn muốn xóa chính đối tượng đó, bạn vẫn sẽ phải xóa riêng () cho điều đó.


2
Việc xóa chỉ phá vỡ tham chiếu, ví dụ, nó làm cho myObject [thành viên] đánh giá thành không xác định và về mặt kỹ thuật không để đối tượng là rác trừ khi có tham chiếu khác đến đối tượng.
Joey Carson

Câu trả lời này rất có trách nhiệm. Nếu được áp dụng cho tất cả các đối tượng được quản lý trong một pha xử lý, nó thường sẽ chăm sóc nhiều chuỗi đối tượng tình cờ. đá trên Wytze
deepelement

1
OP đề nghị sử dụng hasOwnPropertytrong vòng lặp này. Tôi đã đọc các tài liệu, nhưng tôi vẫn đang cố gắng che giấu những rủi ro, nếu có, là rủi ro nếu chúng ta bỏ qua hasOwnProperty()kiểm tra?
logidelic

2
Tôi nghĩ rằng thật nguy hiểm khi xóa các thuộc tính trong khi lặp qua đối tượng - trong hầu hết các ngôn ngữ, điều này sẽ làm mất hiệu lực của trình lặp và phá vỡ mọi thứ, một cách tinh vi hoặc thảm khốc - nhưng rõ ràng điều này là OK trong MDN: "Một thuộc tính đã bị xóa trước đó nó đã được truy cập sẽ không được truy cập sau này. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/,
Piotr

46

ES5

Giải pháp ES5 có thể là:

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

ES6

Và giải pháp ES6 có thể là:

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

Hiệu suất

Bất kể thông số kỹ thuật, các giải pháp nhanh nhất thường sẽ là:

// 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];
}

Lý do tại sao for..inchỉ nên được thực hiện trên đối tượng nông hoặc đơn giản là vì nó đi qua các thuộc tính được kế thừa nguyên mẫu, không chỉ các thuộc tính riêng có thể bị xóa. Trong trường hợp không biết chắc chắn rằng một đối tượng là đơn giản và các thuộc tính là vô số, forvới Object.getOwnPropertyNamessự lựa chọn tốt hơn.


7

Bạn có thể thử điều này. Hàm bên dưới đặt tất cả các giá trị thuộc tính của đối tượng thành không xác định. Hoạt động tốt với các đối tượng lồng nhau.

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

Lưu ý vì các trường chỉ được đặt là không xác định, điều này sẽ gây rò rỉ bộ nhớ theo thời gian vì các trường sẽ không bị xóa bởi trình thu gom rác.
Alexis Tyler

7

Vì vậy, để tóm tắt lại câu hỏi của bạn: bạn muốn tránh, càng nhiều càng tốt, rắc rối với lỗi IE6 GC. Lỗi đó có hai nguyên nhân:

  1. Thu gom rác xảy ra một lần rất nhiều phân bổ ; do đó, càng nhiều phân bổ bạn thực hiện, thì càng nhiều GC sẽ chạy;
  2. Càng có nhiều đối tượng bạn có 'trên không', mỗi lần chạy Bộ sưu tập rác càng mất nhiều thời gian (vì nó sẽ bò qua toàn bộ danh sách các đối tượng để xem những đối tượng được đánh dấu là rác).

Giải pháp cho nguyên nhân 1 dường như là: giữ số lượng phân bổ xuống; gán các đối tượng và chuỗi mới ít nhất có thể.

Giải pháp để gây ra 2 dường như là: giữ số lượng đối tượng 'sống' xuống; xóa các chuỗi và đối tượng của bạn ngay khi bạn không cần chúng nữa và tạo chúng ngay khi cần thiết.

Ở một mức độ nhất định, các giải pháp này trái ngược nhau: để giữ cho số lượng đối tượng trong bộ nhớ thấp sẽ đòi hỏi nhiều phân bổ và phân bổ hơn. Ngược lại, liên tục sử dụng lại các đối tượng tương tự có thể có nghĩa là giữ nhiều đối tượng trong bộ nhớ hơn mức cần thiết.


Bây giờ cho câu hỏi của bạn. Cho dù bạn sẽ đặt lại một đối tượng bằng cách tạo một đối tượng mới hoặc bằng cách xóa tất cả các thuộc tính của nó: điều đó sẽ phụ thuộc vào những gì bạn muốn làm với nó sau đó.

Bạn có thể muốn gán các thuộc tính mới cho nó:

  • Nếu bạn làm như vậy ngay lập tức, thì tôi khuyên bạn nên gán ngay các thuộc tính mới và bỏ qua việc xóa hoặc xóa trước. (Tuy nhiên, hãy đảm bảo rằng tất cả các thuộc tính được ghi đè hoặc bị xóa!)
  • Nếu đối tượng sẽ không được sử dụng ngay lập tức, nhưng sẽ được lặp lại ở giai đoạn sau, thì tôi khuyên bạn nên xóa nó hoặc gán cho nó null và tạo một cái mới sau này.

Không có cách nhanh chóng, dễ sử dụng để xóa đối tượng JScript để sử dụng lại như thể nó là một đối tượng mới - mà không tạo đối tượng mới. Điều đó có nghĩa là câu trả lời ngắn cho câu hỏi của bạn là 'Không', như jthedom nói.


4

Một cái gì đó mới để suy nghĩ về việc mong chờ Object.observe trong ES7 và nói chung với dữ liệu ràng buộc. Xem xét:

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

Vì vậy, trong hoàn cảnh đó # 2 có thể là sự lựa chọn tốt nhất.


Nếu sử dụng các khung như AngularJS có thể liên kết các đối tượng với các khung nhìn (ràng buộc một hoặc hai chiều), việc xóa đối tượng obj = {}sẽ làm cho khung không biết về bất kỳ thay đổi nào đối với đối tượng do đó các mẫu của bạn sẽ không được hiển thị đúng. Tùy chọn # 2 tuy nhiên, sẽ hoạt động đúng.
Ivan Hušnjak

Điểm tốt. Tôi cần phải giữ cùng một đối tượng heap và điều này thể hiện một lý do khác mà bạn muốn giữ cùng một tham chiếu.
Cody

1

Bạn có thể xóa các đạo cụ, nhưng không xóa các biến. delete abc;không hợp lệ trong ES5 (và sử dụng nghiêm ngặt).

Bạn có thể gán nó thành null để thiết lập xóa nó cho GC (nó sẽ không có nếu bạn có các tham chiếu khác đến các thuộc tính)

Đặt thuộc lengthtính trên một đối tượng không thay đổi bất cứ điều gì. (chỉ, tốt, thiết lập tài sản)


Để rõ ràng, khi đặt lengththành 0 trên một mảng (là một loại đối tượng cụ thể - nếu bạn không tin tôi, hãy thử chạy typeof []), nó sẽ không chỉ đặt thuộc tính, trên thực tế nó sẽ xóa nội dung của mảng . Xem stackoverflow.com/questions/1232040/ Mạnh
Sean the Bean

Chà, bạn có thể nói xóa window.abcvì đó là chỗ dựa trong trường hợp đó.
shaedrich

0

Điều này đã làm tôi khó chịu từ lâu vì vậy đây là phiên bản của tôi vì tôi không muốn một đối tượng trống, tôi muốn một đối tượng có tất cả các thuộc tính nhưng đặt lại về một số giá trị mặc định. Kiểu như một khởi tạo mới của một lớp.

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 }

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.