如何取消设置JavaScript变量?


591

我在JavaScript中有一个全局变量(实际上是一个window属性,但我认为这并不重要),该变量已经由先前的脚本填充,但是我不希望另一个脚本稍后运行以查看其值,甚至定义。

我已经说过了some_var = undefined,它可以用于测试目的,typeof some_var == "undefined"但是我真的不认为这是正确的方法。

你怎么看?

Answers:


453

delete操作者,将删除该对象的属性。它不能删除变量。因此,问题的答案取决于如何定义全局变量或属性。

(1)如果使用创建var,则无法删除。

例如:

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)如果创建时不带var,则可以将其删除。

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

技术说明

1.使用 var

在这种情况下,引用g_a是在ECMAScript规范称为“ VariableEnvironment ”的情况下创建的,该引用附加到当前作用域-在函数var内部使用的情况下,这可能是函数执行上下文(尽管可能会有些复杂)当您考虑let)时,或者在使用“全局”代码的情况下,VariableEnvironment会附加到全局对象(通常是window)。

VariableEnvironment中的引用通常不可删除-ECMAScript 10.5中详细介绍的过程对此进行了详细说明,但足以说明,除非您的代码在eval上下文中执行(大多数基于浏览器的开发控制台都在使用),否则使用声明的变量var不能被删除。

2.不使用 var

当尝试在不使用var关键字的情况下为名称分配值时,Javascript尝试在ECMAScript规范称为“ LexicalEnvironment ”的位置查找命名的引用,主要区别在于LexicalEvironment是嵌套的-LexicalEnvironment具有父级( ECMAScript规范称为“外部环境引用”),并且当Javscript无法在LexicalEenvironment中找到该引用时,它将在父LexicalEnvironment中查找(如10.3.110.2.2.1中所述)。顶级LexicalEnvironment是“ 全球环境”,并且绑定到全局对象,因为它的引用是全局对象的属性。因此,如果您尝试访问未使用var当前范围或任何外部范围中的关键字声明的名称,则Javascript最终将获取属性该的window对象充当参考。正如我们之前了解到,在对象的属性可以被删除。

笔记

  1. 重要的是要记住var声明是“悬挂的”-即始终认为它们发生在它们所作用域的开头-尽管不是var语句中可能进行的值初始化-保留在声明的原处。因此,在下面的代码中,a是来自VariableEnvironment的引用,而不是该window属性的引用,并且其值将10位于代码的末尾:

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

  2. 以上讨论是在未启用“严格模式”的情况下。使用“严格模式”时,查找规则略有不同,如果在没有“严格模式”的情况下解析为窗口属性的词法引用将在“严格模式”下引发“未声明变量”错误。我不太了解在哪里指定它,但是它在浏览器中的表现如何。


8
您所说的是一个普遍的误解,但实际上是不正确的-在Javascript中,没有“全局变量”。在没有明确范围的情况下定义的变量(例如var在函数外部使用)是“全局对象”的属性,在Web浏览器中为window。因此- var a = 1; delete window.a; console.log(a);将成功删除变量并导致最后一行发出引用错误。
2014年

7
@Guss,你的代码var a = 1; delete window.a; console.log(a);显示1
张家界

5
我正在使用Google Chrome v36。我在其他浏览器上进行了测试。似乎不是一致的跨浏览器。Chrome和Opera显示1,而我计算机上的Firefox,Safari和IE 11却显示错误。
Dayong 2014年

3
好吧,我的错。请参阅ecma-international.org/ecma-262/5.1/#sec-10.5(子点2和8.c.ii):在开发人员控制台中运行测试时,通常将其视为“评估上下文”(尽管也许不在Chrome中),因此会引发错误。真实文档的全局上下文中的相同代码将1在所有浏览器中正确输出。在真实的文档中运行,您的代码示例是正确的。我选择的答案是正确的,但如果您可以对其进行编辑以包括解释window.a = 1; delete window.a;以及可能的机制,我们将不胜感激。如果您不介意,我也可以这样做。
2014年

2
@KlaiderKlai是的。每次执行函数时,都会创建和销毁函数范围的变量。可能关闭是一个例外。
Dayong 2013年

278

@scunlife的答案会起作用,但从技术上讲应该是

delete window.some_var; 

当目标不是对象属性时,delete应该被禁止使用。例如,

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

但是由于全局变量实际上是window对象的成员,因此它可以工作。

当涉及原型链时,使用delete会变得更加复杂,因为它只会从目标对象中移除属性,而不会从原型中移除属性。例如,

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.

所以要小心

编辑:我的回答有点不准确(请参阅末尾的“误解”)。该链接说明了所有详细信息,但摘要是,浏览器之间可能存在很大差异,并且取决于要删除的对象。delete object.someProp一般只要保持安全object !== windowvar尽管您可以在适当的情况下使用,但我仍然不会使用它来删除声明的变量。


14
感谢@jedierikb提供该有趣文章的链接。更具体地说,对于该部分的该部分< perfectionkills.com/understanding-delete/#misconceptions >,作者指出,诺亚的陈述“删除应被视为无人操作”是不准确的,并且带有很好的解释,为什么它是不正确的。(不要开枪!)
罗伯·威尔斯

2
关于修改后的答案的最后一句话,唯一可以删除用声明var的变量的情况是用声明的变量eval
Stephen Booher 2012年

1
这种情况下,delete语句似乎根本不执行任何操作。这里发生了什么?
安德森·格林

@AndersonGreen-使用DontDelete标志创建的十进制全局变量不可删除。该代码的行为完全符合预期。
RobG 2014年

35

