¿Cómo desarmar una variable de JavaScript?

592

Tengo una variable global en JavaScript (en realidad, una windowpropiedad, pero no creo que importe) que ya estaba poblada por un script anterior, pero no quiero otro script que se ejecute más tarde para ver su valor o que incluso definido.

Lo he puesto some_var = undefinedy funciona con el propósito de probarlo, typeof some_var == "undefined"pero realmente no creo que sea la forma correcta de hacerlo.

¿Qué piensas?

Guss
fuente

Respuestas:

454

El deleteoperador elimina una propiedad de un objeto. No puede eliminar una variable. Entonces, la respuesta a la pregunta depende de cómo se defina la variable o propiedad global.

(1) Si se crea con var, no se puede eliminar.

Por ejemplo:

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) Si se crea sin var, se puede eliminar.

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

Explicación técnica

1. Usando var

En este caso, la referencia g_ase crea en lo que la especificación ECMAScript llama " VariableEnvironment " que se adjunta al alcance actual; este puede ser el contexto de ejecución de una función en el caso de usar vardentro de una función (aunque puede ser un poco más complicado cuando lo considere let) o en el caso del código "global", el entorno variable se adjunta al objeto global (a menudo window).

Las referencias en el entorno Variable normalmente no se pueden eliminar: el proceso detallado en ECMAScript 10.5 explica esto en detalle, pero es suficiente decir que a menos que su código se ejecute en un evalcontexto (que la mayoría de las consolas de desarrollo basadas en navegador usan), entonces las variables declaradas con varno pueden ser eliminado

2. Sin usar var

Al intentar asignar un valor a un nombre sin usar la varpalabra clave, Javascript intenta ubicar la referencia nombrada en lo que la especificación ECMAScript llama " LexicalEnvironment ", y la principal diferencia es que LexicalEvironment s está anidado, es decir, un entorno Lexical tiene un padre ( lo que la especificación ECMAScript llama "referencia de entorno externo") y cuando Javscript no puede localizar la referencia en un entorno Lexical , se ve en el padre entorno Lexical (como se detalla en 10.3.1 y 10.2.2.1 ). El nivel superior de LexicalEnvironment es el " entorno global", y eso está vinculado al objeto global en el sentido de que sus referencias son las propiedades del objeto global. Por lo tanto, si intenta acceder a un nombre que no se declaró utilizando una varpalabra clave en el ámbito actual o cualquier ámbito externo, Javascript finalmente obtendrá una propiedad del windowobjeto para que sirva como referencia. Como hemos aprendido antes, las propiedades de los objetos se pueden eliminar.

Notas

  1. Es importante recordar que las vardeclaraciones son "izadas", es decir, siempre se considera que ocurrieron al comienzo del alcance en el que se encuentran, aunque no la inicialización del valor que se puede hacer en una vardeclaración, que se deja donde está. . Entonces, en el siguiente código, aes una referencia del entorno Variable y no elwindow propiedad y su valor estará 10al final del código:

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

  2. La discusión anterior es cuando el "modo estricto" no está habilitado. Las reglas de búsqueda son un poco diferentes cuando se utiliza el "modo estricto" y las referencias léxicas que se habrían resuelto a las propiedades de la ventana sin el "modo estricto" generarán errores de "variable no declarada" en el "modo estricto". Realmente no entendí dónde se especifica esto, pero es cómo se comportan los navegadores.

Dayong
fuente
8
Lo que usted dijo es un error común, pero en realidad es incorrecto: en Javascript no hay "variables globales". Las variables definidas sin un alcance explícito (como el uso varfuera de una función) son propiedades del "objeto global", que es en los navegadores web window. Entonces, var a = 1; delete window.a; console.log(a);eliminará con éxito la variable y hará que la última línea emita un error de referencia.
Guss
77
@ Guss, su código var a = 1; delete window.a; console.log(a);muestra 1.
Día
55
Estoy usando Google Chrome v36. Probé en otros navegadores. Parece que no es un navegador cruzado consistente. Chrome y Opera mostraron 1, mientras que Firefox, Safari e IE 11 en mi computadora dieron un error.
Dayong
3
Ok, mi error Ver ecma-international.org/ecma-262/5.1/#sec-10.5 ( subpuntos 2 y 8.c.ii): cuando ejecuto mi prueba en la consola del desarrollador, generalmente se considera "contexto de evaluación" (aunque tal vez no en Chrome), por lo que generará un error. El mismo código en el contexto global de un documento real saldrá 1correctamente en todos los navegadores. Ejecutando en documentos reales, sus ejemplos de código son correctos. Seleccioné su respuesta como correcta, pero agradecería si puede editarla para incluir explicaciones window.a = 1; delete window.a;y posiblemente el mecanismo. También puedo hacerlo si no te importa.
Guss
2
@KlaiderKlai sí. Las variables de ámbito de función se crean y destruyen cada vez que se ejecuta la función. Probablemente el cierre es una excepción.
Dayong
278

