usuń ax vs ax = undefined

138

Czy jest jakaś istotna różnica w wykonaniu któregokolwiek z tych działań?

delete a.x;

vs

a.x = undefined;

gdzie

a = {
    x: 'boo'
};

czy można powiedzieć, że są równoważne?

(Nie biorę pod uwagę rzeczy typu „V8 lubi nie używać deletelepiej” )

bevacqua
źródło
2
Operator delete całkowicie usuwa właściwość. Ustawienie właściwości na undefined usuwa wartość. Ustawienie właściwości na null zmienia wartość na wartość null. Oto test perfekcyjny,
j08691
1
@ j08691 Nit: Nie usuwa wartości. Przypisuje undefinedjako wartość, która nadal jest ..
Powinieneś porozmawiać o tym, dlaczego ci na tym zależy, wtedy odpowiedź może zaspokoić twój rzeczywisty problem.
Juan Mendes,

Odpowiedzi:

181

Nie są równoważne. Główną różnicą jest to ustawienie

a.x = undefined

oznacza, że a.hasOwnProperty("x")nadal będzie zwracać wartość true, a zatem nadal będzie pojawiać się w for inpętli i w formacieObject.keys()

delete a.x

oznacza, że a.hasOwnProperty("x")zwróci fałsz

Sposób, w jaki są takie same, polega na tym, że nie można stwierdzić, czy właściwość istnieje, testując

if (a.x === undefined)

Czego nie powinieneś robić, jeśli próbujesz ustalić, czy właściwość istnieje, zawsze powinieneś jej używać

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

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

Podążanie za łańcuchem prototypów (wspomnianym przez zzzzBov ) Wywołanie deletepozwoli mu przejść w górę łańcucha prototypów, podczas gdy ustawienie wartości na undefined nie spowoduje wyszukania właściwości w połączonych prototypach 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

Usuwanie odziedziczonych właściwości Jeśli właściwość, którą próbujesz usunąć, jest dziedziczona, deletenie ma na nią wpływu. Oznacza to, deleteże usuwa tylko właściwości z samego obiektu, a nie właściwości dziedziczone.

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

Dlatego jeśli chcesz się upewnić, że wartość obiektu będzie niezdefiniowana, deletenie zadziała, gdy właściwość zostanie odziedziczona, będziesz musiał undefinedw takim przypadku ustawić (nadpisać) ją na . Chyba że miejsce, które go sprawdza, będzie używać hasOwnProperty, ale prawdopodobnie nie byłoby bezpiecznie założyć, że wszędzie, gdzie sprawdza, będzie używaćhasOwnProperty

Juan Mendes
źródło
1
"x" in apowróci także truez pierwszym i falsedrugim. Wynik Object.keysbędzie również inny.
lonesomeday
dlaczego twierdzisz, że nie powinienem sprawdzać undefined? wydaje mi się wystarczająco rozsądne.
bevacqua
@Nico Ponieważ to nie powie ci, czy nieruchomość istnieje. Nie mówię, nigdy go nie używaj. Ale jeśli szukasz undefined, możesz równie dobrze po prostu sprawdzić if (a.x), chyba że jest to dla liczb i 0 jest poprawne
Juan Mendes
33

Parafrazując pytanie:

delete a.xi a.x = undefinedrównoważne?

Nie.

Pierwsza usuwa klucz ze zmiennej, później ustawia klucz na wartość undefined. Ma to znaczenie podczas iteracji po właściwościach obiektów i kiedy hasOwnPropertyjest używany.

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

Ponadto spowoduje to znaczącą różnicę, gdy w grę wchodzi łańcuch prototypów.

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'
zzzzBov
źródło
2
+1 Świetna uwaga na temat deletedopuszczenia go w górę łańcucha prototypów
Juan Mendes
4

Jeśli a.xjest funkcją ustawiającą, a.x = undefinedwywoła funkcję delete a.x, ale nie wywoła funkcji.

martin770
źródło
3

Tak, jest różnica. Jeśli użyjesz delete a.xx, nie jest już właściwością a, ale jeśli używasz a.x=undefinedgo, jest to właściwość, ale jej wartość jest niezdefiniowana.


źródło
2

Nazwy są trochę zagmatwane. a.x = undefinedpo prostu ustawia właściwość na undefined, ale właściwość nadal istnieje:

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

delete faktycznie usuwa to:

