Here’s the question and answer in a concise form.
js> a = '0'; b = 0; c = '';
js> [a == b, b ==c, a == c]
true,true,false
Clearly, it’s not sensible to say that the empty string ‘’ and the string ‘0’ are equal. For a start, they have different lengths.
So perhaps the question should be: Why a == b and b == c? Given JavaScript’s implicit conversion of numbers to strings and vice versa, a == b is sensible. However, it’s not clear why b == c. Perhaps there’s a good reason, but it has an unpleasant consequence.
In any case, in JavaScript it generally (perhaps always) best not to use the == operator. Fortunately it has a === operator, whose behaviour is sensible.
Here’s the question and answer.
js> words = 'Call me Ishmael.'
Call me Ishmael.
js> words.lang = 'en'
js> lang = words.lang // What is lang?
js> lang == undefined
true
JavaScript has two sorts of strings, which we can call ordinary strings and object strings. Ordinary strings have no attributes or properties. Object strings are object wrappers around ordinary strings. The String constructor creates object strings.
Object strings have some sensible behaviour:
js> typeof 'abc'
string
js> typeof new String('abc')
object
js> x = new String('abc')
abc
js> x.name = 'def'
def
js> x.name == 'def'
true
Object strings have some strange behavior:
js> typeof (x + x)
string
Ordinary strings don’t have attributes. When attribute get or set is performed on an ordinary string, the interpreter substitutes an object string in place of the ordinary string. This replacement does not affect the value of any variables.
Thus
js> x = 'abc'
js> y = x.charAt(2)
is at run time interpreted as
js> x = 'abc'
js> y = (new String(x)).charAt(2)
which creates an object string from x and calls its charAt() method. This call gives y the expected value. Notice that after the call there are no references to the new object string, and so it is recycled (garbage collected).
We can now explain why in
js> words = 'Call me Ishmael.'
Call me Ishmael.
js> words.lang = 'en'
the assignment does not stick. It is because the assignment is equivalent to
js> (new String(words)).lang = 'en'
which assigns an attribute not to words but to an anonymous object string wrapper. Object wrapping occurs for both get and set.
The answer to the supplementary question
js> lang = words.lang = 'en' // What is lang?
js> lang
en
follow because in JavaScript the value of an assignment expression is always the right-hand side of the assignment.
In Python, assignments are statements, not expressions. In Python, multiple assignment is arises from the parsing rules and not from assignment as an expression. This allow the multiple assignment statement and prohibits:
if (a = b):
pass