La respuesta de @ scunlife funcionará, pero técnicamente debería ser

delete window.some_var; 

Se supone que eliminar es un no-op cuando el objetivo no es una propiedad de objeto. p.ej,

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

Pero dado que las variables globales son en realidad miembros del objeto de ventana, funciona.

Cuando las cadenas de prototipos están involucradas, el uso de eliminar se vuelve más complejo porque solo elimina la propiedad del objeto de destino, y no el prototipo. p.ej,

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.

Así que ten cuidado.

EDITAR: Mi respuesta es algo inexacta (ver "Conceptos erróneos" al final). El enlace explica todos los detalles sangrientos, pero el resumen es que puede haber grandes diferencias entre los navegadores y dependiendo del objeto desde el que está borrando. delete object.somePropgeneralmente debe ser seguro mientras object !== window. Todavía no lo usaría para eliminar variables declaradas con, varaunque puede hacerlo en las circunstancias correctas.

Noé
fuente
14
gracias @jedierikb por el enlace a ese interesante artículo. más específicamente a esta parte < perfectionkills.com/understanding-delete/#misconceptions > de ese artículo donde el autor afirma que la declaración de noah "se supone que es un no-op" es bastante inexacta junto con una excelente explicación de por qué es inexacta . (¡No le dispares al mensajero!)
Rob Wells
2
Con respecto a la última oración de la respuesta revisada, la única circunstancia en la que puede eliminar las variables declaradas con vares cuando la variable se declaró con eval.
Stephen Booher
1
En este caso , la declaración de eliminación no parece hacer nada en absoluto. ¿Que está pasando aqui?
Anderson Green
@ AndersonGreen: las variables globales descalificadas se crean con el indicador DontDelete , por lo que no se pueden eliminar. Ese código se comporta exactamente como se esperaba.
RobG
35

Si está declarando implícitamente la variable sin var, la forma correcta sería utilizarla delete foo.

Sin embargo, después de eliminarlo, si intenta usar esto en una operación como la adición ReferenceError, se generará un a porque no puede agregar una cadena a un identificador no declarado ni definido. Ejemplo:

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

Puede ser más seguro en algunas situaciones asignarlo a falso, nulo o indefinido para que se declare y no arroje este tipo de error.

foo = false

Nótese que en ECMAScript null, false, undefined, 0, NaN, o ''sería toda evaluar a false. Solo asegúrese de no usar el !==operador, sino que !=cuando escriba la comprobación de booleanos y no desee la comprobación de identidad (también lo nullharía == falsey false == undefined).

También tenga en cuenta que deleteno "elimina" las referencias sino solo las propiedades directamente en el objeto, por ejemplo:

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 ha declarado una variable con varno puede eliminarla:

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

En rinoceronte:

js> var x
js> delete x
false

Tampoco puede eliminar algunas propiedades predefinidas como Math.PI:

js> delete Math.PI
false

Hay algunas excepciones extrañas al deleteigual que con cualquier idioma, si le importa lo suficiente debe leer:

meder omuraliev
fuente
Gracias por la respuesta completa con todos los detalles. Lo marqué para esto, pero acepté la respuesta de Noah porque creo que, para una simple pregunta, la brevedad es más importante que la finalización. Nuevamente, gracias por el gran trabajo que hizo en esta respuesta.
Guss
30
some_var = null;

//or remove it..
delete some_var;
scunliffe
fuente
11
Esto no funciona si el alcance de este código es una función. Vea la respuesta de @noah para la solución correcta.
Roatin Marth
1
Gracias por la respuesta, pero acepté la respuesta de Noah porque explica mejor las trampas de delete.
Guss
3
no se preocupe ... Di una respuesta simple "rápida y sucia": @noah agregó todos los detalles de los "otros" casos, por lo que también merece crédito. ;-)
scunliffe
77
Esto no es correcto. deletesolo funciona para una propiedad. Al configurarlo, nullla variable todavía existe.
Derek 朕 會 功夫
1
Esta respuesta es lo suficientemente buena para el caso más probable en el que verifica con "if (some_var) {..}"
BearCode
16

TLDR: sencilla define las variables (sin var, let, const) podría suprimirse con delete. Si utiliza var, let, const- no podían ser borrados ni con deleteni 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 muestra el mismo comportamiento.

