ความแตกต่างระหว่างการแช่แข็งและการปิดผนึก


164

ฉันเพิ่งได้ยินเกี่ยวกับวิธีการของ JavaScript freezeและsealสามารถใช้เพื่อทำให้ Object ใด ๆ ไม่เปลี่ยนรูปได้

นี่คือตัวอย่างสั้น ๆ วิธีใช้:

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

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

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

ความแตกต่างระหว่างfreezeและsealคืออะไร? พวกเขาสามารถเพิ่มประสิทธิภาพได้หรือไม่?


6
แค่ใครก็ตามที่ดูคำถามนี้คำตอบที่ยอมรับนั้นไม่ถูกต้องตามข้อเท็จจริง คำตอบของ @ tungd ถูกต้อง
Bjorn

2
ทราบอีกนอกจากนี้ยังมีObject.preventExtensionsนอกเหนือไปและObject.seal เพียงป้องกันไม่ให้มีการเพิ่มรายการใหม่ลงในวัตถุ คุณสามารถลบกำหนดค่าและเปลี่ยนค่าของคุณสมบัติบนวัตถุที่ปิดใช้งานการขยายได้ Object.freezeObject.preventExtensionsObject.preventExtensions
Bjorn

1
อัปเดตล่าสุด: v8.dev/blog/v8-release-76#frozen%2Fsealed-array-improvements
Mike

คำตอบ:


193

Object.seal

  • ช่วยป้องกันการเพิ่มและ / หรือลบคุณสมบัติออกจากวัตถุที่ซีล การใช้deleteจะคืนค่าเท็จ
  • มันทำให้คุณสมบัติที่มีอยู่ทั้งหมดไม่สามารถกำหนดค่าได้ : พวกเขาไม่สามารถแปลงจาก 'data descriptors' เป็น 'accessor descriptors' (และในทางกลับกัน) และไม่มีการแก้ไขคุณสมบัติของ accessor descriptors เลย (ในขณะที่ data descriptors สามารถเปลี่ยนwritableคุณสมบัติของมันได้valueคุณลักษณะของพวกเขาหากwriteableเป็นจริง)
  • สามารถขว้าง a TypeErrorเมื่อพยายามปรับเปลี่ยนค่าของวัตถุที่ซีล (โดยทั่วไปในโหมดเข้มงวด )

Object.freeze

  • สิ่งที่Object.sealทำบวก:
  • จะช่วยป้องกันการแก้ไขคุณสมบัติที่มีอยู่ใด ๆ

ไม่มีใครส่งผลกระทบต่อวัตถุ 'ลึก' / ลูกหลาน เช่นถ้าobjถูกแช่แข็งobj.elไม่สามารถกำหนดใหม่ได้ แต่มูลค่าของobj.elสามารถแก้ไขได้เช่นobj.el.idสามารถเปลี่ยนแปลงได้


ประสิทธิภาพ:

การปิดผนึกหรือการแช่แข็งวัตถุอาจส่งผลต่อความเร็วในการแจงนับขึ้นอยู่กับเบราว์เซอร์:

  • Firefox: ประสิทธิภาพการนับไม่ได้รับผลกระทบ
  • IE: ผลกระทบต่อประสิทธิภาพการนับมีน้อยมาก
  • Chrome: ประสิทธิภาพการนับเร็วขึ้นเมื่อปิดผนึกหรือแช่แข็ง
  • Safari: วัตถุที่ถูกผนึกหรือแช่แข็งระบุว่าช้าลง 92% (จนถึงปี 2014)

แบบทดสอบ: วัตถุปิดผนึก , วัตถุแช่แข็ง


2
คุณช่วยพูดได้ไหมว่าทำไมเราถึงใช้วิธีการเหล่านี้ เพียงเพราะเราทำได้
Alan Dong

3
ในอนาคตฉันคิดว่าพวกเขาจะต้องใช้งานจำนวนมาก (ถ้าปรับให้เหมาะสมอย่างถูกต้อง) เมื่อพัฒนาไลบรารี / กรอบงาน พวกเขาช่วยให้คุณสามารถป้องกันผู้ใช้ (โดยไม่ตั้งใจ) ทำลายรหัสของคุณ (และตามที่ระบุไว้ในคำตอบการเพิ่มประสิทธิภาพควรนำไปสู่การปรับปรุงความเร็วที่ดี) แต่นี้เป็นเก็งกำไรบริสุทธิ์ :)
Niccolò Campolungo

2
คำตอบนี้มีข้อผิดพลาดจริงมากมาย สำหรับหนึ่งsealยังทำให้คุณสมบัติที่มีอยู่ไม่สามารถกำหนดค่าได้ดูjsfiddle.net/btipling/6m743whnหมายเลข 2 คุณยังสามารถแก้ไขได้นั่นคือเปลี่ยนค่าของคุณสมบัติที่มีอยู่บนวัตถุที่ปิดผนึก
Bjorn

8
FWIW วัตถุที่ถูกแช่แข็งและปิดผนึกนั้นเร็วกว่าวัตถุที่ยังไม่ถูกแยกและปิดผนึกใน Chrome Canary v43.0.2317.0
llambda