> var a = {x: 3};
> delete a.x;
> a.constructor.keys(a)
[]
Mikser
źródło
1

Ta REPL z węzła powinna zilustrować różnicę.

> 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
kojiro
źródło
1

Jestem pewien, że widzisz różnicę między var o1 = {p:undefined};i var o2 = {};.

W obu przypadkach o.pbędzie, undefinedale w pierwszym przypadku to dlatego, że jest to wartość, aw drugim, ponieważ nie ma wartości .

deletejest operatorem, który pozwala dostać się z o1(lub inny obiekt, który ma przypisaną wartość jego pmajątku) na o2tej drodze: delete o1.p;.

Operacja odwrotna polega po prostu na przypisaniu wartości ( undefinedw tym przykładzie, ale może to być coś innego) do właściwości o1.p = undefined;.

Więc nie , nie są one równoważne.


delete o.p; będzie

  • usunąć właściwość pz obiektu, jeśli taki posiada

  • nic innego nie rób

o.p = undefined; będzie

  • dodaj właściwość pdo obiektu, jeśli jeszcze jej nie ma, i ustaw jej wartość naundefined

  • po prostu zmień wartość właściwości, jeśli obiekt już ją posiada


Z punktu widzenia wydajności deletejest zły, ponieważ modyfikuje strukturę obiektu (podobnie jak dodanie nowej właściwości, jeśli nie zainicjowałeś jej w konstruktorze).

Natomiast ustawienie wartości na undefinedzwalnia również zawartość, ale bez wymuszania modyfikacji struktury.

xavierm02
źródło
1

Obiekt jest po prostu reprezentacją drzewa, co oznacza, że ​​w pamięci korzeń wskazuje różne miejsca w pamięci, w których przechowywane są klucze tego obiektu. a ta lokalizacja wskazuje na inną lokalizację, w której przechowywana jest rzeczywista wartość tego klucza, lub lokalizacje, w których przechowywane są klucze potomne, lub lokalizacje, w których przechowywane są wartości tablicowe.

Kiedy usuwasz dowolny klucz z obiektu za pomocą funkcji delete, w rzeczywistości usuwa on łącze między tym kluczem a jego obiektem nadrzędnym, a lokalizacje klucza i jego wartość są zwalniane do przechowywania innych informacji.

Kiedy próbujesz usunąć dowolny klucz, ustawiając wartość undefined jako jego wartość, po prostu ustawiasz jego wartość, a nie usuwasz tego klucza. Oznacza to, że lokalizacja pamięci kluczy jest nadal połączona z obiektem nadrzędnym i wartością, jeśli klucz jest niezdefiniowany.

Używanie undefined zamiast używania słowa kluczowego delete jest złą praktyką, ponieważ nie zwalnia lokalizacji pamięci tego klucza.

Nawet jeśli klucz nie istnieje i ustawisz go jako niezdefiniowany, ten klucz zostanie utworzony z wartością undefined.

na przykład

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

nie można pracować z właściwościami dziedziczonymi, ponieważ ta właściwość nie jest częścią tego obiektu podrzędnego.

Laxmikant Dange
źródło
1
Zauważ, że nowsze silniki wolą nie usuwać kluczy, ponieważ kiedy to robisz, silnik musi utworzyć dla niego nową klasę i zaktualizować ją wszędzie tam, gdzie występuje odniesienie do „klasy”.
Juan Mendes
@JuanMendes, czy możesz podać jakieś referencje.
Laxmikant Dange
3
Zobacz: Czy użycie słowa kluczowego delete wpływa na optymalizacje obiektu w wersji 8? TL; DR as a general rule of thumb, using 'delete' makes thing slower.i 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. i wreszcie smashingmagazine.com/2012/11/ ...
Juan Mendes
1

Używając tablicy zamiast obiektu, mogę pokazać, że funkcja delete zużywa mniej pamięci sterty niż wartość undefined.

Na przykład ten kod nie zakończy się:

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

Generuje ten błąd:

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

Tak więc, jak widać, undefinedfaktycznie zajmuje pamięć sterty.

Jednakże, jeśli również wybierzesz deleteelement ary (zamiast po prostu ustawić go na undefined), kod powoli się zakończy:

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}.`);

To są skrajne przykłady, ale wskazują na deleteto, że nie widziałem nikogo o czym wspominał.

Lonnie Best
źródło