Serj.by
fuente
Su respuesta es técnicamente correcta, por lo que obtiene un punto, pero todo lo que escribió está cubierto por la respuesta seleccionada con muchos más detalles y referencias a las especificaciones de ECMAScript; en el futuro sería útil revisar la respuesta existente antes de publicarla.
Guss
55
Convenido. Pero allí solo se menciona el varcaso. En cuanto a mí, fue interesante probar y compartir lety constcasos también. Sin embargo, gracias por la nota. Intentaremos ser más específicos la próxima vez.
Serj
4

ECMAScript 2015 ofrece Reflect API. Es posible eliminar la propiedad del objeto con Reflect.deleteProperty () :

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

Para eliminar la propiedad del windowobjeto global :

Reflect.deleteProperty(window, 'some_var');

En algunos casos, las propiedades no se pueden eliminar (cuando la propiedad no es configurable) y luego se devuelve esta función false(así como eliminar el operador ). En otros casos devuelve 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

Hay una diferencia entre deletePropertyfunción y deleteoperador cuando se ejecuta en modo estricto:

'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
fuente
4

Las variables, en contraste con las propiedades simples, tienen el atributo [[Configurable]] , lo que significa que es imposible eliminar una variable a través del operador de eliminación . Sin embargo, hay un contexto de ejecución en el que esta regla no afecta. Es el contexto de evaluación : el atributo [[Configurable]] no está configurado para variables.

Eldiyar Talantbek
fuente
3

Además de lo que todos habían escrito, también tenga en cuenta que deletedevuelve boolean. Puede decirle si la eliminación fue exitosa o no.

Pruebas en Chrome, todo excepto letdeleltable. cuando deleteregresó, trueen realidad los eliminó:

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
fuente
No siempre es correcto. Especialmente en Chrome. Firefox devuelve todo correctamente. No probé en ningún otro navegador. En cuanto a letvars y constvars, devuelve true lo que debería significar que la variable se eliminó, pero no lo es. Puede verificarlo tanto en Chrome como en FF. FF parece devolver los valores correctos mientras que Chrome no lo está. Así que no estoy seguro de que realmente pueda confiar en ello. Veamos: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
Como jedierikb menciona a continuación, hay un artículo perfecto de kangax perfectionkills.com/understanding-delete que describe principalmente por qué y cómo deletefunciona el operador. Pero no está describiendo por qué literalmente oponen la situación con las funciones. Como una pena Sin embargo, con respecto a las variables, las cosas comienzan a parecer mucho más claras.
Serj
2

No puede eliminar una variable si la declaró (con var x;) en el momento del primer uso. Sin embargo, si su variable x apareció por primera vez en el script sin una declaración, puede usar el operador delete (delete x;) y su variable se eliminará, muy similar a eliminar un elemento de una matriz o eliminar una propiedad de un objeto .

Satyapriya Mishra
fuente
1

Estoy un poco confundido Si todo lo que desea es que un valor de variable no pase a otro script, entonces no hay necesidad de eliminar la variable del ámbito. Simplemente anule la variable y luego compruebe explícitamente si es nula o no. ¿Por qué pasar por la molestia de eliminar la variable del alcance? ¿Para qué sirve este servidor que no puede anular?

foo = null;
if(foo === null) or if(foo !== null)
designdrumm
fuente
El requisito es que el script de orden, que no está bajo mi control, no verá que existe la variable; específicamente para el caso de OP, el script de destino tiene un comportamiento para el nullvalor que no quiero activar.
Guss
No se abusó de ningún "backend" durante la producción de esta pregunta. Estos son solo un par de scripts en un sitio web donde no tengo control de nada, excepto este script.
Guss
¿Ambas secuencias de comandos están en el mismo documento o en documentos separados que uno llama al otro para cargar? Usted mencionó el script de pedido y el script de destino. Si se trata de una variable que se pasa a otro script a través de una variable get / post, entonces la eliminaría en el backend antes de que cualquier javascript lo tenga en sus manos. Un ejemplo de esto en php sería algo así. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm
Veo. Bueno, si hay controles y equilibrios para nulo, establecerlo en un valor con el que el script de destino no hará nada parece más lógico que eliminar una variable del alcance, pero parece que tiene su respuesta, así que dejaré que el caballo descanse. Gracias por sus respuestas
designdrumm
Una pregunta rapida. ¿Habrá alguna vez una secuencia de comandos que se llame después de la suya que no estará bajo su control pero que aún necesitará esta variable? Si es así, eliminar la variable del alcance es una mala idea.
designdrumm