Como desmarcar uma variável JavaScript?

592

Eu tenho uma variável global em JavaScript (na verdade uma windowpropriedade, mas acho que não importa) que já foi preenchida por um script anterior, mas não quero outro script que seja executado mais tarde para ver seu valor ou até mesmo definiram.

Eu coloquei some_var = undefinede funciona com a finalidade de testar, typeof some_var == "undefined"mas realmente não acho que seja o caminho certo a fazer.

O que você acha?

Guss
fonte

Respostas:

454

O deleteoperador remove uma propriedade de um objeto. Não pode remover uma variável. Portanto, a resposta para a pergunta depende de como a variável ou propriedade global é definida.

(1) Se for criado com var, não poderá ser excluído.

Por exemplo:

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 for criado sem var, poderá ser excluído.

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

Explicação técnica

1. Usando var

Nesse caso, a referência g_aé criada no que a especificação ECMAScript chama de " VariableEnvironment " anexada ao escopo atual - este pode ser o contexto de execução de uma função no caso de usar vardentro de uma função (embora possa ser um pouco mais complicado quando você considera let) ou no caso de código "global", o VariableEnvironment é anexado ao objeto global (geralmente window).

Referências no VariableEnvironment normalmente não são deletable - o processo detalhado no ECMAScript 10,5 explica isso em detalhes, mas basta dizer que a menos que o código é executado em um evalcontexto (que a maioria dos consoles de desenvolvimento baseados em navegador usar), então as variáveis declaradas com varnão pode ser excluído.

2. Sem usar var

Ao tentar atribuir um valor a um nome sem usar a varpalavra - chave, o Javascript tenta localizar a referência nomeada no que a especificação ECMAScript chama de " LexicalEnvironment " e a principal diferença é que LexicalEvironment está aninhado - ou seja, um LexicalEnvironment tem um pai ( o que a especificação ECMAScript chama de "referência do ambiente externo") e quando o Javscript falha ao localizar a referência em um LexicalEenvironment , ele procura no LexicalEnvironment pai (conforme detalhado em 10.3.1 e 10.2.2.1 ). O LexicalEnvironment de nível superior é o " ambiente global", e que está vinculado ao objeto global, em que suas referências são as propriedades do objeto global. Portanto, se você tentar acessar um nome que não foi declarado usando uma varpalavra - chave no escopo atual ou em qualquer escopo externo, o Javascript eventualmente buscará uma propriedade do windowobjeto para servir como essa referência.Como aprendemos antes, as propriedades dos objetos podem ser excluídas.

Notas

  1. É importante lembrar que as vardeclarações são "içadas" - isto é, sempre são consideradas como ocorridas no início do escopo em que estão - embora não seja a inicialização de valor que pode ser feita em uma varinstrução - que é deixada onde está. . Portanto, no código a seguir, aé uma referência do VariableEnvironment e não a windowpropriedade e seu valor estará 10no final do código:

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

  2. A discussão acima é quando o "modo estrito" não está ativado. As regras de pesquisa são um pouco diferentes ao usar o "modo estrito" e as referências lexicais que teriam resolvido as propriedades da janela sem o "modo estrito" gerarão erros "variáveis ​​não declaradas" em "modo estrito". Eu realmente não entendi onde isso é especificado, mas é como os navegadores se comportam.

Dayong
fonte
8
O que você disse é um equívoco comum, mas na verdade está incorreto - no Javascript não há "variáveis ​​globais". Variáveis ​​definidas sem um escopo explícito (como usar varfora de uma função) são propriedades do "objeto global", que é o que ocorre nos navegadores da web window. Então - var a = 1; delete window.a; console.log(a);excluirá a variável com êxito e fará com que a última linha emita um erro de referência.
Guss
7
@Guss, seu código var a = 1; delete window.a; console.log(a);exibe 1. #
Dayong
5
Estou usando o Google Chrome v36. Eu testei em outros navegadores. Parece que não é consistente entre navegadores. O Chrome e o Opera exibiram 1, enquanto o Firefox, Safari e IE 11 no meu computador apresentaram um erro.
Dayong
3
Ok, meu erro. Veja ecma-international.org/ecma-262/5.1/#sec-10.5 (sub-pontos 2 e 8.c.ii): Ao executar meu teste no console do desenvolvedor, geralmente é considerado "contexto de avaliação" (embora talvez não no Chrome), então isso gera um erro. O mesmo código no contexto global de um documento real será exibido 1corretamente em todos os navegadores. Executando em documentos reais, seus exemplos de código estão corretos. Selecionei sua resposta como correta, mas agradeceria se você pudesse editá-la para incluir explicações window.a = 1; delete window.a;e possivelmente o mecanismo. Eu também posso fazer isso se você não se importar.
Guss
2
@KlaiderKlai yes. As variáveis ​​com escopo da função são criadas e destruídas sempre que a função é executada. Provavelmente o fechamento é uma exceção.
Dayong
278

