【问题标题】:How can I unset a JavaScript variable?如何取消设置 JavaScript 变量?
【发布时间】:2010-12-08 11:33:16
【问题描述】:

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

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

你怎么看?

【问题讨论】:

    标签: javascript global-variables undefined variable-declaration unset


    【解决方案1】:

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

    注意事项

    1. 重要的是要记住var 声明是“提升的”——即它们总是被认为发生在它们所在作用域的开头——尽管不是可以在@987654345 中完成的值初始化@ 声明 - 留在原处。所以在下面的代码中,a 是来自 VariableEnvironment 的引用,而不是 window 属性,它的值将是代码末尾的 10

      function test() { a = 5; var a = 10; }
      
    2. 以上讨论是在“严格模式”未启用的情况下进行的。使用“严格模式”时查找规则有点不同,在没有“严格模式”的情况下解析为窗口属性的词法引用将在“严格模式”下引发“未声明的变量”错误。我真的不明白这是在哪里指定的,但它是浏览器的行为方式。

    【讨论】:

    • 你所说的是一个常见的误解,但实际上是不正确的——在 Javascript 中没有“全局变量”。没有明确范围定义的变量(例如在函数外使用var)是“全局对象”的属性,在Web 浏览器中为window。所以 - var a = 1; delete window.a; console.log(a); 将成功删除变量并导致最后一行发出引用错误。
    • @Guss,您的代码 var a = 1; delete window.a; console.log(a); 显示为 1。
    • 我使用的是 Google Chrome 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 是的。每次执行函数时,都会创建和销毁函数作用域变量。可能关闭是一个例外。
    【解决方案2】:

    scunliffe's answer 可以,但从技术上讲应该可以

    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
    }());
    

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

    当涉及原型链时,使用 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(见最后的“误解”)。该链接解释了所有血淋淋的细节,但总结是浏览器之间可能存在很大差异,具体取决于您要从中删除的对象。只要object !== windowdelete object.someProp 通常应该是安全的。我仍然不会用它来删除用var 声明的变量,尽管你可以在适当的情况下。

    【讨论】:

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

    如果您在没有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 与任何语言都有一些奇怪的例外,如果你足够在意,你应该阅读:

    【讨论】:

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

    详情请见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;
    

    参考资料:

    【讨论】:

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

    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 案例也很有趣。不过,感谢您的注意。下次会尝试更具体。
    【解决方案6】:

    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
    

    【讨论】:

      【解决方案7】:

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

      TL;DR

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

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

      示例:

      x = null;
      

      ?


      更多信息:

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

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

      • 垃圾收集是自动执行的。 我们无法强制或阻止。
      • 对象在reachable 时保留在内存中。
      • 引用可访问不同:一组相互关联的对象可能整体变得不可访问。 (source)

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

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

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

       


       

      ...唉,唯一的解决办法:

      Freeing the contents of a variable

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

      var x;
      
      // ...
      
      x = null;    // (x can now be garbage collected)
      

      (source)


      延伸阅读:

      【讨论】:

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

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

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

      【讨论】:

        【解决方案9】:

        注意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 好点,我把问题编辑得更清楚了。
        【解决方案10】:

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

        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。)
        【解决方案11】:

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

        【讨论】:

        • 我认为这里的重要区别(当我第一次问这个问题时我错过了)是非“声明”变量不是变量 - 它是一个对象属性。 delete 用于使对象忘记定义的属性,它不会影响变量。
        【解决方案12】:

        我有点困惑。如果您只想让变量值不传递给另一个脚本,则无需从范围中删除该变量。

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

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

        【讨论】:

        • 要求是不受我控制的订单脚本不会看到变量存在 - 特别是对于 OP 情况,目标脚本具有 null 值的行为我不想触发。
        • 在这个问题的产生过程中没有滥用“后端”。这些只是网站上的几个脚本,除了这个脚本,我无法控制任何东西。
        • 两个脚本是在同一个文档中还是在一个调用另一个加载的单独文档中?您提到了订单脚本和目标脚本。如果是通过 get/post 变量将变量传递给另一个脚本的问题,那么我会在任何 javascript 使用它之前在后端删除它。这在 php 中的一个例子是这样的。 &lt;?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?&gt;
        • 我明白了。好吧,如果有对 null 的检查和平衡,那么将其设置为目标脚本将不执行任何操作的值似乎比从范围中删除变量更合乎逻辑,但是您希望得到答案,所以我会让马躺下。感谢您的回复。
        • 一个快速的问题。是否会有一个脚本在你的脚本之后被调用,它不受你的控制但仍然需要这个变量?如果是这样,那么从范围中删除变量是个坏主意。
        猜你喜欢
        • 2017-02-07
        • 2016-07-10
        • 2012-01-27
        • 2011-08-10
        • 1970-01-01
        • 2012-01-31
        • 2011-01-03
        • 2015-09-26
        • 2015-08-22
        相关资源
        最近更新 更多