Comment désinstaller une variable JavaScript?

592

J'ai une variable globale en JavaScript (en fait une windowpropriété, mais je ne pense pas que ce soit important) qui était déjà remplie par un script précédent mais je ne veux pas qu'un autre script s'exécute plus tard pour voir sa valeur ou qu'il était même défini.

J'ai mis some_var = undefinedet cela fonctionne dans le but de tester, typeof some_var == "undefined"mais je ne pense vraiment pas que ce soit la bonne façon de procéder.

Qu'est-ce que tu penses?

Guss
la source

Réponses:

454

L' deleteopérateur supprime une propriété d'un objet. Il ne peut pas supprimer une variable. La réponse à la question dépend donc de la façon dont la variable ou la propriété globale est définie.

(1) S'il est créé avec var, il ne peut pas être supprimé.

Par exemple:

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) S'il est créé sans var, il peut être supprimé.

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

Explication technique

1. Utilisation var

Dans ce cas, la référence g_aest créée dans ce que la spécification ECMAScript appelle " VariableEnvironment " qui est attaché à la portée actuelle - cela peut être le contexte d'exécution d'une fonction dans le cas de l'utilisation à l' varintérieur d'une fonction (bien que cela puisse être un peu plus compliqué). quand on considère let) ou dans le cas du code "global", VariableEnvironment est attaché à l'objet global (souvent window).

Les références dans VariableEnvironment ne sont normalement pas supprimables - le processus détaillé dans ECMAScript 10.5 explique cela en détail, mais il suffit de dire qu'à moins que votre code ne soit exécuté dans un evalcontexte (que la plupart des consoles de développement basées sur un navigateur utilisent), les variables déclarées avec varne peuvent pas être supprimé.

2. Sans utiliser var

Lorsque vous essayez d'attribuer une valeur à un nom sans utiliser le varmot clé, Javascript essaie de localiser la référence nommée dans ce que la spécification ECMAScript appelle " LexicalEnvironment ", et la principale différence est que les LexicalEvironment sont imbriqués - c'est-à-dire qu'un LexicalEnvironment a un parent ( ce que la spécification ECMAScript appelle «référence d'environnement externe») et lorsque Javscript ne parvient pas à localiser la référence dans un environnement Lexical , il regarde dans l' environnement Lexical parent (comme détaillé en 10.3.1 et 10.2.2.1 ). Le plus haut niveau LexicalEnvironment est " l'environnement mondial", et qui est lié à l'objet global dans la mesure où ses références sont les propriétés de l'objet global. Donc, si vous essayez d'accéder à un nom qui n'a pas été déclaré à l'aide d'un varmot clé dans la portée actuelle ou dans une portée externe, Javascript finira par récupérer une propriété de l' windowobjet pour servir de référence. Comme nous l'avons vu précédemment, les propriétés des objets peuvent être supprimées.

Remarques

  1. Il est important de se rappeler que les vardéclarations sont "hissées" - c'est-à-dire qu'elles sont toujours considérées comme étant survenues au début de la portée dans laquelle elles se trouvent - mais pas l'initialisation de la valeur qui peut être effectuée dans une varinstruction - qui reste là où elle est . Ainsi, dans le code suivant, aest une référence de VariableEnvironment et non la windowpropriété et sa valeur sera 10à la fin du code:

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

  2. La discussion ci-dessus est lorsque le "mode strict" n'est pas activé. Les règles de recherche sont un peu différentes lors de l'utilisation du "mode strict" et les références lexicales qui auraient résolu les propriétés de la fenêtre sans "mode strict" déclencheront des erreurs "variable non déclarée" sous "mode strict". Je ne comprenais pas vraiment où cela est spécifié, mais son comportement des navigateurs.

Dayong
la source
8
Ce que vous avez dit est une idée fausse courante mais est en fait incorrect - en Javascript, il n'y a pas de "variables globales". Les variables définies sans portée explicite (comme l'utilisation en vardehors d'une fonction) sont des propriétés de "l'objet global", ce qui est le cas dans les navigateurs Web window. Donc - var a = 1; delete window.a; console.log(a);supprimera avec succès la variable et provoquera une erreur de référence sur la dernière ligne.
Guss
7
@Guss, votre code var a = 1; delete window.a; console.log(a);affiche 1.
Dayong
5
J'utilise Google Chrome v36. J'ai testé sur d'autres navigateurs. Il semble que ce ne soit pas des navigateurs croisés cohérents. Chrome et Opera ont affiché 1, tandis que Firefox, Safari et IE 11 sur mon ordinateur ont donné une erreur.
Dayong
3
Ok, mon erreur. Voir ecma-international.org/ecma-262/5.1/#sec-10.5 (sous-points 2 et 8.c.ii): lors de l'exécution de mon test dans la console développeur, il est généralement considéré comme "contexte eval" (bien que peut-être pas dans Chrome), cela générera donc une erreur. Le même code dans le contexte global d'un document réel sortira 1correctement dans tous les navigateurs. Fonctionnant dans de vrais documents, vos exemples de code sont corrects. J'ai choisi votre réponse comme correcte, mais j'apprécierais que vous puissiez la modifier pour inclure l'explication window.a = 1; delete window.a;et éventuellement le mécanisme. Je peux le faire aussi si cela ne vous dérange pas.
Guss
2
@KlaiderKlai yes. Les variables de portée de fonction sont créées et détruites à chaque fois que la fonction est exécutée. La fermeture est probablement une exception.
Dayong
278