2
@AlanDong กำลังจะมาสาย แต่นี่คือเหตุผลที่คุณต้องการล็อควัตถุหนึ่งในคุณสมบัติของ JavaScript คือคุณสามารถเพิ่มคุณสมบัติได้ทุกเมื่อที่คุณต้องการ; คุณสามารถทำสิ่งนี้โดยไม่ตั้งใจได้ด้วยการพิมพ์ผิด นักเรียนหลายคนของฉันได้พยายามที่จะเพิ่มตัวจัดการเหตุการณ์ที่เรียกว่าonClickหรือonlickและสงสัยว่าทำไมมันไม่ทำงาน หาก JavaScript เกิดข้อผิดพลาดแสดงว่าเป็นสิ่งที่ผิดพลาดน้อยกว่า ประการที่สองนี้ช่วยให้คุณสามารถใช้คุณสมบัติคงที่ในวัตถุที่ป้องกันการเปลี่ยนแปลง สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับเม็ทเจ็ตของวัตถุ
Manngo

119

ฉันเขียนโครงการทดสอบซึ่งเปรียบเทียบทั้ง 3 วิธี:

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

การทดสอบหน่วยของฉันครอบคลุมกรณี CRUD:

  • [C] เพิ่มคุณสมบัติใหม่
  • [R] คุณสมบัติการอ่านมีอยู่
  • [U] แก้ไขคุณสมบัติที่มีอยู่
  • [D] ลบคุณสมบัติที่มีอยู่

ผลลัพธ์:

ป้อนคำอธิบายรูปภาพที่นี่


2
อันนี้ยอดเยี่ยม UPDATE คำนึงถึงการปรับเปลี่ยนแอตทริบิวต์ (ผ่าน defineProperty) หรือไม่เช่นสามารถกำหนดค่าได้นับจำนวนเขียนได้หรือไม่
Drenai

ฉันมักจะคิดว่าวัตถุ DOM นั้นควรได้รับการผนึกไว้ (หลังจาก polyfills แน่นอน) ที่จะช่วยป้องกันข้อผิดพลาดการพิมพ์ผิดจำนวนมาก
Manngo

@Manngo คุณสามารถประทับตราวัตถุ DOM ของคุณ เพียงแค่สร้างตัวแปรและตั้งค่าให้DEBUGMODE จากนั้นทำtrue if (DEBUGMODE) { ... }ในการ...ใส่ฟังก์ชันการทำงานของคุณเพื่อให้แน่ใจว่าวัตถุ DOM ทั้งหมดถูกปิดผนึกอยู่เสมอ จากนั้นเมื่อคุณพร้อมที่จะแจกจ่ายสคริปต์หน้าเว็บของคุณเปลี่ยนDEBUGMODEเป็นfalseเรียกใช้สคริปต์ของคุณผ่านคอมไพเลอร์ปิดและกระจาย มันง่ายอย่างที่คิด
Jack Giffin

@ JackGiffin ขอบคุณสำหรับความคิดเห็น ฉันแค่บอกว่าฉันคิดเสมอว่ามันจะเป็นความคิดที่ดี ฉันมีนักเรียนจำนวนมากที่ลงเอยพิมพ์บางอย่างเช่นelement.onlick=somethingและรู้สึกหงุดหงิดเพราะมันไม่ทำงาน แต่มันก็ไม่ใช่ข้อผิดพลาดทางเทคนิค
Manngo

2
@ เหงาแล้วมันจะไม่สะกดคำว่า CRUD คุณต้องจัดการเรื่อง
RUDE

84

คุณสามารถค้นหาสิ่งเหล่านี้ได้ใน MDN ในระยะสั้น:

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

1
Object.seal()ดูเหมือนว่าจะแช่แข็งคุณสมบัติต้นแบบ: \
K ..

10

Object.freeze()สร้างวัตถุแช่แข็งซึ่งหมายความว่าจะใช้วัตถุที่มีอยู่และเป็นหลักเรียกObject.seal()มัน แต่มันยังทำเครื่องหมายคุณสมบัติ "data accessor" ทั้งหมดเป็นwritable:falseเพื่อให้ค่าของพวกเขาไม่สามารถเปลี่ยนแปลงได้ - Kyle Simpson คุณไม่รู้ JS - ต้นแบบและออบเจ็กต์นี้


4

ฉันกำลังดูความแตกต่างระหว่าง Freeze และ Seal ใน ECMAScript 5 และสร้างสคริปต์เพื่อชี้แจงความแตกต่าง Frozen สร้างวัตถุที่ไม่เปลี่ยนรูปรวมถึงข้อมูลและโครงสร้าง ซีลป้องกันการเปลี่ยนแปลงอินเทอร์เฟซที่มีชื่อ - ไม่มีการเพิ่มลบ - แต่คุณสามารถกลายพันธุ์วัตถุและกำหนดความหมายของอินเทอร์เฟซใหม่

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

ฉันรู้ว่าฉันอาจจะสายไปหน่อย แต่

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

6
สิ่งนี้ไม่ถูกต้องทั้งหมด ===Object.getOwnPropertyDescriptor(Object.freeze({ prop: 1 }), 'prop').enumerable true
Leon Adler

2

ตอนนี้คุณสามารถบังคับให้วัตถุหนึ่งวัตถุถูกแช่แข็งได้แทนที่จะแช่แข็งวัตถุทั้งหมด คุณสามารถทำได้โดยObject.definePropertyใช้writable: falseเป็นพารามิเตอร์

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

ในตัวอย่างobj.firstนี้ขณะนี้มีค่าถูกล็อคเป็น 99


0

ฉันได้สร้างตารางอย่างง่ายเพื่อเปรียบเทียบฟังก์ชั่นด้านล่างและอธิบายความแตกต่างระหว่างฟังก์ชั่นเหล่านี้

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

ตารางที่อธิบายความแตกต่างระหว่างสามวิธีข้างต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.