Come disinserire una variabile JavaScript?


592

Ho una variabile globale in JavaScript (in realtà una windowproprietà, ma non credo che sia importante) che era già stata popolata da uno script precedente ma non voglio un altro script che verrà eseguito in seguito per vedere il suo valore o che era anche definito.

Ho messo some_var = undefinede funziona allo scopo di test, typeof some_var == "undefined"ma non credo davvero che sia il modo giusto di procedere.

Cosa ne pensi?

Risposte:


454

L' deleteoperatore rimuove una proprietà da un oggetto. Non è possibile rimuovere una variabile. Quindi la risposta alla domanda dipende da come viene definita la variabile globale o la proprietà.

(1) Se viene creato con var, non può essere eliminato.

Per esempio:

var g_a = 1; //create with var, g_a is a variable 
delete g_a; //return false
console.log(g_a); //g_a is still 1

(2) Se viene creato senza var, può essere eliminato.

g_b = 1; //create without var, g_b is a property 
delete g_b; //return true
console.log(g_b); //error, g_b is not defined

Spiegazione tecnica

1. Utilizzo var

In questo caso il riferimento g_aviene creato in ciò che la specifica ECMAScript chiama " VariableEnvironment " che è collegata all'ambito corrente - questo potrebbe essere il contesto di esecuzione di una funzione nel caso di utilizzo varall'interno di una funzione (anche se potrebbe essere un po 'più complicato se si considera let) o nel caso del codice "globale", VariableEnvironment è associato all'oggetto globale (spesso window).

I riferimenti in VariableEnvironment non sono normalmente cancellabili: il processo dettagliato in ECMAScript 10.5 lo spiega in dettaglio, ma è sufficiente dire che a meno che il codice non venga eseguito in un evalcontesto (che la maggior parte delle console di sviluppo basate su browser utilizza), le variabili dichiarate con varnon possono essere cancellato.

2. Senza usare var

Quando si tenta di assegnare un valore a un nome senza utilizzare la varparola chiave, Javascript cerca di individuare il riferimento al nome in quello che la specifica ECMAScript chiama " LexicalEnvironment " e la differenza principale è che i nidificanti di LexicalEvironment sono nidificati - ovvero un LexicalEnvironment ha un parent ( ciò che la specifica ECMAScript chiama "riferimento all'ambiente esterno") e quando Javscript non riesce a individuare il riferimento in un ambiente Lexical , appare nell'ambiente Lexical padre (come dettagliato in 10.3.1 e 10.2.2.1 ). Il livello superiore LexicalEnvironment è " l'ambiente globale", e questo è legato all'oggetto globale in quanto i suoi riferimenti sono proprietà dell'oggetto globale. Quindi, se si tenta di accedere a un nome che non è stato dichiarato utilizzando una varparola chiave nell'ambito corrente o eventuali ambiti esterni, Javascript alla fine recupererà una proprietà del windowoggetto per servire come quella di riferimento. come abbiamo appreso in precedenza, le proprietà sugli oggetti possono essere cancellati.

Appunti

  1. È importante ricordare che le vardichiarazioni vengono "sollevate", ovvero sono sempre considerate avvenute all'inizio dell'ambito in cui si trovano - sebbene non l'inizializzazione del valore che può essere fatta in varun'istruzione - che viene lasciata dove si trova . Quindi, nel codice seguente, aè un riferimento da VariableEnvironment e non dalla windowproprietà e il suo valore sarà 10alla fine del codice:

    function test() { a = 5; var a = 10; }

  2. La discussione sopra è quando la "modalità rigorosa" non è abilitata. Le regole di ricerca sono leggermente diverse quando si utilizza la "modalità rigorosa" e i riferimenti lessicali che si sarebbero risolti con le proprietà della finestra senza "modalità rigorosa" genereranno errori "variabili non dichiarate" in "modalità rigorosa". Non ho davvero capito dove sia specificato, ma è come si comportano i browser.


8
Quello che hai detto è un malinteso comune ma in realtà è errato - in Javascript non ci sono "variabili globali". Le variabili definite senza un ambito esplicito (come l'utilizzo varal di fuori di una funzione) sono proprietà dell '"oggetto globale", come nei browser Web window. Quindi - var a = 1; delete window.a; console.log(a);eliminerà correttamente la variabile e causerà un errore di riferimento nell'ultima riga.
Guss,

7
@Guss, il tuo codice var a = 1; delete window.a; console.log(a);mostra 1.
Dayong

5
Sto usando Google Chrome v36. Ho testato su altri browser. Sembra che non sia coerente tra i browser. Chrome e Opera hanno visualizzato 1, mentre Firefox, Safari e IE 11 sul mio computer hanno dato un errore.
Dayong

3
Ok, errore mio. Vedi ecma-international.org/ecma-262/5.1/#sec-10.5 (sotto-punti 2 e 8.c.ii): quando eseguo il mio test nella console degli sviluppatori, è generalmente considerato "contesto di valutazione" (anche se forse non in Chrome), quindi genererà un errore. Lo stesso codice nel contesto globale di un documento reale verrà generato 1correttamente in tutti i browser. In esecuzione in documenti reali, i tuoi esempi di codice sono corretti. Ho selezionato la tua risposta come corretta, ma ti sarei grato se fosse possibile modificarla per includere spiegazioni window.a = 1; delete window.a;e possibilmente il meccanismo. Posso farlo anche se non ti dispiace.
Guss,

2
@KlaiderKlai sì. Le variabili con ambito funzione vengono create e distrutte ogni volta che viene eseguita la funzione. Probabilmente la chiusura è un'eccezione.
Dayong,

278

La risposta di @ scunlife funzionerà, ma tecnicamente dovrebbe esserlo

delete window.some_var; 

si suppone che delete non sia operativo quando la destinazione non è una proprietà dell'oggetto. per esempio,

(function() {
   var foo = 123;
   delete foo; // wont do anything, foo is still 123
   var bar = { foo: 123 };
   delete bar.foo; // foo is gone
}());

Ma dal momento che le variabili globali sono in realtà membri dell'oggetto window, funziona.

Quando sono coinvolte le catene di prototipi, l'uso di delete diventa più complesso perché rimuove solo la proprietà dall'oggetto target e non il prototipo. per esempio,

function Foo() {}
Foo.prototype = { bar: 123 };
var foo = new Foo();
// foo.bar is 123
foo.bar = 456;
// foo.bar is now 456
delete foo.bar;
// foo.bar is 123 again.

Quindi sii attento.

EDIT: La mia risposta è in qualche modo inaccurata (vedi "Idee sbagliate" alla fine). Il link spiega tutti i dettagli gory, ma il riassunto è che possono esserci grandi differenze tra i browser e in base all'oggetto da cui stai eliminando. delete object.somePropdovrebbe generalmente essere sicuro fino a quando object !== window. Non lo userei ancora per eliminare le variabili dichiarate con, varsebbene sia possibile nelle giuste circostanze.


14
grazie @jedierikb per il link a questo interessante articolo. più specificamente a questa parte < perfectionkills.com/understanding-delete/#misconceptions > di quell'articolo in cui l'autore afferma che l'affermazione di Noè "si suppone che sia un no-op" è piuttosto imprecisa insieme a un'eccellente spiegazione del perché è imprecisa . (Non sparare al messaggero!)
Rob Wells,

2
Per quanto riguarda l'ultima frase della risposta rivista, l'unica circostanza in cui è possibile eliminare le variabili dichiarate con varè quando la variabile è stata dichiarata con eval.
Stephen Booher,

1
In questo caso , l'istruzione delete non sembra fare nulla. Cosa sta succedendo qui?
Anderson Green,

@ AndersonGreen: le variabili globali decalizzate vengono create con il flag DontDelete , quindi non cancellabili. Quel codice si comporta esattamente come previsto.
RobG

35

Se stai dichiarando implicitamente la variabile senza var, il modo corretto sarebbe usare delete foo.

Tuttavia, dopo averlo eliminato, se si tenta di utilizzarlo in un'operazione come l'aggiunta a ReferenceErrorverrà generata perché non è possibile aggiungere una stringa a un identificatore non dichiarato e non definito. Esempio:

x = 5;
delete x
alert('foo' + x )
// ReferenceError: x is not defined

In alcune situazioni potrebbe essere più sicuro assegnarlo a false, null o indefinito, quindi viene dichiarato e non genererà questo tipo di errore.

foo = false

Si noti che in ECMAScript null, false, undefined, 0, NaN, o ''sarebbero tutti a valutare false. Assicurati solo di non usare l' !==operatore ma invece !=quando controlli il tipo per i booleani e non vuoi il controllo dell'identità (così nullfarebbe == falsee false == undefined).

Si noti inoltre che deletenon "elimina" riferimenti ma solo proprietà direttamente sull'oggetto, ad esempio:

bah = {}, foo = {}; bah.ref = foo;

delete bah.ref;
alert( [bah.ref, foo ] )
// ,[object Object] (it deleted the property but not the reference to the other object)

Se hai dichiarato una variabile con varnon puoi eliminarla:

(function() {
    var x = 5;
    alert(delete x)
    // false
})();

In Rhino:

js> var x
js> delete x
false

Né è possibile eliminare alcune proprietà predefinite come Math.PI:

js> delete Math.PI
false

Ci sono alcune strane eccezioni deletecome con qualsiasi lingua, se ti interessa abbastanza dovresti leggere:


Grazie per la risposta completa con tutti i dettagli. L'ho segnato per questo, ma ho accettato la risposta di Noè perché credo che per una semplice domanda la brevità sia più importante del completamento. Ancora una volta - grazie per l'ottimo lavoro svolto su questa risposta.
Guss,

30
some_var = null;

//or remove it..
delete some_var;

11
Questo non funziona se l'ambito di questo codice è una funzione. Vedi la risposta di @ noah per la soluzione corretta.
Roatin Marth,

1
Grazie per la risposta, ma ho accettato la risposta di Noè perché spiega meglio le insidie ​​di delete.
Guss,

3
non preoccuparti ... ho dato una risposta semplice "rapida e sporca" - @noah ha aggiunto tutti i dettagli per gli "altri" casi, quindi anche lui merita credito. ;-)
scunliffe,

7
Questo non è corretto deletefunziona solo per una proprietà. Impostandola, nullla variabile esiste ancora.
Derek 朕 會 功夫

1
Questa risposta è abbastanza buona per il caso più probabile in cui si controlla con "if (some_var) {..}"
BearCode

16

TLDR: semplici variabili definite (senza var, let, const) potrebbe essere cancellato con delete. Se si utilizza var, let, const- non potevano essere cancellati né per deletené con Reflect.deleteProperty.

Chrome 55:

simpleVar = "1";
"1"
delete simpleVar;
true
simpleVar;
VM439:1 Uncaught ReferenceError: simpleVar is not defined
    at <anonymous>:1:1
(anonymous) @ VM439:1
var varVar = "1";
undefined
delete varVar;
false
varVar;
"1"
let letVar = "1";
undefined
delete letVar;
true
letVar;
"1"
const constVar="1";
undefined
delete constVar;
true
constVar;
"1"
Reflect.deleteProperty (window, "constVar");
true
constVar;
"1"
Reflect.deleteProperty (window, "varVar");
false
varVar;
"1"
Reflect.deleteProperty (window, "letVar");
true
letVar;
"1"

FF Nightly 53.0a1 mostra lo stesso comportamento.


La tua risposta è tecnicamente corretta, quindi ottieni un punto, ma tutto ciò che hai scritto è coperto dalla risposta selezionata con molti più dettagli e riferimenti alle specifiche ECMAScript - in futuro sarebbe utile rivedere la risposta esistente prima di pubblicare.
Guss,

5
Concordato. Ma si parlava solo di un varcaso. Per quanto mi riguarda è stato interessante prova e condividere lete constcasi. Tuttavia, grazie per la nota. Cercheremo di essere più specifici la prossima volta.
Serj

4

ECMAScript 2015 offre l'API Reflect. È possibile eliminare la proprietà dell'oggetto con Reflect.deleteProperty () :

Reflect.deleteProperty(myObject, 'myProp');
// it is equivalent to:
delete myObject.myProp;
delete myObject['myProp'];

Per eliminare la proprietà windowdell'oggetto globale :

Reflect.deleteProperty(window, 'some_var');

In alcuni casi le proprietà non possono essere eliminate (quando la proprietà non è configurabile) e quindi questa funzione ritorna false(così come l' operatore di eliminazione ). In altri casi restituisce true:

Object.defineProperty(window, 'some_var', {
    configurable: false,
    writable: true,
    enumerable: true,
    value: 'some_val'
});

var frozen = Object.freeze({ myProperty: 'myValue' });
var regular = { myProperty: 'myValue' };
var blank = {};

console.log(Reflect.deleteProperty(window, 'some_var')); // false
console.log(window.some_var); // some_var

console.log(Reflect.deleteProperty(frozen, 'myProperty')); // false
console.log(frozen.myProperty); // myValue

console.log(Reflect.deleteProperty(regular, 'myProperty')); // true
console.log(regular.myProperty); // undefined

console.log(Reflect.deleteProperty(blank, 'notExistingProperty')); // true
console.log(blank.notExistingProperty); // undefined

Esiste una differenza tra deletePropertyfunzione e deleteoperatore quando eseguito in modalità rigorosa:

'use strict'

var frozen = Object.freeze({ myProperty: 'myValue' });

Reflect.deleteProperty(frozen, 'myProperty'); // false
delete frozen.myProperty;
// TypeError: property "myProperty" is non-configurable and can't be deleted

4

Le variabili, a differenza delle proprietà semplici, hanno l'attributo [[Configurabile]] , il che significa l'impossibilità di rimuovere una variabile tramite l' operatore di eliminazione . Tuttavia, esiste un contesto di esecuzione in cui questa regola non influisce. È il contesto eval : lì l'attributo [[Configurabile]] non è impostato per le variabili.



3

Oltre a ciò che tutti hanno scritto, nota anche che deleteritorna booleano. Può dirti se l'eliminazione ha avuto esito positivo o meno.

Test su Chrome, tutto tranne letera eliminabile. quando deleterestituito trueli ha effettivamente rimossi:

implicit_global = 1;
window.explicit_global = 1;
function_set = function() {};
function function_dec() { };
var declared_variable = 1;
let let_variable = 1;

delete delete implicit_global; // true, tested on Chrome 52
delete window.explicit_global; // true, tested on Chrome 52
delete function_set; // true, tested on Chrome 52
delete function_dec; // true, tested on Chrome 52
delete declared_variable; // true, tested on Chrome 52
delete let_variable; // false, tested on Chrome 78

Non è sempre corretto. Soprattutto in Chrome. Firefox restituisce tutto correttamente. Non testato in nessun altro browser. Per quanto riguarda letvar e constvars sta tornando vero cosa dovrebbe significare che la variabile è stata cancellata ma non lo è. Puoi verificarlo sia in Chrome che in FF. FF sembra restituire i valori corretti mentre Chrome non lo è. Quindi non sono sicuro di poter davvero fare affidamento su di esso. Vediamo:let letVar = "1"; undefined delete letVar; true letVar "1" typeof letVar; "string" const constVar="1"; undefined delete constVar; true constVar; "1" typeof constVar; "string"
Serj

1
Come indicato di seguito da jedierikb, l'articolo perfetto di kangax perfectionkills.com/understanding-delete descrive principalmente perché e come deletefunziona l'operatore. Ma non sta descrivendo perché la situazione letteralmente opposta con le funzioni. Peccato. Tuttavia, per quanto riguarda le variabili, le cose iniziano ad apparire molto più chiare.
Serj

2

Non è possibile eliminare una variabile se è stata dichiarata (con var x;) al momento del primo utilizzo. Tuttavia, se la tua variabile x è apparsa per la prima volta nello script senza una dichiarazione, puoi utilizzare l'operatore delete (delete x;) e la tua variabile verrà eliminata, molto simile alla cancellazione di un elemento di un array o alla cancellazione di una proprietà di un oggetto .


1

Sono un po 'confuso. Se tutto ciò che si desidera è che il valore di una variabile non passi a un altro script, non è necessario eliminare la variabile dall'ambito. Annullare semplicemente la variabile, quindi controllare esplicitamente se è o non è null. Perché affrontare il problema dell'eliminazione della variabile dall'ambito? A quale scopo questo server non può essere annullato?

foo = null;
if(foo === null) or if(foo !== null)

Il requisito è che lo script dell'ordine, che non è sotto il mio controllo, non vedrà l'esistenza della variabile - in particolare per il caso OP, lo script di destinazione ha un comportamento per il nullvalore che non voglio innescare.
Guss,

Nessun "backend" è stato abusato durante la produzione di questa domanda. Questi sono solo un paio di script su un sito Web in cui non ho alcun controllo tranne questo script.
Guss,

Entrambi gli script sono nello stesso documento o in documenti separati che uno chiama l'altro per caricare? Hai menzionato lo script degli ordini e lo script di destinazione. Se si tratta di passare una variabile a un altro script tramite una variabile get / post, la cancellerei sul backend prima che qualsiasi javascript ci metta le mani sopra. Un esempio di questo in php sarebbe qualcosa di simile. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm,

Vedo. Bene se ci sono controlli e saldi per null quindi impostarlo su un valore con cui lo script target non farà nulla sembra più logico quindi cancellare una variabile dall'ambito, ma sembri avere la tua risposta, quindi lascerò il cavallo in posa. Grazie per le tue risposte
designdrumm,

Una domanda veloce. Ci sarà mai uno script chiamato dopo il tuo che non sarà sotto il tuo controllo ma avrà comunque bisogno di questa variabile? In tal caso, quindi eliminare la variabile dall'ambito è una cattiva idea.
designdrumm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.