Як скинути змінну JavaScript?


591

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

Я поставив, some_var = undefinedі це працює для тестування, typeof some_var == "undefined"але я дійсно не думаю, що це правильний шлях для цього.

Як ти гадаєш?

Відповіді:


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) або у випадку "глобального" коду, VariableEn Environments додається до глобального об'єкта (часто window).

Посилання в VariableEnvironment зазвичай не видаляються - процес, детально описаний у ECMAScript 10.5, пояснює це детально, але достатньо сказати, що якщо ваш код не виконаний у evalконтексті (який використовує більшість консолей розробки на основі браузера), змінні, оголошені varне можуть буде видалено.

2. Без використання var

При спробі призначити значення імені без використання varключового слова, Javascript намагається знайти названу посилання в тому, що специфікація ECMAScript називає " LexicalEnvironment ", і головна відмінність полягає в тому, що LexicalEvironment s вкладені - тобто LexicalEnvironment має батьківського ( те, що специфікація ECMAScript називає "посиланням на зовнішнє середовище"), а коли Javscript не вдається знайти посилання в LexicalEen Environment , воно виглядає в батьківському LexicalEl Environment (як детально описано в 10.3.1 та 10.2.2.1 ). Найвищим рівнем LexicalEl Environment є " глобальне середовище", і це пов'язано з глобальним об'єктом тим, що його посилання є властивостями глобального об'єкта. Отже, якщо ви спробуєте отримати доступ до імені, яке не було оголошено за допомогою varключового слова в поточному обсязі або будь-яких зовнішніх областях, Javascript з часом отримає властивість від windowоб'єкта , щоб служити цим посиланням. Як ми дізналися раніше, властивості на об'єкти можуть бути видалені.

Примітки

  1. Важливо пам’ятати, що varдекларації є «піднятими» - тобто вони завжди вважаються такими, що відбулися на початку сфери, в якій вони перебувають - хоча це не ініціалізація значення, яка може бути зроблена в varзаяві, - що залишається там, де вона є . Отже, у наведеному нижче коді aє посилання з змінної навколишнього середовища, а не windowвластивість та його значення буде 10в кінці коду:

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

  2. Вищенаведене обговорення - це коли "суворий режим" не включений. Правила пошуку дещо відрізняються при використанні "суворого режиму", а лексичні посилання, які вирішили би властивості вікон без "суворого режиму", призведуть до помилок "незадекларованої змінної" в "суворому режимі". Я не дуже розумів, де це вказано, але як ведуть себе браузери.


8
Те, що ви сказали, є поширеним оманом, але насправді є невірним - у Javascript немає "глобальних змінних". Змінні, визначені без явного кола (наприклад, використання varпоза функцією), є властивостями "глобального об'єкта", який є у веб-браузерах window. Отже - var a = 1; delete window.a; console.log(a);вдало видалить змінну і призведе до того, що останній рядок видасть помилку посилання.
Гасс

7
@Guss, ваш код var a = 1; delete window.a; console.log(a);відображається 1.
Dayong

5
Я використовую Google Chrome v36. Я тестував на інших браузерах. Схоже, це не несумісні крос-браузери. Chrome і Opera відобразили 1, а Firefox, Safari та IE 11 на моєму комп’ютері видали помилку.
Дейонг

3
Гаразд, моя помилка. Див. Ecma-international.org/ecma-262/5.1/#sec-10.5 (підпункти 2 та 8.c.ii): Під час запуску мого тесту в консолі розробника, це, як правило, вважається "контекстом eval" (хоча можливо не в Chrome), тому це призведе до помилки. Один і той же код у глобальному контексті реального документа буде виводитися 1правильно у всіх браузерах. Працюючи в реальних документах, приклади коду є правильними. Я вибрав вашу відповідь як правильну, але буду вдячний, якщо ви можете відредагувати її, щоб вона включала пояснення window.a = 1; delete window.a;та, можливо, механізм. Я можу це зробити так само, якщо ви не заперечуєте.
Гасс

2
@KlaiderKlai так. Змінні, що охоплюються функціями, створюються та знищуються щоразу, коли функція виконується. Ймовірно, закриття - виняток.
Дейонг

278

@ відповідь scunlife спрацює, але технічно це повинно бути

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.

Тож будьте обережні.