La réponse de @ scunlife fonctionnera, mais techniquement, elle devrait être

delete window.some_var; 

delete est censé être un no-op lorsque la cible n'est pas une propriété d'objet. par exemple,

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

Mais puisque les variables globales sont en fait membres de l'objet window, cela fonctionne.

Lorsque des chaînes de prototypes sont impliquées, l'utilisation de la suppression devient plus complexe car elle supprime uniquement la propriété de l'objet cible, et non du prototype. par exemple,

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.

Donc sois prudent.

EDIT: Ma réponse est quelque peu inexacte (voir "Idées fausses" à la fin). Le lien explique tous les détails sanglants, mais le résumé est qu'il peut y avoir de grandes différences entre les navigateurs et selon l'objet que vous supprimez. delete object.somePropdevrait généralement être sûr aussi longtemps que object !== window. Je ne l'utiliserais toujours pas pour supprimer les variables déclarées avec varbien que vous puissiez dans les bonnes circonstances.

Noé
la source
14
merci @jedierikb pour le lien vers cet article intéressant. plus précisément à cette partie < perfectionkills.com/understanding-delete/#misconceptions > de cet article où l'auteur déclare que la déclaration de noah "supprimer est censé être un non-op" est plutôt inexacte avec une excellente explication pourquoi elle est inexacte . (Ne tirez pas sur le messager!)
Rob Wells
2
En ce qui concerne la dernière phrase de la réponse révisée, la seule circonstance dans laquelle vous pouvez supprimer des variables déclarées avec varest lorsque la variable a été déclarée avec eval.
Stephen Booher
1
Dans ce cas , l'instruction delete ne semble rien faire du tout. Que se passe t-il ici?
Anderson Green
@ AndersonGreen: les variables globales décalquées sont créées avec l' indicateur DontDelete et ne peuvent donc pas être supprimées. Ce code se comporte exactement comme prévu.
RobG
35

Si vous déclarez implicitement la variable sans var, la bonne façon serait d'utiliser delete foo.

Cependant, après l'avoir supprimé, si vous essayez de l'utiliser dans une opération telle que l'ajout, un ReferenceErrorsera levé car vous ne pouvez pas ajouter de chaîne à un identifiant non déclaré et non défini. Exemple:

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

Il peut être plus sûr dans certaines situations de l'affecter à false, null ou indéfini afin qu'il soit déclaré et ne génère pas ce type d'erreur.

foo = false

Notez que dans ECMAScript null, false, undefined, 0, NaNou ''serait tout à évaluer false. Assurez-vous simplement que vous n'utilisez pas l' !==opérateur mais à la place !=lors de la vérification de type pour les booléens et que vous ne voulez pas de vérification d'identité (comme le nullferait == falseet false == undefined).

Notez également que deletene "supprime" pas les références mais seulement les propriétés directement sur l'objet, par exemple:

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)

Si vous avez déclaré une variable avec varvous ne pouvez pas la supprimer:

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

Dans Rhino:

js> var x
js> delete x
false

Vous ne pouvez pas non plus supprimer certaines propriétés prédéfinies comme Math.PI:

js> delete Math.PI
false

Il y a quelques exceptions étranges à deletecomme avec n'importe quelle langue, si vous vous souciez suffisamment, vous devriez lire:

meder omuraliev
la source
Merci pour la réponse complète avec tous les détails. Je l'ai marqué pour cela, mais j'ai accepté la réponse de Noah parce que je crois que pour une simple question, la brièveté est plus importante que l'achèvement. Encore une fois - merci pour l'excellent travail que vous avez fait sur cette réponse.
Guss
30
some_var = null;

//or remove it..
delete some_var;
scunliffe
la source
11
Cela ne fonctionne pas si la portée de ce code est une fonction. Voir la réponse de @ noah pour la bonne solution.
Roatin Marth
1
Merci pour la réponse, mais j'ai accepté la réponse de Noah car elle explique mieux les pièges de delete.
Guss
3
pas de soucis ... J'ai donné une réponse simple "rapide et sale" - @noah a ajouté tous les détails pour les "autres" cas, donc il mérite aussi le crédit. ;-)
scunliffe
7
Ce n'est pas correct. deletene fonctionne que pour une propriété. En nullla définissant, la variable existe toujours.
Derek 朕 會 功夫
1
Cette réponse est assez bonne pour le cas le plus probable où vous vérifiez avec "if (some_var) {..}"
BearCode
16

TLDR: simples variables définies par (sans var, let, const) pourrait être supprimé avec delete. Si vous utilisez var, let, const- ils ne pouvaient pas être supprimés ni avec deleteni avec 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 montre le même comportement.

