ChatGPT解决这个技术问题 Extra ChatGPT

如何取消设置 JavaScript 变量?

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

我放了 some_var = undefined,它用于测试 typeof some_var == "undefined",但我真的认为这不是正确的方法。

你怎么看?


P
Peter Mortensen

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”中定位命名引用,主要区别在于 LexicalEnvironment s 是嵌套的——即 LexicalEnvironment 有一个父级(ECMAScript 规范称之为“外部环境引用”),当 JavaScript 无法在 LexicalEnvironment 中找到引用时,它在父 LexicalEnvironment 中查找(详见 10.3.110.2.2.1)。顶层 LexicalEnvironment 是“global environment”,它绑定到全局对象,因为它的引用是全局对象的属性。因此,如果您尝试访问未在当前范围或任何外部范围内使用 var 关键字声明的名称,JavaScript 最终将获取 window 对象的属性以用作该引用。正如我们之前所了解的,对象的属性可以被删除。

笔记

重要的是要记住 var 声明是“提升的”——即它们总是被认为发生在它们所在作用域的开头——尽管不是可能在 var 语句中完成的值初始化——即留在这是。因此,在下面的代码中,a 是来自 VariableEnvironment 而不是 window 属性的引用,它的值在代码末尾将为 10: function test() { a = 5;变量 a = 10;上面的讨论是当“严格模式”没有启用时。使用“严格模式”时查找规则有点不同,在没有“严格模式”的情况下解析为窗口属性的词法引用将在“严格模式”下引发“未声明的变量”错误。我真的不明白这是在哪里指定的,但它是浏览器的行为方式。


你所说的是一个常见的误解,但实际上是不正确的——在 Javascript 中没有“全局变量”。没有明确范围定义的变量(例如在函数外使用 var)是“全局对象”的属性,在 Web 浏览器中是 window。所以 - var a = 1; delete window.a; console.log(a); 将成功删除变量并导致最后一行发出引用错误。
@Guss,您的代码 var a = 1; delete window.a; console.log(a); 显示 1。
我正在使用谷歌浏览器 v36。我在其他浏览器上测试过。看起来它不是一致的跨浏览器。 Chrome 和 Opera 显示 1,而我电脑上的 Firefox、Safari 和 IE 11 出现错误。
好吧,我的错。请参阅 ecma-international.org/ecma-262/5.1/#sec-10.5(子点 2 和 8.c.ii):在开发者控制台中运行我的测试时,它通常被认为是“评估上下文”(尽管在 Chrome 中可能不是),因此它会引发错误。真实文档的全局上下文中的相同代码将在所有浏览器中正确输出 1。在真实文档中运行,您的代码示例是正确的。我选择您的答案是正确的,但如果您可以编辑它以包括解释 window.a = 1; delete window.a; 和可能的机制,我将不胜感激。如果你不介意,我也可以这样做。
@KlaiderKlai 是的。每次执行函数时,都会创建和销毁函数作用域变量。可能关闭是一个例外。
P
Peter Mortensen

scunliffe's answer 会起作用,但从技术上讲,它应该是

delete window.some_var;

当目标不是对象属性时,删除应该是空操作。例如,

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

但是由于全局变量实际上是窗口对象的成员,所以它可以工作。

当涉及原型链时,使用 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.

所以要小心。

注意:我的答案是somewhat inaccurate(见最后的“误解”)。该链接解释了所有血淋淋的细节,但总结是浏览器之间可能存在很大差异,具体取决于您要从中删除的对象。 delete object.someProp 通常应该与 object !== window 一样安全。我仍然不会使用它来删除用 var 声明的变量,尽管您可以在适当的情况下。


感谢@jedierikb 提供指向那篇有趣文章的链接。更具体地说,这部分 <perfectionkills.com/understanding-delete/#misconceptions>在那篇文章中,作者指出诺亚的陈述“删除应该是一个无操作”是相当不准确的,并且很好地解释了为什么它是不准确的。 (不要射击信使!)
关于修改后的答案的最后一句,您可以删除使用 var 声明的变量的唯一情况是使用 eval 声明的变量。
this case 中,delete 语句似乎根本没有做任何事情。这里发生了什么?
@AndersonGreen—贴花的全局变量是使用 DontDelete 标志创建的,因此不可删除。该代码的行为完全符合预期。
此答案已过时delete 的这种用法已弃用。 (参见 herethis answer。)
p
pimvdb

如果您在没有 var 的情况下隐式声明变量,则正确的方法是使用 delete foo

但是,在您删除它之后,如果您尝试在诸如添加之类的操作中使用它,则会抛出 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 也有一些奇怪的例外,如果您足够在意,您应该阅读:

https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/delete_Operator

http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf


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

请参阅 noah's answer 了解完整详情

//Option A.) set to null
some_var = null;

//Option B.) set to undefined
some_var = undefined;

//Option C.) remove/delete the variable reference
delete obj.some_var
//if your variable was defined as a global, you'll need to
//qualify the reference with 'window'
delete window.some_var;

参考:

MDN 删除 API

在严格模式下删除不合格的变量名时出现 MDN SyntaxError


如果此代码的范围是函数,则此方法不起作用。请参阅@noah 的答案以获取正确的解决方案。
感谢您的回答,但我接受了 Noah 的回答,因为它更好地解释了 delete 的缺陷。
不用担心......我给出了一个“快速而肮脏”的简单答案 - @noah 为“其他”案例添加了所有细节,因此他也值得称赞。 ;-)
这是不正确的。 delete 仅适用于属性。设置它 null 变量仍然存在。
对于您使用“if (some_var) { .. }”进行检查的最有可能的情况,这个答案已经足够好了
P
Peter Mortensen