A resposta da @ scunlife funcionará, mas tecnicamente deve ser

delete window.some_var; 

delete é suposto não funcionar quando o destino não é uma propriedade de objeto. por exemplo,

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

Mas como as variáveis ​​globais são na verdade membros do objeto window, ele funciona.

Quando cadeias de protótipos estão envolvidas, o uso de delete fica mais complexo, pois remove apenas a propriedade do objeto de destino, e não o protótipo. por exemplo,

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.

Por isso tem cuidado.

EDIT: Minha resposta é um pouco imprecisa (consulte "Conceitos errados " no final). O link explica todos os detalhes sangrentos, mas o resumo é que pode haver grandes diferenças entre os navegadores e dependendo do objeto do qual você está excluindo. delete object.somePropgeralmente deve ser seguro enquanto object !== window. Eu ainda não o usaria para excluir variáveis ​​declaradas com, varembora você possa, nas circunstâncias certas.

Noé
fonte
14
obrigado @jedierikb pelo link para esse artigo interessante. mais especificamente a esta parte < perfectionkills.com/understanding-delete/#misconceptions > daquele artigo em que o autor afirma que a declaração de noah "excluir é suposto ser um não-op" é bastante imprecisa, juntamente com uma excelente exlpanação, por que é imprecisa . (Não atire o mensageiro!)
Rob Wells
2
Em relação à última sentença da resposta revisada, a única circunstância na qual você pode excluir variáveis ​​declaradas com varé quando a variável foi declarada com eval.
Stephen Booher
1
Nesse caso , a instrução delete não parece fazer nada. O que está acontecendo aqui?
Anderson Green
@ AndersonGreen - variáveis ​​globais decalcadas são criadas com o sinalizador DontDelete para que não sejam deletáveis. Esse código se comporta exatamente como o esperado.
RobG
35

Se você estiver declarando implicitamente a variável sem var, a maneira correta seria usar delete foo.

No entanto, após a exclusão, se você tentar usá-lo em uma operação como a adição a ReferenceError, será lançada porque você não pode adicionar uma string a um identificador não declarado e indefinido. Exemplo:

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

Em algumas situações, pode ser mais seguro atribuí-lo a falso, nulo ou indefinido, para que seja declarado e não gerará esse tipo de erro.

foo = false

Note que no ECMAScript null, false, undefined, 0, NaN, ou ''que todos avaliar a false. Apenas certifique-se de não usar o !==operador; em vez disso, !=ao digitar booleanos e não desejar a verificação de identidade (o nullfaria == falseefalse == undefined ).

Observe também que deletenão "exclui" referências, mas apenas propriedades diretamente no objeto, por exemplo:

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 você declarou uma variável com, varnão pode excluí-la:

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

No Rhino:

js> var x
js> delete x
false

Também não é possível excluir algumas propriedades predefinidas, como Math.PI:

js> delete Math.PI
false

Existem algumas exceções estranhas a deletequalquer idioma, se você se importa o suficiente, você deve ler:

meder omuraliev
fonte
Obrigado pela resposta completa com todos os detalhes. Marquei isso para isso, mas aceitei a resposta de Noah porque acredito que, para uma simples pergunta, a brevidade é mais importante do que a conclusão. Mais uma vez - obrigado pelo excelente trabalho que você fez nesta resposta.
Guss
30
some_var = null;

//or remove it..
delete some_var;
scunliffe
fonte
11
Isso não funciona se o escopo deste código for uma função. Veja a resposta de @ noah para a solução correta.
Roatin Marth 20/10/2009
1
Obrigado pela resposta, mas aceitei a resposta de Noé porque explica melhor as armadilhas do delete.
Guss
3
não se preocupe ... Eu dei uma resposta simples "rápido e sujo" - @noah adicionou todos os detalhes para os "outros" casos, assim ele também merece crédito. ;-)
scunliffe 22/10/09
7
Isso não está correto. deletesó funciona para uma propriedade. Definindo nulla variável ainda existe.
Derek
1
Esta resposta é boa o suficiente para o caso mais provável em que você verifica com "if (some_var) {..}" #
BearCode
16