如果隐式声明不带变量var,则正确的方法是使用delete foo

但是,将其删除后,如果尝试在诸如添加a之类的操作中使用它,则会ReferenceError抛出该错误,因为您无法将字符串添加至未声明的未定义标识符。例:

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

在某些情况下,将其分配为false,null或undefined可能更为安全,因此将其声明且不会引发此类错误。

foo = false

请注意,在ECMAScript中nullfalseundefined0NaN,或者''将所有的计算结果为false。只要确保你不使用的!==运营商,而是!=当布尔类型检查,你不想身份检查(所以null== falsefalse == undefined)。

还要注意,delete不是“删除”引用而是仅直接在对象上的属性,例如:

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)

如果使用声明了变量,var则无法删除它:

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

在犀牛中:

js> var x
js> delete x
false

您也不能删除一些预定义的属性,例如Math.PI

js> delete Math.PI
false

delete任何一种语言都有一些奇怪的例外,如果您足够在意,则应阅读:


感谢您提供所有详细信息的完整答案。我为此做了标记,但是我接受了诺亚的回答,因为我认为对于一个简单的问题,简洁比完成更重要。再次-感谢您在此答案上所做的出色工作。
古斯

30
some_var = null;

//or remove it..
delete some_var;

11
如果此代码的范围是函数,则此方法不起作用。有关正确的解决方案,请参见@noah的答案。
Roatin Marth,2009年

1
感谢您的回答,但我接受了诺亚的回答,因为它可以更好地说明的陷阱delete
古斯

3
不用担心...我给了一个“快速n肮脏”的简单答案-@noah添加了“其他”案件的所有详细信息,因此他也应得到荣誉。;-)
scunliffe

7
这是不正确的。delete仅适用于物业。设置它null变量仍然存在。
德里克·朕会功夫,2012年

1
对于大多数情况下,您使用“ if(some_var){..}”进行检查,此答案就足够了
BearCode 2013年

16

TLDR:简单定义的变量(无varletconst)可以与被删除delete。如果使用varletconst-他们不能既不删除delete也不符合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显示相同的行为。


您的答案在技术上是正确的,因此您可以理解,但是所选答案涵盖了您编写的所有内容,其中包含更多详细信息和对ECMAScript规范的引用-将来在发布之前复查现有答案会很有用。
古斯

5
同意 但是这里只提到了var情况。对于我来说这是有趣的测试和分享let,并const案件。但是,感谢您的注意。下次将尝试更具体。
Serj.by

4

ECMAScript 2015提供了Reflect API。可以使用Reflect.deleteProperty()删除对象属性:

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

要删除全局window对象的属性:

Reflect.deleteProperty(window, 'some_var');

在某些情况下,无法删除属性(当属性不可配置时),然后此函数返回false(以及delete运算符)。在其他情况下,返回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

在严格模式下运行时,deleteProperty函数和delete运算符之间存在区别:

'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

与简单属性相反,变量具有属性[[Configurable]],这意味着无法通过delete运算符删除变量。但是,有一个执行上下文不会影响此规则。它是评估的上下文:没有为变量设置[[Configurable]]属性。



3

除了大家写的东西外,还要注意 delete返回布尔值。它可以告诉您删除是否成功。

在Chrome上进行测试,除可let删除之外的所有内容都可以。当delete返回时,true它实际上删除了它们:

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

这并不总是正确的。特别是在Chrome中。Firefox会正确返回所有内容。没有在任何其他浏览器中进行测试。至于letvar和constvars,它返回true,这意味着该变量已删除,但不是。您可以在Chrome和FF中进行检查。FF似乎返回正确的值,而Chrome没有。因此,不确定您是否真的可以依靠它。让我们看看: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
正如jedierikb在下面提到的那样,kaangax perfectionkills.com/understanding-delete上有一篇完美的文章,主要描述了delete操作员为什么以及如何工作。但这并没有说明为什么功能与字面意思相反。真可惜。但是,关于变量,事情似乎开始变得更加清晰。
Serj.by

2

如果在首次使用时声明了变量(使用var x;),则不能删除该变量。但是,如果变量x首先不带声明地出现在脚本中,则可以使用delete运算符(删除x;),变量将被删除,这与删除数组的元素或删除对象的属性非常相似。 。


1

我有点困惑。如果您只希望变量值不传递到另一个脚本,则无需从作用域中删除变量。只需使变量无效,然后显式检查其是否为null。为什么要麻烦从范围中删除变量?无效的服务器不能达到什么目的?

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

要求是不受我控制的订单脚本将不会看到该变量存在-特别是在OP情况下,目标脚本具有null我不想触发的值的行为。
古斯

在产生此问题期间,没有滥用任何“后端”。这些只是网站上的几个脚本,除了这个脚本之外,我什么也无法控制。
古斯

这两个脚本是在同一文档中还是在一个单独的文档中,而一个脚本又调用另一个脚本来加载?您提到了订单脚本和目标脚本。如果是通过get / post变量将变量传递给另一个脚本的问题,那么我将在后端将其删除之前,将所有javascript移交给它。php中的一个例子就是这样。 <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm

我知道了。好吧,如果存在制衡功能,那么将其设置为值,则目标脚本将不执行任何操作,似乎更合乎逻辑,然后从作用域中删除变量,但是您希望得到答案,所以我让这匹马放下脚步。感谢您的回复。
designdrumm

一个简单的问题。是否会有一个脚本在您的脚本之后被调用,而该脚本不在您的控制范围内,但仍需要此变量?如果是这样,那么从作用域中删除变量是一个坏主意。
designdrumm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.