Как сбросить переменную JavaScript?

592

У меня есть глобальная переменная в JavaScript (на самом деле это windowсвойство, но я не думаю, что это имеет значение), которое уже было заполнено предыдущим скриптом, но я не хочу, чтобы другой скрипт запускался позже, чтобы увидеть его значение или что он был даже определены.

Я поставил, some_var = undefinedи это работает с целью тестирования, typeof some_var == "undefined"но я действительно не думаю, что это правильный путь.

Что вы думаете?

Guss
источник

Ответы:

454

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.1 и 10.2.2.1 ). Верхний уровень LexicalEnvironment - это « глобальная среда»", и это связано с глобальным объектом в том смысле, что его ссылки являются свойствами глобального объекта. Поэтому, если вы попытаетесь получить доступ к имени, которое не было объявлено с использованием varключевого слова в текущей области или каких-либо внешних областях, Javascript в конечном итоге получит свойство от windowобъекта , чтобы служить этой ссылке. Как мы узнали ранее, свойства на объекты могут быть удалены.

Ноты

  1. Важно помнить, что varобъявления «подняты», т. Е. Всегда считается, что они произошли в начале области действия, в которой они находятся, - но не при инициализации значения, которое может быть выполнено в varоператоре, - который остается там, где он находится. , Таким образом, в следующем коде aэто ссылка из VariableEnvironment, а не windowсвойство, и его значение будет 10в конце кода:

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

  2. Обсуждение выше, когда «строгий режим» не включен. Правила поиска немного отличаются при использовании «строгого режима», и лексические ссылки, которые разрешались бы в свойствах окна без «строгого режима», приводят к ошибкам «необъявленная переменная» в «строгом режиме». Я не совсем понял, где это указано, но как ведут себя браузеры.

Dayong
источник
8
То, что вы сказали, является распространенным заблуждением, но на самом деле неверно - в Javascript нет «глобальных переменных». Переменные, определенные без явной области видимости (например, использование varвне функции), являются свойствами "глобального объекта", который есть в веб-браузерах window. Так что - var a = 1; delete window.a; console.log(a);удалит переменную и заставит последнюю строку выдать ошибку ссылки.
Guss
7
@Guss, твой код var a = 1; delete window.a; console.log(a);отображает 1.
Dayong
5
Я использую Google Chrome v36. Я тестировал в других браузерах. Похоже, что это не соответствует кросс-браузеров. Chrome и Opera отображали 1, в то время как Firefox, Safari и IE 11 на моем компьютере выдавали ошибку.
Dayong
3
Хорошо моя ошибка См. Ecma-international.org/ecma-262/5.1/#sec-10.5 (подпункты 2 и 8.c.ii): при запуске моего теста в консоли разработчика его обычно считают «eval context» (хотя, возможно, не в Chrome), так что это вызовет ошибку. Один и тот же код в глобальном контексте реального документа будет 1корректно выводиться во всех браузерах. Работая в реальных документах, ваши примеры кода верны. Я выбрал ваш ответ как правильный, но я был бы признателен, если бы вы могли отредактировать его, включив пояснения window.a = 1; delete window.a;и, возможно, механизм. Я тоже могу это сделать, если ты не против.
Guss
2
@KlaiderKlai да. Переменные области действия функции создаются и уничтожаются каждый раз, когда функция выполняется. Вероятно, закрытие является исключением.
Dayong
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 !== window. Я все еще не использовал бы это, чтобы удалить переменные, объявленные с, varхотя вы можете при правильных обстоятельствах.

Ной
источник
14
спасибо @jedierikb за ссылку на эту интересную статью. более конкретно к этой части < perfectionkills.com/understanding-delete/#misconceptions > этой статьи, где автор заявляет, что утверждение Ноа «предполагается, что удаление запрещено» довольно неверно, а также дает отличное объяснение, почему оно неточно , (Не стреляйте в курьера!)
Роб Уэллс
2
Что касается последнего предложения пересмотренного ответа, единственное обстоятельство, при котором вы можете удалить переменные, объявленные с, var- это когда переменная была объявлена ​​с eval.
Стивен Бухер
1
В этом случае оператор delete, похоже, ничего не делает. Что тут происходит?
Андерсон Грин
@ AndersonGreen - глобальные переменные в декальтере создаются с флагом DontDelete, поэтому их нельзя удалить. Этот код ведет себя точно так, как ожидалось.
RobG
35

Если вы неявно объявляете переменную без var, правильным способом будет использование delete foo.

Однако после его удаления, если вы попытаетесь использовать это в такой операции, как добавление, ReferenceErrorбудет брошено, потому что вы не можете добавить строку в необъявленный, неопределенный идентификатор. Пример:

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

В некоторых ситуациях может быть безопаснее присвоить ему значение false, null или undefined, чтобы оно было объявлено и не выдавало этот тип ошибки.

