ลบขวาน vs ขวาน = ไม่ได้กำหนด


142

มีความแตกต่างอย่างมากในการทำอย่างใดอย่างหนึ่งหรือไม่?

delete a.x;

เทียบกับ

a.x = undefined;

ที่ไหน

a = {
    x: 'boo'
};

อาจกล่าวได้ว่าเทียบเท่า?

(ฉันไม่ได้คำนึงถึงสิ่งต่างๆเช่น"V8 ชอบไม่ใช้deleteดีกว่า" )


2
ตัวดำเนินการลบจะลบคุณสมบัติทั้งหมด การตั้งค่าคุณสมบัติที่ไม่ได้กำหนดจะลบค่า การตั้งค่าคุณสมบัติเป็น null จะเปลี่ยนค่าเป็นค่า null นี่คือการทดสอบที่สมบูรณ์แบบหากคุณต้องการ: jsperf.com/delete-vs-undefined-vs-null/3
j08691

1
@ j08691 Nit: มันไม่ได้ลบค่า มันกำหนดundefinedเป็นค่าซึ่งยังคงเป็น ..

คุณควรพูดถึงสาเหตุที่คุณสนใจเรื่องนี้จากนั้นคำตอบสามารถตอบสนองปัญหาที่แท้จริงของคุณได้
Juan Mendes

คำตอบ:


185

พวกเขาไม่เทียบเท่า ความแตกต่างที่สำคัญคือการตั้งค่านั้น

a.x = undefined

หมายความว่าa.hasOwnProperty("x")จะยังคงกลับมาเป็นจริงดังนั้นมันจะยังคงแสดงในแบบfor inวนซ้ำและในObject.keys()

delete a.x

หมายความว่าa.hasOwnProperty("x")จะส่งคืนเท็จ

วิธีที่เหมือนกันคือคุณไม่สามารถบอกได้ว่ามีคุณสมบัติอยู่หรือไม่โดยการทดสอบ

if (a.x === undefined)

สิ่งที่คุณไม่ควรทำหากพยายามตรวจสอบว่ามีทรัพย์สินอยู่หรือไม่คุณควรใช้เสมอ

// If you want inherited properties
if ('x' in a)

// If you don't want inherited properties
if (a.hasOwnProperty('x'))

การติดตามโซ่ต้นแบบ (กล่าวถึงโดยzzzzBov ) การโทรdeleteจะทำให้มันขึ้นไปบนห่วงโซ่ต้นแบบในขณะที่การตั้งค่าเป็นไม่ได้กำหนดจะไม่มองหาคุณสมบัติในต้นแบบที่ถูกล่ามโซ่http://jsfiddle.net/NEEw4/1/

var obj = {x: "fromPrototype"};
var extended = Object.create(obj);
extended.x = "overriding";
console.log(extended.x); // overriding
extended.x  = undefined;
console.log(extended.x); // undefined
delete extended.x;
console.log(extended.x); // fromPrototype

การลบคุณสมบัติที่สืบทอดมาหากคุณสมบัติที่คุณพยายามจะลบได้รับการสืบทอดdeleteจะไม่มีผลกับคุณสมบัตินั้น นั่นคือ,deleteลบคุณสมบัติจากอ็อบเจ็กต์เท่านั้นไม่ใช่คุณสมบัติที่สืบทอดมา

var obj = {x: "fromPrototype"};
var extended = Object.create(obj);
delete extended.x;
console.log(extended.x); // Still fromPrototype

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


1
"x" in aก็จะกลับมาtrueพร้อมกับอดีตและอย่างfalseหลัง ผลลัพธ์ของObject.keysจะแตกต่างกันด้วย
lonesomeday

ทำไมคุณถึงบอกว่าฉันไม่ควรตรวจสอบ undefined? ดูเหมือนมีเหตุผลเพียงพอสำหรับฉัน
bevacqua

@Nico เพราะนั่นจะไม่บอกคุณว่ามีทรัพย์สินอยู่หรือไม่ ฉันไม่ได้บอกว่าอย่าใช้มัน แต่ถ้าคุณกำลังตรวจสอบคุณก็สามารถตรวจสอบได้undefinedเช่นกันif (a.x)เว้นแต่จะเป็นตัวเลขและ 0 ก็ใช้ได้
Juan Mendes

