Commander McLane wrote:But we still can compare properties of objects to whatever we want, using "==" and "!=", like in the small example above.
No, not really.
To fully understand this, you need to distinguish between
values and
references. A value is, er, a thing, and a reference is a name for a thing. There are essentially two types of reference in JavaScript: variables and properties. There are also two fundamental types of values: primitives and objects. Any reference can refer to a primitive or an object; in particular, properties often refer to other objects.
The primitives are numbers, strings,
true
,
false
,
null
and
undefined
. Everything else is an object. (Just to confuse matters, there are
Number
,
String
and
Boolean
objects as well as primitive types, but let’s pretend we didn’t hear that.)
One of the special attributes of primitives is that there is conceptually only one primitive of a given type with a given value. For instance, there is only one
true
and one
false
, so
===
comparisons make sense for booleans:
Code: Select all
var a = false;
var b = (0 == 1);
a === b; // True
Similarly, there is only one number primitive with the value 5:
Code: Select all
var a = 5;
var b = 2 + 3;
a === b; // True
Another special property of primitives, which goes hand in hand with this, is that they are
immutable. Attempts to set a property of a primitive are ignored:
Code: Select all
true.flavour = "strawberry";
true.flavour; // Warning: reference to undefined property true.flavour
(Offhand, I believe this is an error in ECMAScript 5 strict mode.)
The other type of value is objects. An object is a collection of properties, and by default is mutable (i.e. its properties can be changed). Two objects with identical sets of properties and values are still two distinct objects, and changing one does not affect the other:
Code: Select all
var a = new Object;
var b = new Object;
a == b; // False; the objects are structurally identical, but == doesn’t test this.
a === b; // False
var c = a;
a == c; // True
a === c; // True
a.flavour = "banana"; // The objects are no longer structurally identical.
a.flavour; // "banana"
b.flavour; // undefined
c.flavour; // "banana" — a and c refer to the same object.
Now, if life were simple we’d be able to say that an object never compares equal to anything else, but it isn’t and we can’t. Remember those objects corresponding to primitives I tried to sweep under the carpet above? The Powers That Be decided to try to hide their existence by allowing objects to be compared to numbers and strings. In particular, if you try to compare an object to a number or string primitive, the JS engine will try to call the object’s
valueOf()
method, then its
toString()
method, to get a primitive value. (In ECMAScript 5th Edition, which I expect the next release to conform to,
toString()
is called first when comparing to a string.) Example:
Code: Select all
var strPrimitive = "foo";
var strObjA = new String(strPrimitive);
var strObjB = new String(strPrimitive);
var customObject = { toString: function() { return "foo"; } }
// === is false for all combinations.
strPrimitive == strObjA; // True
strPrimitive == strObjB; // True
strObjA == strObjB; // False!
customObject == strPrimitive; // True!
The old version of SpiderMonkey implements this using a hook that
host objects (i.e., objects defined by the program running the JavaScript engine, in this case Oolite) can override. The new version does not.
So to return to an earlier question:
system.ID == 7
is OK, because 7 is a number primitive, one of the types for which
==
is meaningful.
player.ship.position == Vector3D(5, 7, 22)
isn’t, because
==
isn’t meaningful for
Vector3D
objects. This is similar to how you can add numbers using
+
, but have to use the
add()
method for vectors. The next version of JavaScript
may address this by adding “value types”, but that won’t be out until 2013, so we’ll be at least at test release 1.78 by then. ;-)