foo = false

Обратите внимание , что в ECMAScript null, false, undefined, 0, NaN, или ''бы все вычисляться false. Просто убедитесь, что вы не используете !==оператор, а вместо этого !=при проверке типа для логических значений, и вы не хотите, чтобы проверка идентичности (так nullбудет == falseи false == 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как и в случае с любым другим языком, если вы достаточно внимательны, вы должны прочитать:

медер омуралиев
источник
Спасибо за полный ответ со всеми деталями. Я пометил это для этого, но я принял ответ Ноя, потому что я считаю, что для простого вопроса важнее краткость, чем завершение. Еще раз - спасибо за отличную работу, которую вы проделали с этим ответом.
Guss
30
some_var = null;

//or remove it..
delete some_var;
scunliffe
источник
11
Это не работает, если область действия этого кода является функцией. Смотрите @ ноа ответ для правильного решения.
Роатин Март
1
Спасибо за ответ, но я принял ответ Ноя, потому что он лучше объясняет подводные камни delete.
Guss
3
не беспокойтесь ... Я дал простой и быстрый ответ - @noah добавил все детали для "других" дел, поэтому он тоже заслуживает похвалы. ;-)
scunliffe
7
Это не правильно. deleteработает только на недвижимость. Устанавливая это nullпеременная все еще существует.
Дерек 朕 會 功夫
1
Этот ответ достаточно хорош для наиболее вероятного случая, когда вы проверяете с помощью «if (some_var) {..}»
BearCode
16

TLDR: просто определенные переменные (без var, let, const) может быть удален с delete. Если вы используете var, let, const- они не могут быть удалены ни с 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 показывает то же самое поведение.

Serj.by
источник
Ваш ответ технически верен, поэтому вы получите точку зрения, но все, что вы написали, покрыто выбранным ответом с гораздо более подробной информацией и ссылками на спецификации 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(а также оператор удаления ). В других случаях возвращается 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
madox2
источник
4

Переменные, в отличие от простых свойств, имеют атрибут [[Configurable]] , что означает невозможность удаления переменной с помощью оператора удаления . Однако есть один контекст выполнения, на который это правило не влияет. Это eval context: там атрибут [[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
oriadam
источник
Это не всегда правильно. Особенно в Chrome. Firefox возвращает все правильно. Не тестировал ни в одном другом браузере. Что касается letvars и 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, есть отличная статья от kangax perfectionkills.com/understanding-delete, которая в основном описывает, почему и как deleteработает оператор. Но это не описание, почему буквально противоположная ситуация с функциями. Как жаль. Тем не менее, в отношении переменных вещи начинают казаться намного более ясными.
Serj.by
2

Вы не можете удалить переменную, если вы объявили ее (с помощью var x;) во время первого использования. Однако, если ваша переменная x впервые появилась в скрипте без объявления, вы можете использовать оператор delete (delete x;), и ваша переменная будет удалена, что очень похоже на удаление элемента массива или удаление свойства объекта ,

Сатьяприя Мишра
источник
1

Я немного смущен. Если все, что вам нужно, это чтобы значение переменной не передавалось в другой сценарий, тогда нет необходимости удалять переменную из области. Просто обнулите переменную, а затем явно проверьте, является ли она нулевой. Зачем переживать проблему удаления переменной из области видимости? С какой целью этот сервер обнулять не может?

foo = null;
if(foo === null) or if(foo !== null)
designdrumm
источник
Требование состоит в том, что сценарий заказа, который не находится под моим контролем, не будет видеть, что переменная существует - специально для случая OP, целевой сценарий имеет поведение для nullзначения, которое я не хочу вызывать.
Guss
Ни один «бэкэнд» не использовался во время постановки этого вопроса. Это всего лишь пара сценариев на веб-сайте, где я ничего не контролирую, кроме этого сценария.
Гусс
Оба сценария находятся в одном и том же документе или в отдельных документах, которые один вызывает другой для загрузки? Вы упомянули скрипт заказа и целевой скрипт. Если речь идет о передаче переменной в другой скрипт через переменную get / post, то я бы удалил ее в бэкэнде, прежде чем любой javascript получит его в свои руки. Примером этого в php будет что-то вроде. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
Designdrumm
Понимаю. Хорошо, если есть проверки и балансы для нуля, тогда установка его в значение, с которым целевой скрипт ничего не сделает, кажется более логичным, чем удаление переменной из области видимости, но вы надеетесь получить свой ответ, поэтому я позволю коню лежать. Спасибо за ваши ответы.
Designdrumm
Один быстрый вопрос. Будет ли когда-нибудь сценарий, который будет вызываться после вашего, который не будет под вашим контролем, но все еще будет нуждаться в этой переменной? Если это так, то удаление переменной из области является плохой идеей.
Designdrumm