Почему ключевое слово delete действует против ожидаемого?

В Chrome попробуйте выполнить следующие действия в консоли. Первая

console = 0;

чтобы присвоить значение 0 console. Тогда

console // (prints `0`)

чтобы проверить, что мы правильно перезаписали console. Наконец,

delete console

Удивительно, что console теперь содержит исходный объект console. По сути, ключевое слово delete "resurected" console, вместо того, чтобы истребить его!

Является ли это ожидаемым поведением? Где это реализовано в коде Chromium?

Ответ 1

Как указано в документация MDN на delete:

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

Ваш delete просто отбрасывает собственное свойство, унаследованное цепочкой прототипов.

В некоторых браузерах window наследуется от собственного прототипа, и вы будете проверять источники, чтобы узнать, как свойство наследуется, если вы действительно хотите знать, что много деталей, но в основном они работают так же, как и JS.

Ответ 2

Получил это:

Мне удалось доказать, что консоль является свойством глобального объекта: просто откройте консоль и введите: this.parent или window.parent. Это покажет более полный список свойств и методов в вашем распоряжении. Включая console: Console, около 2/3 пути вниз, чуть ниже chrome: Object (интересно...:)). Я подумал об этом, когда вспомнил, что мне как-то удалось изменить правила CSS самой консоли (в хроме, не спрашивайте меня, как я туда попал, я не помню).
Нижняя строка: console - свойство объекта окна. Я думаю, что это хорошо подтверждает мои объяснения.


@Randomblue: поскольку вы заинтересованы в том, как это реализовано в v8, вы можете здесь проверить тулбар или просмотреть кровотечение. Где-то вы найдете тестовый каталог, в котором есть несколько файлов, имеющих дело с delete. Особое внимание уделяется delete, используемому для глобальных переменных/свойств: их нельзя удалить, другими словами: консоль никогда не исчезает. Я хотел бы знать, почему этот ответ исходил из того, что он был признан полезным и принят к не-полезному и не принятому, хотя...


Это совершенно просто. console не является случайным, автономным объектом. Это на самом деле свойство глобального объекта. Откройте консоль и введите this.console === console или window.console === console. Конечно, это верно.

Таким образом, благодаря подразумеваемым глобальным переменным console = 0 в значительной степени совпадает с window.console = 0. Вы как бы переназначаете свойство экземпляра. Разница с обычными объектами заключается в том, что глобальный объект - это не просто старый объект: его свойства нельзя удалить (где-то здесь, в MDN). Таким образом, ваш глобальный объект маскирует объект консоли, который все еще существует, вы просто потеряли свою ссылку:

var bar = window.console;
console = 12;
bar.log(console);//logs 12, bar is now an alternative reference to the console object
delete console;//unmasks the console reference
console === bar;//true

Не следует на мгновение обманывать мысль о том, что глобальный объект не имеет прототипа. Просто введите this.constructor.name и lo and behold: Window с капиталом W. Другой способ двойной проверки: Object.getPrototypeOf(this); или Object.getPrototypeOf(window);. Другими словами, есть прототипы для рассмотрения. Как всегда, цепь заканчивается на Object.prototype:

 Object.getPrototypeOf(Object.getPrototypeOf(window));

Короче говоря, здесь нет ничего странного, кроме странной природы самого глобального объекта. Он ведет себя так, как будто происходит некоторая форма прототипного наследования. Посмотрите на глобальный объект, как если бы он был настроен следующим образом:

this.prototype.window = this;//<-- window is a circular reference, global obj has no name
this.prototype.console = new Console();//this is the global object
this.hasOwnProperty(console);//false
console = 0;//implied global

При попытке доступа к console JS находит свойство console, которое вы только что установили перед экземпляром объекта console, и с радостью возвращает его значение. То же самое происходит, когда мы удаляем его, первое обнаружение console удаляется, но свойство выше цепочки прототипов остается неизменным. В следующий раз, когда запрашивается console, JS сканирует цепочку наследования и возвращает старый экземпляр консоли. Консольный объект никогда не исчезал, он просто скрывался за собственностью, которую вы сами установили.

Отключить тему, но для полноты:
Есть еще несколько вещей, кроме этого (сканирование области до поиска объектов/прототипов), из-за особого характера глобального объекта, но это AFAIK, суть этого.
Что вам нужно знать, что такой вещи нет (в JS) как объект без (по крайней мере) 1 прототипа. Это включает в себя глобальный объект. То, что вы делаете, просто дополняет текущий экземпляр глобального объекта, удаляет свойство, и прототип снова захватывает. Просто как тот. То, что @Peeter намекнул на его ответ: подразумеваемые глобальные переменные не допускаются в строгом режиме, потому что они изменяют глобальный объект. Который, как я пытался объяснить здесь, - это именно то, что здесь происходит.

