14

I was confusing myself a little with a thought experiment and now I'm looking for some advice. Its about ECMAscript references and the Array.prototype.indexOf() method.

Lets start easy:

var container = [ ];
// more code
container.push( 5 );
container.push( 7 );
container.push( 10 );

So now we pushed some "primitive values" into our ECMAscript array (whether or not that statement is true I'll come back for), at least I imagined it like this so far. A call to

container.indexOf( 7 );

will return 1 as expected. The big question I'm having is, if .indexOf() really compares the primitive value or if in reality a Number() object is created + stored and its reference is getting compared. This becomes a little more obvious if we re-write that like so:

var a = 5,
    b = 7,
    c = 10;

var container = [ ];

container.push( a );
container.push( b );
container.push( c );

container.indexOf( b );

Until this point, one could still easily argue that all .indexOf() needs to do is to compare values, but now lets look at something like this:

var a = { name: 'a', value: 5 },
    b = { name: 'b', value: 10 },
    c = { name: 'c', value: 15 };

var container = [ ];
// more code
container.push( a );
container.push( b );
container.push( c );

Here, we filled that container array with object-references and still, .indexOf() works as expected

container.indexOf( b ) // === 1

while a call like this

container.indexOf({ name: 'b', value: 10 });

obviously returns -1 since we are creating a new object and get a new reference. So here it must internally compare references with each other, right?

Can some ECMAscript spec genius confirm that or even better link me some material about that ?

A side question on this would be if there is any possibly way to access an internally stored object-reference within a lexicalEnvironment respectively Activation Object.

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • If this works anywhere similar to other languages, then `indexOf` works on object hashes where objects usually have different values and primitive objects usually have a constant hash based on their value (for integers, this is very often just the integer value itself). So two int objects `5` and `5` both have the hash `5` and as such are “the same”. – poke Sep 27 '12 at 10:26
  • 1
    I started writing an answer, but then it turned into a blog post so I stopped and got discouraged from re-writing it. But if you're interested: http://pastie.org/4828933 – Zirak Sep 27 '12 at 10:38

3 Answers3

8

It boils down to indexOf() comparing against each array property in turn using the same algorithm as the === operator.

The relevant section of the ECMAScript 5 spec is section 15.4.4.14, step 9, section b (highlighting mine):

If kPresent is true, then

i. Let elementK be the result of calling the [[Get]] internal method of O with the argument ToString(k).

ii. Let same be the result of applying the Strict Equality Comparison Algorithm to searchElement and elementK.

iii. If same is true, return k.

References:

Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • nice heads-up thanks. However, the *strict equality comparison* described there does not describe how to deal with objects in general ? Looks like they only describe for numbers, strings, booleans and null/undefined values. Also, I'm curious about the first section aswell which says, "*calling toString()*" on the argument. If that really is the native `toString` then all objects should return *object Object* no ? – jAndy Sep 27 '12 at 11:03
  • @jAndy: the `ToString(k)` bit is referring to the property name, not the value. The strict equality comparison section does describe the algorithm completely. See step 7 in 11.9.6: *"Return true if x and y refer to the same object. Otherwise, return false."*. – Tim Down Sep 27 '12 at 11:23
2

I'm not sure if this is guaranteed across all ECMAScript implementations or not, but the Mozilla documentation states that it uses strict equality to make the comparison (===). As such this would exhibit the behaviour you describe, comparing by values on primitives, but by reference on objects (see strict equality).

Vala
  • 5,628
  • 1
  • 29
  • 55
  • Even `==` uses comparison by references for objects. So it's nothing to do with strict equality – zerkms Sep 27 '12 at 10:26
  • 3
    I'm not saying it's *because of* strict equality. I'm saying it's actually using strict equality and that's consistent with the behaviour he describes. – Vala Sep 27 '12 at 10:29
0

@Tim Down is right. indexOf does strict comparison. I am giving a demonstration of this by overriding valueOf function

var MyObject = function(n, v){
   this.name = n;
   this.value = v;
}

MyObject.prototype.valueOf = function(){
    return this.value;
}

var a = new MyObject("a", 5);
var b = new MyObject("b", 10);
var c = new MyObject("c", 15);

var container = [ ];

container.push( a );
container.push( b );
container.push( c );

console.log(b == 10); // true
console.log(container[1] == 10); // true

console.log(b === 10); // false
container.indexOf(10); // -1
Diode
  • 24,570
  • 8
  • 40
  • 51