Serj.by
la source
Votre réponse est techniquement correcte, vous obtenez donc un point, mais tout ce que vous avez écrit est couvert par la réponse sélectionnée avec beaucoup plus de détails et de références aux spécifications ECMAScript - à l'avenir, il serait utile de revoir la réponse existante avant de poster.
Guss
5
D'accord. Mais il n'a mentionné que le varcas. Quant à moi, c'était intéressant de tester et de partager letainsi que des constcas. Cependant, merci pour la note. J'essaierai d'être plus précis la prochaine fois.
Serj.by
4

ECMAScript 2015 propose l'API Reflect. Il est possible de supprimer la propriété d'objet avec Reflect.deleteProperty () :

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

Pour supprimer la propriété d'un windowobjet global :

Reflect.deleteProperty(window, 'some_var');

Dans certains cas, les propriétés ne peuvent pas être supprimées (lorsque la propriété n'est pas configurable), puis cette fonction revient false(ainsi que l' opérateur de suppression ). Dans les autres cas, retourne 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

Il y a une différence entre la deletePropertyfonction et l' deleteopérateur lorsqu'il est exécuté en mode strict:

'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
madox2
la source
4

Les variables, contrairement aux propriétés simples, ont l'attribut [[Configurable]] , ce qui signifie l'impossibilité de supprimer une variable via l' opérateur de suppression . Cependant, il existe un contexte d'exécution sur lequel cette règle n'affecte pas. C'est le contexte eval : l'attribut [[Configurable]] n'est pas défini pour les variables.

Eldiyar Talantbek
la source
3

En plus de ce que tout le monde avait écrit, notons également que les deleteretours booléens. Il peut vous dire si la suppression a réussi ou non.

Test sur Chrome, tout sauf letétait délassable. une fois deleteretourné, trueil les supprimait:

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
oriadam
la source
Ce n'est pas toujours correct. Surtout dans Chrome. Firefox renvoie tout correctement. N'a pas testé dans d'autres navigateurs. Quant aux letvars et constvars, il renvoie vrai ce qui devrait signifier que la variable a été supprimée mais ce n'est pas le cas. Vous pouvez le vérifier dans Chrome et FF. FF semble renvoyer des valeurs correctes alors que Chrome ne l'est pas. Alors ne savez pas que vous pouvez vraiment compter dessus. Voyons: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.by
1
Comme jedierikb l'a mentionné ci-dessous, il y a un article parfait par kangax perfectionkills.com/understanding-delete qui décrit principalement pourquoi et comment deletefonctionne l'opérateur. Mais il ne décrit pas pourquoi la situation est littéralement opposée aux fonctions. Dommage. Cependant, en ce qui concerne les variables, les choses commencent à sembler beaucoup plus claires.
Serj.by
2

Vous ne pouvez pas supprimer une variable si vous l'avez déclarée (avec var x;) au moment de la première utilisation. Cependant, si votre variable x est apparue pour la première fois dans le script sans déclaration, alors vous pouvez utiliser l'opérateur de suppression (delete x;) et votre variable sera supprimée, très similaire à la suppression d'un élément d'un tableau ou la suppression d'une propriété d'un objet .

Satyapriya Mishra
la source
1

Je suis un peu confus. Si tout ce que vous voulez, c'est qu'une valeur de variable ne passe pas à un autre script, il n'est pas nécessaire de supprimer la variable de la portée. Annulez simplement la variable puis vérifiez explicitement si elle est ou non nulle. Pourquoi se donner la peine de supprimer la variable de la portée? À quoi sert ce serveur qui annulant ne peut pas?

foo = null;
if(foo === null) or if(foo !== null)
designdrumm
la source
L'exigence est que le script de commande, qui n'est pas sous mon contrôle, ne verra pas que la variable existe - spécifiquement pour le cas OP, le script cible a un comportement pour la nullvaleur que je ne veux pas déclencher.
Guss
Aucun "backend" n'a été abusé lors de la production de cette question. Ce ne sont que quelques scripts sur un site Web où je n'ai aucun contrôle, sauf celui-ci.
Guss
Les deux scripts sont-ils dans le même document ou dans des documents séparés que l'un appelle l'autre à charger? Vous avez mentionné le script de commande et le script cible. S'il s'agit d'une variable transmise à un autre script via une variable get / post, je la supprimerais sur le backend avant que tout javascript ne mette la main dessus. Un exemple de cela en php serait quelque chose comme. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm
Je vois. Eh bien, s'il existe des vérifications et des équilibres pour null, le définir sur une valeur avec laquelle le script cible ne fera rien semble plus logique que la suppression d'une variable de la portée, mais vous cherchez à avoir votre réponse, alors je vais laisser le cheval pondre. Merci pour vos réponses.
designdrumm
Une petite question. Y aura-t-il jamais un script appelé après le vôtre qui ne sera pas sous votre contrôle mais qui aura toujours besoin de cette variable? Si c'est le cas, la suppression de la variable de la portée est une mauvaise idée.
designdrumm