33

ในการถอดความคำถาม:

มีdelete a.xและa.x = undefinedเทียบเท่าหรือไม่?

ไม่

undefinedอดีตลบที่สำคัญจากตัวแปรชุดต่อมาที่สำคัญที่มีค่าของ สิ่งนี้สร้างความแตกต่างเมื่อทำซ้ำคุณสมบัติของอ็อบเจ็กต์และเมื่อใดที่hasOwnPropertyถูกใช้

a = {
    x: true
};
a.x = undefined;
a.hasOwnProperty('x'); //true
delete a.x;
a.hasOwnProperty('x'); //false

นอกจากนี้สิ่งนี้จะสร้างความแตกต่างอย่างมากเมื่อมีส่วนเกี่ยวข้องกับห่วงโซ่ต้นแบบ

function Foo() {
    this.x = 'instance';
}
Foo.prototype = {
    x: 'prototype'
};
a = new Foo();
console.log(a.x); //'instance'

a.x = undefined;
console.log(a.x); //undefined

delete a.x;
console.log(a.x); //'prototype'

2
+1 จุดที่ยอดเยี่ยมเกี่ยวกับการdeleteอนุญาตให้ขึ้นโซ่ต้นแบบ
Juan Mendes


3

ใช่มีความแตกต่าง ถ้าคุณใช้delete a.xx ไม่ใช่พร็อพเพอร์ตี้ของ a อีกต่อไป แต่ถ้าคุณใช้a.x=undefinedมันเป็นคุณสมบัติ แต่ไม่ได้กำหนดค่าของมัน


2

ชื่อมีความสับสนเล็กน้อย a.x = undefinedเพียงแค่ตั้งค่าคุณสมบัติเป็นundefinedแต่คุณสมบัติยังคงอยู่ที่นั่น:

> var a = {x: 3};
> a.x = undefined;
> a.constructor.keys(a)
["x"]

delete ลบออกจริง:

> var a = {x: 3};
> delete a.x;
> a.constructor.keys(a)
[]

1

REPL จากโหนดนี้ควรแสดงให้เห็นถึงความแตกต่าง

> a={ x: 'foo' };
{ x: 'foo' }
> for (var i in a) { console.log(i); };
x
undefined
> a.x=undefined;
undefined
> for (var i in a) { console.log(i); };
x
undefined
> delete a.x;
true
> for (var i in a) { console.log(i); };
undefined

1

ฉันแน่ใจว่าคุณสามารถเห็นความแตกต่างระหว่างและvar o1 = {p:undefined};var o2 = {};

ในทั้งสองกรณีo.pจะเป็นundefinedแต่ในกรณีแรกนั่นเป็นเพราะนั่นคือค่าและในกรณีที่สองเนื่องจากไม่มีค่าไม่มีค่า

deleteเป็นผู้ประกอบการที่ช่วยให้คุณได้รับจากo1(หรือวัตถุอื่นที่มีค่าของตนได้รับมอบหมายให้pสถานที่ให้บริการ) เพื่อให้วิธีการที่:o2delete o1.p;

การดำเนินการย้อนกลับจะทำโดยเพียงแค่การกำหนดค่า ( undefinedในตัวอย่างนี้ แต่มันอาจจะเป็นอย่างอื่น) o1.p = undefined;ไปยังสถานที่

ดังนั้นไม่พวกเขาไม่เทียบเท่า


delete o.p; จะ

  • ลบคุณสมบัติpออกจากวัตถุถ้ามี

  • ไม่ทำอะไรเลย

o.p = undefined; จะ

  • เพิ่มคุณสมบัติpให้กับวัตถุหากยังไม่มีและตั้งค่าเป็นundefined

  • เพียงแค่เปลี่ยนค่าของคุณสมบัติหากวัตถุมีอยู่แล้ว


จากมุมมองด้านประสิทธิภาพdeleteคือไม่ดีเพราะมันปรับเปลี่ยนโครงสร้างของวัตถุ (เช่นเดียวกับการเพิ่มคุณสมบัติใหม่ถ้าคุณยังไม่ได้เริ่มต้นในการสร้าง)