EDIT: Моя відповідь дещо неточна (див. "Помилкові уявлення" наприкінці). Посилання пояснює всі деталі горі, але резюме полягає в тому, що можуть бути великі відмінності між браузерами та залежно від об'єкта, з якого ви видаляєте. delete object.somePropяк правило, повинні бути безпечними, доки object !== window. Я все одно не використовую його для видалення змінних, оголошених за допомогою, varхоча ви можете за правильних обставин.


14
дякую @jedierikb за посилання на цю цікаву статтю. точніше до цієї частини < perfectionkills.com/understanding-delete/#misconceptions > тієї статті, де автор заявляє, що заява Ноя "видалити, мабуть, не є", є досить неточним, а також чудовим поясненням, чому це неточно . (Не стріляйте в месенджера!)
Роб Уеллс

2
Що стосується останнього речення переглянутої відповіді, то єдиною обставиною, за якої можна видалити змінні, оголошені за допомогою, varє те, коли змінна була оголошена за допомогою eval.
Стівен Бухер

1
У цьому випадку оператор видалення взагалі нічого не робить. Що тут відбувається?
Андерсон Грін

@ AndersonGreen - декольовані глобальні змінні створюються за допомогою прапора DontDelete, тому не видаляються. Цей код поводиться точно так, як очікувалося.
RobG

35

Якщо ви неявно оголошуєте змінну без var, правильним способом було б користуватися delete foo.

Однак після видалення його, якщо ви спробуєте використовувати це в такій операції, як додавання a, 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як і для будь-якої мови, якщо ви достатньо дбаєте, вам слід прочитати:


Дякую за повну відповідь з усіма подробицями. Я відзначив це для цього, але прийняв відповідь Ноя, тому що вважаю, що для простого питання важливіше стислість, ніж завершення. Ще раз - дякую за велику роботу, яку ви зробили над цією відповіддю.
Гасс

30
some_var = null;

//or remove it..
delete some_var;

11
Це не працює, якщо область дії цього коду є функцією. Дивіться відповідь @ noah для правильного рішення.
Roatin Marth

1
Дякую за відповідь, але я прийняв відповідь Ноя, оскільки це краще пояснює підводні камені delete.
Гасс

3
не хвилюйтесь ... Я дав просту відповідь на "швидку n брудну" - @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 демонструє таку ж поведінку.


Ваша відповідь технічно правильна, тому ви отримуєте бал, але все, що ви написали, охоплюється вибраною відповіддю з набагато більше деталями та посиланнями на специфікації ECMAScript - надалі було б корисно переглянути наявну відповідь перед публікацією.
Guss

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

4

Змінні, на відміну від простих властивостей, мають атрибут [[Configurable]] , що означає неможливість видалення змінної через оператор видалення . Однак є один контекст виконання, на який це правило не впливає. Це контекст eval : атрибут [[Configurable]] не встановлений для змінних.



3

Крім того, що всі написали, також зауважте, що deleteповертається логічно. Він може сказати вам, чи видалення було успішним чи ні.

letТестуючи на Chrome, все, крім цього, було придатним для видалення. коли 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 повертає все правильно. Не тестували в інших браузерах. Що стосується 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)

Вимога полягає в тому, що сценарій замовлення, який не знаходиться під моїм контролем, не побачить, що змінна існує - спеціально для випадку OP цільовий скрипт має поведінку щодо nullзначення, яке я не хочу запускати.
Guss

Під час постановки цього питання жодний «запуск» не зловживав. Це всього лише кілька сценаріїв на веб-сайті, де я не маю ніякого контролю над окрім цього одного сценарію.
Guss

Чи є обидва сценарії в одному документі або в окремих документах, які один викликає інший для завантаження? Ви згадали сценарій замовлення та цільовий сценарій. Якщо це питання про те, щоб змінна була передана іншому скрипту через змінну get / post, я б видалив її на бекенді, перш ніж будь-який javascript перейде на неї. Прикладом цього в php було б щось подібне. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm

Розумію. Добре, якщо для нуля є перевірки та противаги, то встановлення його на значення цільовий скрипт нічого не зробить, здається, більш логічним, ніж видалення змінної з області, але ви маєте відповідь, тому я дозволю коня лежати. Дякуємо за ваші відповіді.
designdrumm

Одне швидке запитання. Чи коли-небудь буде сценарій, який буде викликаний після вашого, який не буде під вашим контролем, але все ж потрібна ця змінна? Якщо так, то видалення змінної з області є поганою ідеєю.
designdrumm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.