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

我已经把some_var = undefined,它用于测试类型的some_var ==“undefined”的目的,但我真的不认为这是正确的方法。

你怎么看?


当前回答

详见诺亚的回答

//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

其他回答

与简单的属性相比,变量具有属性[[可配置的]],这意味着不可能通过删除操作符删除变量。

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

如果你在第一次使用时声明了一个变量(使用var x;),你就不能删除它。 但是,如果你的变量x第一次出现在脚本中没有声明,那么你可以使用delete操作符(delete x;),你的变量将被删除,这与删除数组的元素或删除对象的属性非常相似。

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附加到全局对象(通常是窗口)。

VariableEnvironment中的引用通常是不可删除的——ECMAScript 10.5中详细解释了这一点,但只需说明,除非您的代码在eval上下文中执行(大多数基于浏览器的开发控制台使用),否则使用var声明的变量是不能删除的。

2. 不使用var

When trying to assign a value to a name without using the var keyword, JavaScript tries to locate the named reference in what the ECMAScript spec calls "LexicalEnvironment", and the main difference is that LexicalEnvironments are nested - that is a LexicalEnvironment has a parent (what the ECMAScript spec calls "outer environment reference") and when JavaScript fails to locate the reference in a LexicalEnvironment, it looks in the parent LexicalEnvironment (as detailed in 10.3.1 and 10.2.2.1). The top level LexicalEnvironment is the "global environment", and that is bound to the global object in that its references are the global object's properties. So if you try to access a name that was not declared using a var keyword in the current scope or any outer scopes, JavaScript will eventually fetch a property of the window object to serve as that reference. As we've learned before, properties on objects can be deleted.

笔记

It is important to remember that var declarations are "hoisted" - i.e. they are always considered to have happened in the beginning of the scope that they are in - though not the value initialization that may be done in a var statement - that is left where it is. So in the following code, a is a reference from the VariableEnvironment and not the window property and its value will be 10 at the end of the code: function test() { a = 5; var a = 10; } The above discussion is when "strict mode" is not enabled. Lookup rules are a bit different when using "strict mode" and lexical references that would have resolved to window properties without "strict mode" will raise "undeclared variable" errors under "strict mode". I didn't really understand where this is specified, but its how browsers behave.

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

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

删除全局窗口对象的一个属性:

Reflect.deleteProperty(window, 'some_var');

在某些情况下,属性不能被删除(当属性不可配置时),然后该函数返回false(以及删除操作符)。在其他情况下,它返回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

注意,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
})()

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