ในขณะที่การตั้งค่าเพื่อundefinedเผยแพร่เนื้อหาเช่นกัน แต่ไม่บังคับให้แก้ไขโครงสร้าง


1

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

เมื่อคุณลบคีย์ใด ๆ ออกจากอ็อบเจ็กต์โดยใช้ลบจริงๆแล้วมันจะลบลิงก์ระหว่างคีย์นั้นกับอ็อบเจ็กต์หลักและตำแหน่งหน่วยความจำของคีย์และค่าของคีย์จะถูกปล่อยให้เก็บข้อมูลอื่น

เมื่อคุณพยายามลบคีย์ใด ๆ โดยการตั้งค่าไม่ได้กำหนดเป็นค่าของคีย์นั้นคุณก็แค่กำหนดค่าของคีย์นั้นไม่ใช่การลบคีย์นั้น นั่นหมายความว่าตำแหน่งหน่วยความจำของคีย์ยังคงเชื่อมโยงกับอ็อบเจ็กต์หลักและค่าหากไม่ได้กำหนดคีย์

การใช้ undefined แทนการใช้คำสำคัญลบเป็นแนวทางปฏิบัติที่ไม่ดีเนื่องจากจะไม่ปล่อยตำแหน่งหน่วยความจำของคีย์นั้น

undefinedแม้ว่าที่สำคัญคือไม่ได้อยู่และคุณตั้งค่าเป็นไม่ได้กำหนดแล้วที่สำคัญที่จะได้รับการสร้างขึ้นด้วยความคุ้มค่า

เช่น

var a = {};
a.d = undefined;
console.log(a); // this will print { d: undefined }

ลบไม่สามารถใช้กับคุณสมบัติที่สืบทอดมาได้เนื่องจากคุณสมบัตินั้นไม่ใช่ส่วนของวัตถุลูกนั้น


1
โปรดทราบว่าเอ็นจิ้นรุ่นใหม่ไม่ต้องการให้คุณลบคีย์เนื่องจากเมื่อคุณทำเอ็นจินจำเป็นต้องสร้างคลาสใหม่สำหรับมันและอัปเดตเมื่อใดก็ตามที่มีการอ้างถึง "คลาส"
Juan Mendes

@JuanMendes คุณช่วยอ้างอิงได้ไหม
Laxmikant Dange

3
ดูการใช้คำสำคัญลบมีผลต่อการปรับแต่ง v8 ของออบเจ็กต์หรือไม่ TL; DR as a general rule of thumb, using 'delete' makes thing slower.และdevelopers.google.com/v8/design To reduce the time required to access JavaScript properties, V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes. In V8, an object changes its hidden class when a new property is added.และในที่สุดก็smashingmagazine.com/2012/11/…
Juan Mendes

1

การใช้อาร์เรย์แทนวัตถุฉันสามารถแสดงให้เห็นว่าการลบใช้หน่วยความจำฮีปน้อยกว่าที่ไม่ได้กำหนดไว้

ตัวอย่างเช่นรหัสนี้จะไม่เสร็จสิ้น:

let y = 1;
let ary = [];
console.log("Fatal Error Coming Soon");
while (y < 4294967295)
{
    ary.push(y);
    ary[y] = undefined;
    y += 1;
}
console(ary.length);

มันทำให้เกิดข้อผิดพลาดนี้:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory.

ดังที่คุณเห็นundefinedจริง ๆ แล้วใช้หน่วยความจำฮีป

อย่างไรก็ตามหากคุณdeleteเป็น ary-item ด้วย (แทนที่จะแค่ตั้งค่าเป็นundefined) โค้ดจะเสร็จช้า:

let x = 1;
let ary = [];
console.log("This will take a while, but it will eventually finish successfully.");
while (x < 4294967295)
{
    ary.push(x);
    ary[x] = undefined;
    delete ary[x];
    x += 1;
}
console.log(`Success, array-length: ${ary.length}.`);

นี่เป็นตัวอย่างที่รุนแรง แต่พวกเขาชี้deleteให้เห็นว่าฉันไม่เคยเห็นใครพูดถึงที่ไหนเลย

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