TLDR:可以使用 delete 删除简单定义的变量(没有 varletconst)。如果您使用 varletconst - 它们既不能用 delete 也不能用 Reflect.deleteProperty 删除。

铬 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"

Firefox Nightly 53.0a1 显示相同的行为。


您的答案在技术上是正确的,因此您明白了,但是您写的所有内容都包含在所选答案中,其中包含更多详细信息和对 ECMAScript 规范的引用 - 将来在发布之前查看现有答案会很有用。
同意。但这里只提到了 var 的情况。对我来说,测试和分享 letconst 案例也很有趣。不过,感谢您的注意。下次会尝试更具体。
P
Peter Mortensen

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 operator)。在其他情况下,它返回 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

a
ashleedawg

⚠️ 接受的答案(和其他)已经过时了!

TL;博士

delete 不会删除变量。 (仅用于从对象中删除属性。)

“取消设置”的正确方法是简单地将变量设置为空。 (来源)(这使 JavaScript 的自动进程能够从内存中删除变量。)

例子:

x = null;

😉

更多信息:

自 2012 年所有浏览器都实施(自动)mark-and-sweep garbage-collection 以来,已弃用对变量使用 delete 运算符。该过程通过自动确定对象/变量何时变为“unreachable”(确定代码是否仍需要它们)来工作。

使用 JavaScript,在所有现代浏览器中:

垃圾收集是自动执行的。我们不能强迫或阻止它。对象在可访问时保留在内存中。被引用与可访问不同:一组相互关联的对象可能会整体变得不可访问。 (资源)

delete 运算符仅用于从对象中删除属性不会删除变量。

与人们普遍认为的不同(可能是由于其他编程语言,如 C++ 中的 delete),delete 运算符与直接释放内存无关。内存管理是通过中断引用间接完成的。 (资源)

使用 strict mode'use strict';,与常规/"sloppy mode" 相对)时,尝试删除变量将引发错误并且不允许。 JavaScript 中的普通变量无法使用 delete 运算符 (source)(或任何其他方式,截至 2021 年)删除。

...唉,唯一的解决方案:

释放变量的内容

要释放变量的内容,只需将其设置为 null:

变量 x; // ... x = null; // (x 现在可以被垃圾回收)

(source)

延伸阅读:

内存管理(MDN 文档)

垃圾收集(厄勒布鲁大学)

垃圾收集的基础知识 (javascript.info)

了解使用垃圾收集的 JavaScript 内存管理

消除 Javascript 中的内存泄漏


