I just observed the following weird behavior:
> delete a
true
> 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 = {}
{}
> delete a.foo
true
> delete a.bar.something
TypeError: Cannot convert null to object
> a.bar
undefined
I have two questions:
delete a
works while a
is not defined?a.bar.something
throw the error Cannot convert null to object
instead of Cannot read property 'something' of undefined
(because a.bar
is undefined
)?According to documentation The delete
operator removes a property from an object., so the answer for the first question would be that a
is supposed to be a property of this
object?
When using delete a;
in c++ app, this error appears (and it's supposed to do) error: ‘a’ was not declared in this scope
.
The answer is split into two. The first doesn't describe much but answer the question, while the latter goes into the nitty gritty details of the specification.
a
isn't defineddelete a.foo
works because there's no reason it shouldn'tdelete a.bar.something
throws because it's first trying to turn a.bar
into an object before trying to access a.bar.something
.First, let's make clear that the two sections of code are conceptually different since the first talks about a variable which was not declared.
We'll be looking at how delete
is specified quite a bit.
Let's start with the easier to understand parts:
> 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
All these lines are trying to do something with a
, a variable which was not declared. Therefore, you get a ReferenceError
. So far so good.
> delete a
true
This goes into the 3rd clause of the delete
statement: a
is an "unresolved reference", which is a fancy way of saying "it wasn't declared". Spec says to simply return true
in that case.
> a = {}
{}
> delete a.foo
true
This one's intuitive as you expect (probably), but let's dive into it anyway. delete obj.property
goes into the 4th clause:
Return the result of calling the
[[Delete]]
internal method onToObject(GetBase(ref))
providingGetReferencedName(ref)
andIsStrictReference(ref)
as the arguments.
Welp that's a whole lot of uninteresting stuff. Let's just ignore everything after the [[Delete]]
part, and just look at how [[Delete]]
is specified. If I were to write it in js, it's be like this:
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!?');
}
In the example, a
does not have a property named foo
, so nothing special happens.
Now it gets interesting:
> delete a.bar.something
TypeError: Cannot convert null to object
This happens because of some of the uninteresting things we ignored earlier:
Return the result of calling the
[[Delete]]
internal method onToObject(GetBase(ref))
[...]
I highlighted the portion relevant to this specific snippet. Before we try to delete
anything, spec tells us to call ToObject(GetBase(ref))
, where ref = a.bar.something
. Let's do that, then!
GetBase
(a.bar.something) === a.bar
ToObject
(a.bar)
=== ToObject(undefined)
which throws a TypeError
Which explains the final behaviour.
Final note: The error message you've shown is misleading, since it says it tried to convert null
into an object, which it didn't, as it tried to convert undefined
into one. Latest chrome and firefox throw a more accurate one, but I think v8 only recently fixed the error message, so perhaps upgrading to node v11 will show a correct version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With