Sự khác biệt giữa đóng băng và đóng dấu


164

Tôi chỉ nghe nói về các phương thức JavaScript freezeseal, có thể được sử dụng để làm cho bất kỳ Đối tượng nào không thay đổi.

Đây là một ví dụ ngắn về cách sử dụng nó:

var o1 = {}, o2 = {};
Object.freeze(o2);

o1["a"] = "worked";
o2["a"] = "worked";

alert(o1["a"]);   //prints "worked"
alert(o2["a"]);   //prints "undefined"

Sự khác biệt giữa freezevà là sealgì? Họ có thể tăng hiệu suất?


6
Chỉ cần một lưu ý cho bất cứ ai nhìn vào câu hỏi này, câu trả lời được chấp nhận là thực tế không chính xác. Câu trả lời của @ tungd là đúng.
Bjorn

2
Một lưu ý khác, Object.preventExtensionsngoài ra còn có Object.sealObject.freeze. Object.preventExtensionschỉ ngăn các mục mới được thêm vào một đối tượng. Bạn có thể xóa, định cấu hình và thay đổi giá trị của các thuộc tính trên các đối tượng đã tắt tính năng mở rộng của chúng Object.preventExtensions.
Bjorn

1
Cập nhật gần đây: v8.dev/blog/v8-release-76#f Frozen% 2Fseal
Mike

Câu trả lời:


193

Object.seal

  • Nó ngăn chặn việc thêm và / hoặc loại bỏ các thuộc tính khỏi đối tượng kín; sử dụng deletesẽ trả về false
  • Nó làm cho mọi thuộc tính hiện tại không thể định cấu hình : chúng không thể được chuyển đổi từ 'mô tả dữ liệu' thành 'mô tả truy cập' (và ngược lại), và không có thuộc tính nào của mô tả truy cập có thể thay đổi cả (trong khi mô tả dữ liệu có thể thay đổi writablethuộc tính của chúng và valuethuộc tính của họ nếu writeablelà đúng).
  • Có thể ném TypeErrorkhi cố gắng sửa đổi giá trị của chính đối tượng được niêm phong (phổ biến nhất ở chế độ nghiêm ngặt )

Object.freeze

  • Chính xác cái gì Object.seal không, cộng với:
  • Nó ngăn chặn sửa đổi bất kỳ thuộc tính hiện có

Không ai ảnh hưởng đến các đối tượng 'sâu' / cháu. Ví dụ: nếu objbị đóng băng, obj.elkhông thể được chỉ định lại, nhưng giá trị của obj.elcó thể được sửa đổi, ví dụ obj.el.idcó thể được thay đổi.


Hiệu suất:

Niêm phong hoặc đóng băng một đối tượng có thể ảnh hưởng đến tốc độ liệt kê của nó, tùy thuộc vào trình duyệt:

  • Firefox: hiệu suất liệt kê không bị ảnh hưởng
  • IE: tác động hiệu suất liệt kê là không đáng kể
  • Chrome: hiệu suất liệt kê nhanh hơn với các đối tượng được niêm phong hoặc đông lạnh
  • Safari: các đối tượng được niêm phong hoặc đông lạnh liệt kê chậm hơn 92% (tính đến năm 2014)

Các xét nghiệm: Các vật bịt kín , các vật đông lạnh .


2
Bạn có thể nói về lý do tại sao chúng ta sẽ sử dụng các phương pháp này? Chỉ vì chúng ta có thể?
Alan Dong

3
Trong tương lai tôi nghĩ chúng sẽ được sử dụng rất nhiều (nếu được tối ưu hóa chính xác) khi phát triển thư viện / khung. Chúng cho phép bạn ngăn người dùng (thậm chí vô tình) phá mã của bạn (và, như đã nêu trong câu trả lời, tối ưu hóa sẽ dẫn đến cải thiện tốc độ lớn). Nhưng đây chỉ là suy đoán thuần túy :)
Niccolò Campolungo

2
Câu trả lời này có nhiều lỗi thực tế. Đối với một, sealcũng làm cho các thuộc tính hiện tại không thể định cấu hình, xem jsfiddle.net/btipling/6m743whn Số 2, bạn vẫn có thể chỉnh sửa, đó là thay đổi giá trị của các thuộc tính hiện có trên một đối tượng được niêm phong.
Bjorn