嗯...实际上,null 是一个有效值 - 如果将变量设置为 null 会导致它消失(即导致将来的引用抛出 ReferenceError: x is not defined),那么很多软件都会崩溃。如果您会说将其设置为 undefined,我们可能会有一些事情要讨论。
此外,最初的问题是关于“全局变量”,正如接受的答案正确描述的那样,它要么是一个范围变量,在这种情况下它不能被删除(不,将它设置为 null 不会删除它) ,或者它是全局对象的一个属性,在这种情况下 delete 将删除它就好了。
再次阅读所有内容后,我认为我对您的回答的问题是,它主要是关于 Javascript VM 如何管理内存,并且它处理(可能非常好)人们可能希望如何释放存储在 Javascript 中的值所消耗的内存。不幸的是,OP 本身并不是关于内存管理的,而是更多关于导致其他 Javascript 代码看不到已定义变量(实际上是全局对象属性)。它不是关于实际内存使用,而是更多关于对象元数据。
如果您将其设置为未定义,您的答案将是有意义的。如果您将其设置为 null 它会将值保留在 JS 的内存中
o
oriadam

请注意,delete 成功时会返回 true

2021 年更新:在 Chrome 88 和 Firefox 84 上测试:

implicit_global = 1;
delete implicit_global; // true

window.explicit_global = 1;
delete explicit_global; // true

const _object = {property: 1};
delete _object.property; // true

function_set = function() {};
delete function_set; // true

function function_declaration() {};
delete function_declaration; // false

(function () {
    var _var = 1;
    console.log(delete _var); // false
    console.log(_var); // 1
})()

(function () {
    let _let = 1;
    console.log(delete _let); // false
    console.log(_let); // 1
})()

(function () {
    const _const = 1;
    console.log(delete _const); // false
    console.log(_const); // 1
})()

由于浏览器更新,此答案的先前编辑不再相关。


这并不总是正确的。尤其是在 Chrome 中。 Firefox 正确返回所有内容。没有在任何其他浏览器中测试。至于 let vars 和 const vars 它返回 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"
正如下面提到的 jedierikb,kangax perfectionkills.com/understanding-delete 有一篇完美的文章,主要描述了 delete 运算符的工作原理和方式。但这并没有描述为什么与功能相反的情况。可惜。然而,关于变量,事情开始变得更加清晰。
原始答案于 2015 年在 Chrome 52 上进行了测试。现在 Chrome 88 上的行为有所不同,所以我更新了答案。也在 Firefox 84 上进行了测试 - 行为与 Chrome 88 相同。
什么是“原始答案”?你自己的答案? Dayong's answer?还是其他答案?
@PeterMortensen 好点,我将问题编辑得更清楚。
P
Peter Mortensen

与简单属性相比,变量具有属性 [[Configurable]],这意味着无法通过删除运算符删除变量。

但是,有一个执行上下文不受此规则影响。它是 eval 上下文:没有为变量设置 [[Configurable]] 属性。


S
Swarnendu Paul

删除操作符从对象中删除一个属性。

delete object.property
delete object['property']

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete

根据问题,您需要以下之一

delete some_var;
delete window.some_var;
delete window['some_var'];

delete 实际 所做的部分是正确的,但建议的解决方案不是正确的方法。 (参见 herethis answer。)
G
Guss

如果您在首次使用时声明了变量(使用 var x;),则无法删除该变量。但是,如果您的变量 x 首次出现在脚本中没有声明,那么您可以使用 delete 运算符 (delete x;) 并且您的变量将被删除,非常类似于删除数组的元素或删除对象的属性。


我认为这里的重要区别(当我第一次问这个问题时我错过了)是一个非“声明”的变量,不是一个变量——它是一个对象属性。 delete 用于使对象忘记正在定义的属性,它不会影响变量。
P
Peter Mortensen

我有点困惑。如果您只想让变量值不传递给另一个脚本,那么就没有必要从范围中删除该变量。

只需使变量无效,然后显式检查它是否为空。为什么要麻烦从范围中删除变量?这是为了什么目的而无效化不能呢?

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']); ?>
我懂了。好吧,如果有对 null 的检查和平衡,那么将其设置为目标脚本将不执行任何操作的值似乎比从范围中删除变量更合乎逻辑,但是您希望得到答案,所以我会让马躺下。感谢您的回复。
一个快速的问题。是否会有一个脚本在你的脚本之后被调用,它不受你的控制但仍然需要这个变量?如果是这样,那么从范围中删除变量是一个坏主意。