Ответ 3

Некоторые свойства объекта окна не удаляются. True возвращается, потому что вы не работаете в строгом режиме. Попробуйте следующее (не в консоли):

"use strict";
delete console;

и вы получите исключение (JSFiddle).

Вы можете узнать больше о том, как это обрабатывается в http://es5.github.com/#x11.4.1

Ответ 4

Во-первых, это не просто консоль, вы можете сделать это с помощью каждого собственного свойства каждого свойства, определенного браузером, на window.

setTimeout = 0;
setTimeout //=> 0
delete window.setTimeout;
setTimeout //=> function setTimeout() { [native code] }

Свойства, которые являются частью ECMA- Script Spec, могут быть полностью перезаписаны и удалены:

Array = 0;
Array //=> 0
delete window.Array;
Array //=> ReferenceError

Вы можете почти перезаписать любое свойство на window, удалить перезапись и вернуться к нормальной функции.

Простая причина для этого заключается в том, что консоль и все остальные встроенные глобальные функции, определенные браузером, не связаны с объектом DOMWindow через javascript, а через С++. Вы можете видеть, что консоль подключена к DOMWindow прямо здесь и реализация DOMWindow здесь

Это также означает, что оконный объект каким-то образом является объектом С++, замаскированным как объект javascript. Объект окна, по крайней мере, частично определяется С++, и это не прототипическое наследование делает магию: Take for пример:

window.hasOwnProperty('console') //=> true, console is defined directly on the window
window.__proto__.hasOwnProperty('console') // => false, the window prototype does not have a console property

Кроме того, если это было прототипическое наследование, следующее приведет к возврату консоли 3:

window.__proto__.console = 3;
delete console;
console //=> still returns console;
window.hasOwnProperty('console') //=> the window still has it.

То же самое с свойством, относящимся к прототипическому наследованию:

window.someProp = 4;
window.__proto__.someProp = 6;
someProp //=> 4
delete someProp;
someProp //=> 6

Поэтому, когда вы устанавливаете console на что-либо, оно исчезает и может быть воскрешено только (hoorray для иронии): delete console.

Итак, что это значит, вы не можете удалить какие-либо собственные свойства в объекте window. Попробуйте delete window.console, когда он не будет перезаписан, он снова появится. Тот факт, что вы можете перенести его в первую очередь (даже в строгом режиме), не получив какого-либо предупреждения (на мой взгляд) одной из ключевых уязвимостей javascript (установите setTimeout почти на любую страницу на 0 и увидите, что он разрывает сам по себе), но, как говорится, у человека-паука:

С большой силой приходит большая ответственность

Update

Чтобы включить намек на то, что это специфично для реализации браузера/движка, а не для какого-либо требования самого языка: в nodejs удаляются свойства, указанные как для ядра, так и для свойств ecma-w370 > для глобального объекта:

delete this.console //=> true
console //=> ReferenceError

delete parseInt //=> true
parseInt //=> ReferenceError

Ответ 5

Оператор delete удаляет свойство из объекта.

...

Вы можете использовать оператор delete для удаления объявленных переменных неявно, но не объявленные с помощью var или функции утверждение.

См. delete в MDN

Edit:

См. также Понимание удаления, если вы действительно используете хардкорный JavaScript.

Ответ 6

То же самое происходит в Firefox.

Я предполагаю следующее, основанное на собственных наблюдениях.

Сначала проверяются переменные, чтобы увидеть, соответствуют ли они локальным переменным, а если нет, тогда будет проверяться, соответствуют ли они window.variable.

Когда вы устанавливаете console в 1, вы устанавливаете локальную переменную console равным 1, поэтому в любом поиске будет показано, что вместо window.console (который все еще существует). Когда вы delete console, локальная переменная console будет удалена. Теперь любой поиск console будет соответствовать window.console. Вот почему вы получаете свое поведение.

Я предполагаю, что это основано на эксперименте с интерпретатором JavaScript в Firefox. И, я сожалею о неправильной терминологии (не стесняйтесь редактировать), я не испытываю проблем с пространствами имен.

Ответ 7

Ожидаемое поведение. Малоизвестный факт, что консоль Javascript не запускается в глобальном пространстве браузера, а работает внутри своей анонимной функции.

Я знаю, что разные браузеры обрабатывают вещи по-разному, но вкратце - удаление не работает так, как ожидалось, потому что оно не работает в глобальном пространстве.

Если вы действительно хотите, чтобы все было нарушено, попробуйте сыграть с delete window.console

Хорошо, это официально - я идиот. Одной из новых особенностей ECMAScript является способность объявить свойство как dontdelete. Прошу прощения за эту путаницу.

Ответ 8

Что происходит, так это переписывание прототипа объектов, тогда вы удаляете перезаписанное значение, а оставшееся... - это исходный объект, который является прототипом.