8
FWIW, các đối tượng bị đóng băng và bịt kín hiện nhanh hơn so với các đối tác không bị đóng băng và không được bảo vệ trong Chrome Canary v43.0.2317.0.
llambda

2
@AlanDong Hơi muộn một chút, nhưng đây là lý do tại sao bạn muốn khóa một đối tượng. Một trong những tính năng của JavaScript là bạn có thể thêm một tài sản bất cứ lúc nào bạn muốn; bạn cũng có thể vô tình làm điều này bằng cách gõ sai. Nhiều sinh viên của tôi đã cố gắng thêm một trình xử lý sự kiện được gọi onClickhoặc onlickvà tự hỏi tại sao nó không hoạt động. Nếu JavaScript đưa ra một lỗi, thì đó là một điều ít sai. Thứ hai, điều này cho phép bạn thực hiện các thuộc tính không đổi trên một đối tượng ngăn chặn các thay đổi. Điều này đặc biệt hữu ích trên methjods đối tượng.
Manngo

119

Tôi đã viết một dự án thử nghiệm so sánh 3 phương pháp sau:

  • Object.freeze()
  • Object.seal()
  • Object.preventExtensions()

Bài kiểm tra đơn vị của tôi bao gồm các trường hợp CRUD:

  • [C] thêm tài sản mới
  • [R] đọc tài sản tồn tại
  • [U] sửa đổi tài sản tồn tại
  • [D] xóa tài sản tồn tại

Kết quả:

nhập mô tả hình ảnh ở đây


2
Điều này thật tuyệt vời. CẬP NHẬT có tính đến việc sửa đổi tài khoản (thông qua định nghĩa), các thuộc tính mô tả, ví dụ: có thể định cấu hình, đếm được, ghi được không?
Drenai

Tôi luôn luôn mặc dù các đối tượng DOM nên được niêm phong (tất nhiên là sau các polyfill). Điều đó sẽ giúp ngăn ngừa rất nhiều lỗi chính tả.
Manngo

@Manngo Bạn có thể niêm phong các đối tượng DOM của bạn. Đơn giản chỉ cần tạo một DEBUGMODEbiến và đặt nó thành true. Sau đó, làm if (DEBUGMODE) { ... }. Trong ..., đặt chức năng của bạn để đảm bảo tất cả các đối tượng DOM luôn được niêm phong. Sau đó, khi bạn sẵn sàng phân phối tập lệnh trang web của mình, hãy đổi DEBUGMODEthành false, chạy tập lệnh của bạn thông qua trình biên dịch đóng và phân phối nó. Nó đơn giản như vậy.
Jack Giffin

@JackGiffin Cảm ơn bạn đã bình luận. Tôi chỉ nói rằng tôi luôn nghĩ rằng nó sẽ là một ý tưởng tốt. Tôi có rất nhiều sinh viên cuối cùng đã gõ một cái gì đó giống như element.onlick=somethingvà cảm thấy thất vọng vì nó không hoạt động, nhưng về mặt kỹ thuật không phải là một lỗi.
Manngo

2
@Lonely Sau đó, nó sẽ không đánh vần CRUD. Bạn sẽ phải giải quyết một cái gì đó như RUDE;)
Manngo

84

Bạn luôn có thể tra cứu chúng trong MDN. Nói ngắn gọn:

  • Đóng băng : làm cho đối tượng bất biến, nghĩa là không thay đổi đối với thuộc tính được xác định cho phép, trừ khi chúng là đối tượng.
  • Con dấu : ngăn chặn thêm các thuộc tính, tuy nhiên các thuộc tính được xác định vẫn có thể được thay đổi.

1
Object.seal()dường như cũng đóng băng các thuộc tính nguyên mẫu: \
K ..

10

Object.freeze()tạo ra một đối tượng đóng băng, có nghĩa là nó lấy một đối tượng hiện có và về cơ bản gọi Object.seal()nó, nhưng nó cũng đánh dấu tất cả các thuộc tính của dữ liệu truy cập dữ liệu writable:false, như vậy để các giá trị của chúng không thể thay đổi. - Kyle Simpson, Bạn không biết JS - Nguyên mẫu & Đối tượng này


