14

I was raised with the "everything in JavaScript object-oriented and assignable" paradigm. So I lived my life happy, until...

    var x = {};

    x.field = true;
    x.field.netType = "System.Boolean";
    
    alert(x.field.netType);

It compiles, but the alert keeps giving me 'undefined'. Why!?

Henry Obiaraije
  • 301
  • 3
  • 10
Kees C. Bakker
  • 32,294
  • 27
  • 115
  • 203
  • 4
    Throw in `'use strict';` at the top and see the magic ;-) – thefourtheye Sep 17 '15 at 12:51
  • 1
    @thefourtheye ? what would strict mode do here? – Pointy Sep 17 '15 at 12:52
  • 2
    @Pointy Complain. It would complain. Strict mode is that picky girl who doesn't let you do the most tempting stuff, while non-strict mode is the oposite. – Ismael Miguel Sep 17 '15 at 13:55
  • @IsmaelMiguel hmm `(function() { "use strict"; (true).x = 0; })()` - I get no complaints. – Pointy Sep 17 '15 at 13:56
  • @Pointy Now that's a surprise. A cool one. I guess that assigning to coarced values is strictly fine. They are object afterall, and aren't read-only objects. – Ismael Miguel Sep 17 '15 at 13:59
  • "It compiles" no it doesn't. JavaScript is not a compiled language. – Mike G Sep 17 '15 at 14:20
  • what this does is `new Boolean(x.field).netType = "System.Boolean"` but then the Boolean object is discarded. – njzk2 Sep 17 '15 at 14:35
  • @Pointy Actually, it should barf itself when running that piece. It seems that Firefox has a bug. It has the expected behaviour on Chrome. – Ismael Miguel Sep 17 '15 at 15:02
  • @IsmaelMiguel yes, it works in Node 0.10 also. – Pointy Sep 17 '15 at 15:07
  • @Pointy All V8 engine versions that support strict mode will barf. That's a nice point to add to your answer. Firefox and IE simply run it like if nothing happened. – Ismael Miguel Sep 17 '15 at 15:10
  • @IsmaelMiguel no, the older V8 in Node 0.10 doesn't barf, whether I enable strict mode with the special string or with the command line option. But Node 0.10 is pretty old. – Pointy Sep 17 '15 at 15:12
  • @Pointy Then I should change my statement. I don't have Node.js to try it. I know that Chrome will chew itself when you try to run that piece of code, and another version of Node.js will as well. – Ismael Miguel Sep 17 '15 at 15:14
  • @IsmaelMiguel yes my fresh Chrome doesn't like it. It complains that "x" is a read-only property. – Pointy Sep 17 '15 at 15:39
  • That V8 behaviour is strange. I like it as a warning, but it shouldn't violate the spec, should it? – Bergi Sep 17 '15 at 16:23
  • Hm, this *must* be a duplicate. Anyone? – Bergi Sep 17 '15 at 16:35
  • 1
    @Bergi: Already looked and didn't find anything with a good answer. Btw, the spec says about strict mode *"The LeftHandSide also may not be a reference to a data property with the attribute value {[[Writable]]:false}, to an accessor property with the attribute value {[[Set]]:undefined}, nor to a non-existent property of an object whose [[Extensible]] internal slot has the value false. In these cases a TypeError exception is thrown (12.14)."* However, it doesn't look like `[[Extensible]]: false` is set when coercing primitives this way, so yeah, it looks like V8 is wrong? – Felix Kling Sep 17 '15 at 16:43

3 Answers3

19

Primitives (strings, numbers, true and false) in JavaScript are not objects. However, when they are used with . or [] as if they were objects, the language obliges by implicitly constructing object wrappers for them.

In your example, that's what happened. The assignment to the object property did actually work, so there was no error, but that wrapper object was then immediately thrown away.

On the other hand:

var x = {};

x.field = new Boolean(true);
x.field.netType = "System.Boolean";

alert(x.field.netType);

