/*=== TypeError TypeError string [object Boolean] string [object Boolean] string [object Number] string [object String] string 1,2 string [object Object] string [object Object] string JOIN string BOOLJOIN proto.join string foo;bar;quux string [object Object] MyJoinError ===*/ function basicTest() { var proto, obj; function test(this_value) { var t; try { t = Array.prototype.toString.call(this_value); print(typeof t, t); } catch (e) { print(e.name); } } // join() is called if the ToObject() coercion has it; for these basic tests // only the array ([1,2]) has a join(). Others fall back to the standard // Object.prototype.toString() test(undefined); test(null); test(true); test(false); test(123); test('foo'); test([1,2]); test({ foo: 1, bar:1 }); // explicit join() on an object test({ join: 123 }); // not callable, still fall back to Object.prototype.toString() test({ join: function() { return 'JOIN' } }); // join() inherited Boolean.prototype.join = function() { return 'BOOLJOIN'; }; test(true); // join() inherited, with joinable members proto = Object.create(Object.prototype); proto.join = function() { var i; var tmp = []; print('proto.join'); for (i = 0; i < this.length; i++) { tmp.push(String(this[i])); } return tmp.join(';'); }; obj = Object.create(proto); obj.length = 3; obj['0'] = 'foo'; obj['1'] = 'bar'; obj['2'] = 'quux'; test(obj); // the join() fallback is the standard Object.prototype.toString() function, // i.e. override should have no effect (must return '[object Object]' and // not 'OBJ-TOSTRING'). Object.prototype.toString = function() { return 'OBJ-TOSTRING'; }; test({ foo: 1, bar: 2 }); // a join() error should propagate out test({ join: function() { var e = new Error('join error'); e.name = 'MyJoinError'; throw e; } }); } try { basicTest(); } catch (e) { print(e); }