Ответ 1
Ответ разбит на два. Первое не описывает много, но отвечает на вопрос, в то время как последний переходит в подробные детали спецификации.
TL;DR
- Первая строка работает, потому что в нестрогом режиме просто пытается удалить переменную.
- Остальные примеры в первом разделе не работают, потому что
a
не определен -
delete a.foo
работает, потому что нет причин, по которым он не должен -
delete a.bar.something
бросает, потому что сначала пытается превратитьa.bar
в объект, прежде чем пытаться получить доступ кa.bar.something
.
И теперь для чего-то совершенно другого
Во-первых, давайте поясним, что два раздела кода концептуально отличаются, поскольку первые говорят о переменной, которая не была объявлена.
Мы рассмотрим как delete
задано довольно немного.
Начнем с более понятных частей:
> delete a[0]
ReferenceError: a is not defined
> delete a.something
ReferenceError: a is not defined
> delete a.something[0]
ReferenceError: a is not defined
Все эти строки пытаются что-то сделать с a
, переменной, которая не была объявлена. Поэтому вы получаете ReferenceError
. Пока все хорошо.
> delete a
true
Это относится к 3-му предложению оператора delete
: a
- это "неразрешенная ссылка", которая представляет собой причудливый способ сказать "он не был объявлен". Spec говорит, что просто возвращает true
в этом случае.
> a = {}
{}
> delete a.foo
true
Этот интуитивный, как вы ожидаете (вероятно), но пусть погружается в него в любом случае. delete obj.property
переходит в четвертое предложение:
Возвращает результат вызова внутреннего метода
[[Delete]]
наToObject(GetBase(ref))
, предоставляяGetReferencedName(ref)
иIsStrictReference(ref)
в качестве аргументов.
Приветствую, что много неинтересных вещей. Позвольте просто игнорировать все после части [[Delete]]
и просто посмотрите как указано [[Delete]]
. Если бы я написал его в js, это будет так:
function Delete (obj, prop) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc) {
return true;
}
if (desc.configurable) {
desc.magicallyRemove(prop);
return true;
}
throw new TypeError('trying to delete a non-configurable property, eh!?');
}
В этом примере a
не имеет свойства с именем foo
, поэтому ничего особенного не происходит.
Теперь это становится интересным:
> delete a.bar.something
TypeError: Cannot convert null to object
Это происходит из-за некоторых неинтересных вещей, которые мы игнорировали ранее:
Возвращает результат вызова внутреннего метода
[[Delete]]
наToObject(GetBase(ref))
[...]
Я выделил часть, относящуюся к этому конкретному фрагменту. Прежде чем мы попробуем delete
что-нибудь, spec сообщит нам позвонить ToObject(GetBase(ref))
, где ref = a.bar.something
. Тогда сделаем это!
-
GetBase
(a.bar.something) === a.bar
-
ToObject
(a.bar)
===ToObject(undefined)
, который выдаетTypeError
Это объясняет окончательное поведение.
Заключительное примечание. Сообщение об ошибке, которое вы указали, вводит в заблуждение, так как оно говорит, что оно пыталось преобразовать null
в объект, чего не было, поскольку он пытался преобразовать undefined
в один. Последние хром и firefox бросают более точный, но я думаю, что v8 только недавно исправил сообщение об ошибке, поэтому, возможно, обновление до node v11 покажет правильную версию.