(I wouldn't advise actually doing that; using objects made from the primitive wrapper types tends to have weird effects as those values propagate into code that doesn't expect them.)

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    Is there any documentation about this behavior? – Kees C. Bakker Sep 17 '15 at 13:14
  • 3
    @KeesC.Bakker well there's the [language spec](http://www.ecma-international.org/ecma-262/5.1/) but that's not easy reading. I would imagine that any good JavaScript reference would have to touch on this behavior, as it's pretty fundamental to the language. Something as common as checking the length of a string will cause implicit object wrapping. – Pointy Sep 17 '15 at 13:22
  • 2
    @KeesC.Bakker: This may interest you: https://github.com/getify/You-Dont-Know-JS/blob/master/types%20&%20grammar/ch3.md#boxing-wrappers . In the spec, the boxing is defined here: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-getvalue (but that's not really valuable without context, i.e. knowing how/where `GetValue` is called). – Felix Kling Sep 17 '15 at 13:40
  • 3
    Warning: `new Boolean(false)` is a truthy value. – Keen Sep 17 '15 at 14:50
  • @Keen yes exactly, and `typeof new String("hello")` is `"object"` – Pointy Sep 17 '15 at 14:51
  • `x.field = true; x.field.netType = "System.Boolean";` is actually working. `x.field` which is a primitive boolean value is getting converted to object internally at the time of `x.field.netType = "System.Boolean"` but we dont have a reference of it so immediately it becomes garbage. If we store the refrence of `x.field` so that it doesnt be garbage we can get the value. like this....`x.field = true; var y = x.field.netType = "System.Boolean"; alert(y);` – intekhab Sep 18 '15 at 04:30
10
x.field = true; 
x.field.netType = "System.Boolean"; 

is actually working.
x.field which is a primitive boolean value is getting converted to object internally but we dont have a reference of it so immediately it becomes garbage. If we store the refrence of x.field so that it doesnt be garbage we can get the value. like this....

x.field = true;  
var y = x.field.netType = "System.Boolean"; 
alert(y); 

If you write you code like this

var x = {};

x.field = {};
x.field.netType = "System.Boolean";

alert(x.field.netType);

Then it will work.

In your code this line x.field.netType = "System.Boolean"; will throw error in `strict mode

`//Cannot assign to read only property 'netType' of true`

Why this line x.field.netType gives undefined

Objects of this type are merely wrappers, their value is the primitive they wrap and they will generally coerce down to this value as required.

JavaScript will readily coerce between primitives and objects.

var a = 'Intekhab';
 a.length;//In this case the string value is coerced to a string object in order to access the property length.

var Twelve = new Number(12); 
var fifteen = Twelve + 3; // In this case Object Twelve is coerced to a primitive value.
fifteen; //15

If JavaScript detects an attempt to assign a property to a primitive it will indeed coerce the primitive to an object. This new object has no references and will immediately become fodder for garbage collection.

var primitive = "september";
primitive.vowels = 3;
//new object created to set property 
(new String("september")).vowels = 3;



primitive.vowels;
//another new object created to retrieve property 
(new String("september")).vowels; //undefined
intekhab
  • 1,566
  • 1
  • 13
  • 19
  • 2
    This is only half the answer … it explains why you get `undefined`. but it does not explain why the assignment seems to work in first place. Apart from that, the question was not how to fix this, but what's the reason for this… the answer by @Pointy is *way* better. – Golo Roden Sep 17 '15 at 12:50
  • 1
    This is not clear enough and "wrong" in some way because doing var t = new Boolean(); t.foo = "bar"; is actually working. It can mislead OP. @Pointy answer is more accurate atm. – Stranded Kid Sep 17 '15 at 12:54
  • Do you have any documentation to support your answer? – Kees C. Bakker Sep 17 '15 at 13:13
5

x.field is a boolean value; boolean values are primitives and are read-only. When you attempt to assign a value to x.field.netType, you are attempting to modify the value of x.field. Pointy's answer mostly explains this.

When in 'normal' JavaScript mode, this results in the value simply being undefined.

The reason why someone in the comments suggested strict mode (which you should definitely be using) is that strict mode will throw an error informing you that you are attempting to assign a value to a read-only value and prevent you from doing that instead of silently returning undefined whenever you access that property.

'use strict';
var foo = true;
foo.bar = 'qux'; // this line will throw an Error

As per Pointy's example, it does actually error, at the very least on Node 4.0. I have no idea what JS engine Pointy is using but it is not operating correctly.

> (function() { 'use strict'; (true).x = 0; })()
TypeError: Cannot assign to read only property 'x' of true
    at repl:1:38
    at repl:1:45
    at REPLServer.defaultEval (repl.js:154:27)
    at bound (domain.js:254:14)
    at REPLServer.runBound [as eval] (domain.js:267:12)
    at REPLServer.<anonymous> (repl.js:308:12)
    at emitOne (events.js:77:13)
    at REPLServer.emit (events.js:169:7)
    at REPLServer.Interface._onLine (readline.js:209:10)
    at REPLServer.Interface._line (readline.js:548:8)
Dan Pantry
  • 51
  • 2