ECMAScript-regrets
ECMAScript-regrets copied to clipboard
The subtle difference between property assignment and definition
This is not exactly a regret but "how it works" or "why it is like this", trying to explain here the subtle difference between property assignment (=) and property definition (defineProperty), I hope this is not confusing (comments, corrections are welcome), apparently we need to get used to it :
var HTMLElement=function() {};
HTMLElement.prototype={
get innerHTML() {return this.html_},
set innerHTML(val) {this.html_=val},
a: 'a'
};
var DIV=new HTMLElement();
DIV.innerHTML='c';
// Assignment operator "=", called as '[[Put]' below.
// If the property (own or inherited) does exist, is not an accessor and is
// allowed to be modified, this creates a new own property 'innerHTML'
// (if the property is inherited) or modifies the existing one.
// If the property does not exist, this creates a new own property 'innerHTML'.
// If the property is an accessor, this does trigger the accessor
// (our case here).
console.log(DIV.innerHTML); //c
// OK, normal
Object.defineProperty(DIV,'innerHTML',{value:'b',writable : true,enumerable : true,configurable : true});
// Called as '[[DefineOwnProperty]]' below.
// This does define or modify a new 'innerHTML' own property that will
// be returned prior to the inherited one (if it exists, our case here).
console.log(DIV.innerHTML); //b
// OK, so what ?
// Now consider for example http://wiki.ecmascript.org/doku.php?id=strawman:object_extension_literals
// To make it short the proposal is about extending an object literal :
// DIV.{width:'100px',height:'100px'}
// Surprisingly, the proposal does allow to do :
// DIV.{width='100px',height='100px'}
// the difference is that ':' stands for [[DefineOwnProperty]]
// and '=' for [[Put]]
// OK, surprising, but so what ? (again)
// Now consider that while coding, you might do :
// DIV.{innerHTML:'my html'}
// This will not have the effect you could expect, but just create a new
// 'innerHTML' own property, therefore your div will not contain 'my html'
// Instead, you should have written :
// DIV.{innerHTML='my html'}
delete DIV.innerHTML;
// Delete the own property
console.log(DIV.innerHTML); // c
// Returns the inherited one
//Other example
console.log(DIV.a); //a
// Inherited property
DIV.a='b';
console.log(DIV.a); //b
// New own property
Object.defineProperty(DIV,'a',{value:'c',writable : true,enumerable : true,configurable : true});
// Modify the own property
console.log(DIV.a); //c
delete DIV.a;
// Delete the own property
console.log(DIV.a); //a
// Returns the inherited property
Maybe this sentence from Brendan Eich can summarize what is happening : "There really is a difference between assigning and defining in JS. Assigning runs setters, even inherited ones. Defining overrides or shadows."
Other examples can be found here : http://wiki.ecmascript.org/doku.php?id=strawman:define_properties_operator