4

Tôi đã xem xét sự khác biệt giữa Freeze và Seal trong ECMAScript 5 và tạo ra một kịch bản để làm rõ sự khác biệt. Frozen tạo ra một đối tượng bất biến bao gồm dữ liệu và cấu trúc. Con dấu ngăn chặn các thay đổi đối với các giao diện được đặt tên - không thêm, xóa - nhưng bạn có thể thay đổi đối tượng và xác định lại ý nghĩa của giao diện.

function run()
{
    var myObject = function() 
    { 
        this.test = "testing"; 
    }

    //***************************SETUP****************************

    var frozenObj = new myObject();
    var sealedObj = new myObject();

    var allFrozen = Object.freeze(frozenObj);
    var allSealed = Object.seal(sealedObj);
    alert("frozenObj of myObject type now frozen - Property test= " + frozenObj.test);
    alert("sealedObj of myObject type now frozen - Property test= " + sealedObj.test);

    //***************************FROZEN****************************

    frozenObj.addedProperty = "added Property"; //ignores add
    alert("Frozen addedProperty= " + frozenObj.addedProperty);
    delete frozenObj.test; //ignores delete
    alert("Frozen so deleted property still exists= " + frozenObj.test);
    frozenObj.test = "Howdy"; //ignores update
    alert("Frozen ignores update to value= " + frozenObj.test);
    frozenObj.test = function() { return "function"; } //ignores
    alert("Frozen so ignores redefinition of value= " + frozenObj.test);

    alert("Is frozen " + Object.isFrozen(frozenObj));
    alert("Is sealed " + Object.isSealed(frozenObj));
    alert("Is extensible " + Object.isExtensible(frozenObj));

    alert("Cannot unfreeze");
    alert("result of freeze same as the original object: " + (frozenObj === allFrozen).toString());

    alert("Date.now = " + Date.now());

    //***************************SEALED****************************

    sealedObj.addedProperty = "added Property"; //ignores add
    alert("Sealed addedProperty= " + sealedObj.addedProperty);
    sealedObj.test = "Howdy"; //allows update
    alert("Sealed allows update to value unlike frozen= " + sealedObj.test);
    sealedObj.test = function() { return "function"; } //allows
    alert("Sealed allows redefinition of value unlike frozen= " + sealedObj.test);
    delete sealedObj.test; //ignores delete
    alert("Sealed so deleted property still exists= " + sealedObj.test);
    alert("Is frozen " + Object.isFrozen(sealedObj));
    alert("Is sealed " + Object.isSealed(sealedObj));
    alert("Is extensible " + Object.isExtensible(sealedObj));

    alert("Cannot unseal");
    alert("result of seal same as the original object: " + (sealedObj === allSealed).toString());

    alert("Date.now = " + Date.now());
}

3

Tôi biết tôi có thể đến muộn một chút nhưng

  • Độ tương tự: cả hai đều được sử dụng để tạo các đối tượng không thể mở rộng .
  • Sự khác biệt: Trong Freeze có thể cấu hình, các thuộc tính có thể đếm được và ghi được của đối tượng được đặt thành false. trong đó như trong thuộc tính có thể ghi được niêm phong được đặt thành truevà phần còn lại của các thuộc tính là sai.

6
Đây không phải là hoàn toàn chính xác. Object.getOwnPropertyDescriptor(Object.freeze({ prop: 1 }), 'prop').enumerable=== true.
Leon Adler

2

Bây giờ bạn có thể buộc một thuộc tính đối tượng bị đóng băng thay vì đóng băng toàn bộ đối tượng. Bạn có thể đạt được điều này Object.definePropertyvới writable: falsenhư là một tham số.

var obj = {
    "first": 1,
    "second": 2,
    "third": 3
};
Object.defineProperty(obj, "first", {
    writable: false,
    value: 99
});

Trong ví dụ này, obj.firstbây giờ giá trị của nó bị khóa đến 99.


0

Tôi đã tạo một bảng đơn giản để so sánh các hàm dưới đây và giải thích sự khác biệt giữa các hàm này.

  • Object.freeze ()
  • Object.seal ()
  • Object.preventExtensions ()

bảng giải thích sự khác biệt giữa ba phương pháp trên

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.