Motive behind strict mode syntax error when deleting an unqualified identifier?

Dagg Nabbit picture Dagg Nabbit · May 17, 2012 · Viewed 12.4k times · Source

I'm having trouble understanding why, in strict mode, a syntax error occurs when delete is used on an unqualified identifier.

In most cases, it makes sense... if you are declaring variables in the usual way with the var keyword, and then trying to use delete on them, in non-strict mode it would silently fail, so it makes sense for strict mode to fail with an error in those cases.

However, there are cases where you can't delete identifiers that are qualified:

(function() {

  // "use strict";

  var obj = Object.create({}, { bloop: { configurable: false } });

  delete obj.bloop; // throws TypeError in strict mode, silently fails in non-strict.

  console.log('bloop' in obj); // true

}());

Strict mode must do a runtime check here, because a TypeError is thrown when this is encountered. There are also cases where you can successfully delete unqualified identifiers in non-strict mode...

// "use strict";

window.bar = 6;

console.log(typeof bar); // number

delete bar; // works in non-strict, syntax error in strict!

console.log(typeof bar); // undefined

In fact, to my understanding, whether or not you can delete things (in non-strict mode) depends on the internal [[Configurable]] property, and has nothing to do with qualified identifiers. As far as I can tell, there is no way in strict mode to delete non-global variables that (as properties of the local VO) are configurable:

(function() {

  // "use strict";

  eval('var foo = 5;');

  console.log(typeof foo); // number

  delete foo; // works in non-strict, SyntaxError in strict.

  console.log(typeof foo); // undefined

}());

So, my question is, what's the point of throwing a SyntaxError when using delete on an unqualified identifier, when the TypeError would throw anyway if the property is not configurable? This seems like an unnecessary restriction, and in some cases there doesn't seem to be any workaround other than not using strict mode (third example). Can anyone explain the motivation behind this decision?


Update: I just realized that I was overlooking the fact that direct eval calls have their own scope in strict mode, instead of the calling function's scope, so in the third example foo would not be defined under strict mode. Anyway, the runtime check would still catch this, but it raises a side question: Is there no way to have configurable local variables in strict mode, as we do with eval'd variable declarations in non-strict? AFAIK that was one of the few legitimate uses of eval.

Answer

user123444555621 picture user123444555621 · Aug 19, 2012

You are talking about Section 11.4.1, paragraph 5.a. of the specs:

  1. Else, ref is a Reference to an Environment Record binding, so
    a. If IsStrictReference(ref) is true, throw a SyntaxError exception.
    b. Let bindings be GetBase(ref).
    c. Return the result of calling the DeleteBinding concrete method of bindings, providing GetReferencedName(ref) as the argument.

What you called "unqualified identifiers" is officially named "Environment Record binding".

Now, to your question. Why throw a SyntaxError when 5.c. would fail anyway? I think you answered it yourself!

Strict mode must do a runtime check here, because a TypeError is thrown when this is encountered.

That's right. But it's always better to fail fast. So, when there is a chance of detecting a SyntaxError (at parse time), that opportunity should be taken.

Why? It saves you the trouble of fixing your app if an error occurs. Think about IDEs that may show you the error right away, as opposed to hours of debugging.
Also, such restrictions may be advantageous for optimized JIT compilers.