TLDR: simples definido variáveis (sem var, let, const) poderia ser suprimida com delete. Se você usar var, let, const- não podiam ser apagados nem com deletenem com 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 o mesmo comportamento.

Serj.by
fonte
Sua resposta está tecnicamente correta, então você entendeu, mas tudo o que você escreveu é coberto pela resposta selecionada com muito mais detalhes e referências às especificações do ECMAScript - no futuro, seria útil revisar a resposta existente antes de postar.
Guss
5
Acordado. Mas mencionou apenas um varcaso. Quanto a mim foi interessante para testar e participação lete constcasos bem. No entanto, obrigado pela observação. Tentará ser mais específico da próxima vez.
Serj
4

O ECMAScript 2015 oferece a API Reflect. É possível excluir a propriedade do objeto com Reflect.deleteProperty () :

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

Para excluir a propriedade do windowobjeto global :

Reflect.deleteProperty(window, 'some_var');

Em alguns casos, as propriedades não podem ser excluídas (quando a propriedade não é configurável) e, em seguida, essa função retorna false(assim como o operador delete ). Em outros casos, retornos 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

Há uma diferença entre deletePropertyfunção e deleteoperador quando executada no modo estrito:

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

Variáveis, em contraste com propriedades simples, têm o atributo [[Configurable]] , significando impossibilidade de remover uma variável através do operador delete . No entanto, há um contexto de execução no qual esta regra não afeta. É o contexto eval : o atributo [[Configurable]] não está definido para variáveis.

Eldiyar Talantbek
fonte
3

Além do que todos haviam escrito, observe também que delete retorna booleano. Pode dizer se a exclusão foi bem-sucedida ou não.

Testando no Chrome, tudo exceto letfoi delicioso. quando deleteretornado true, na verdade os removeu:

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
fonte
Nem sempre é correto. Especialmente no Chrome. O Firefox retorna tudo corretamente. Não testou em nenhum outro navegador. Quanto aos letvars e constvars, ele retorna true, o que deve significar que a variável foi excluída, mas não é. Você pode verificá-lo no Chrome e no FF. O FF parece retornar valores corretos, enquanto o Chrome não está. Portanto, não tenha certeza de que você realmente pode confiar nisso. Vamos ver: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
Como jedierikb mencionado abaixo, há um artigo perfeito de kangax perfectionkills.com/understanding-delete que descreve principalmente por que e como o deleteoperador trabalha. Mas não está descrevendo por que literalmente opõe a situação às funções. Que pena. No entanto, em relação às variáveis, as coisas começam a parecer muito mais claras.
usar o seguinte código
2

Você não pode excluir uma variável se a declarar (com var x;) no momento do primeiro uso. No entanto, se sua variável x apareceu pela primeira vez no script sem uma declaração, você pode usar o operador delete (delete x;) e sua variável será excluída, muito semelhante a excluir um elemento de uma matriz ou excluir uma propriedade de um objeto .

Satyapriya Mishra
fonte
1

Estou um pouco confuso. Se tudo o que você deseja é que um valor de variável não passe para outro script, não há necessidade de excluir a variável do escopo. Simplesmente anule a variável e verifique explicitamente se é ou não nula. Por que enfrentar o problema de excluir a variável do escopo? Que finalidade esse servidor que anula não pode?

foo = null;
if(foo === null) or if(foo !== null)
designdrumm
fonte
O requisito é que o script de pedido, que não está sob meu controle, não veja a variável existir - especificamente para o caso do OP, o script de destino tem um comportamento para o nullvalor que eu não quero acionar.
Guss
Nenhum "back-end" foi abusado durante a produção desta pergunta. Estes são apenas alguns scripts em um site onde eu não tenho controle de nada, exceto esse script.
Guss
Os dois scripts estão no mesmo documento ou em documentos separados que um chama o outro para carregar? Você mencionou o script do pedido e o script de destino. Se for uma questão de uma variável ser transmitida para outro script por meio de uma variável get / post, eu a excluiria no back-end antes de qualquer javascript colocar as mãos nela. Um exemplo disso no php seria algo como. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
Designdrumm 15/04/19
Eu vejo. Bem, se houver verificações e balanços para null, defini-lo como um valor com o qual o script de destino não fará nada parece mais lógico do que excluir uma variável do escopo, mas você deve ter sua resposta, então deixarei o cavalo descansar. Obrigado por suas respostas.
Designdrumm 15/04/19
Uma pergunta rápida. Alguma vez haverá um script chamado após o seu que não estará sob seu controle, mas ainda precisará dessa variável? Nesse caso, excluir a variável do escopo é uma má ideia.
Designdrumm 15/04/19