// ECMA-262, section 8.6.2.6, page 28. functionDefaultString(x) { if (!IS_SYMBOL_WRAPPER(x)) { if (IS_SYMBOL(x)) throwMakeTypeError(kSymbolToString); var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = % _CallFunction(x, toString); if (IsPrimitive(s)) return s; }
var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = % _CallFunction(x, valueOf); if (IsPrimitive(v)) return v; } } throwMakeTypeError(kCannotConvertToPrimitive); }
转数字过程
对象转换成数字也做了同样的事情,只是它会首先尝试调用 valueOf():
在 V8 源代码里的实现函数是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// ECMA-262, section 8.6.2.6, page 28. functionDefaultNumber(x) { var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = % _CallFunction(x, valueOf); if (IS_SYMBOL(v)) throwMakeTypeError(kSymbolToNumber); if (IS_SIMD_VALUE(x)) throwMakeTypeError(kSimdToNumber); if (IsPrimitive(v)) return v; } var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = % _CallFunction(x, toString); if (IsPrimitive(s)) return s; } throwMakeTypeError(kCannotConvertToPrimitive); }
// ECMA-262, section 9.1, page 30. Use null/undefined for no hint, // (1) for number hint, and (2) for string hint. functionToPrimitive(x, hint) { //hint的值可能为"NO_HINT"、"STRING_HINT"、"NUMBER_HINT"; if (!IS_SPEC_OBJECT(x)) return x; //如果hint为空且是日期对象的话,则先调用String转换; if (hint == NO_HINT) hint = IS_DATE(x) ? STRING_HINT : NUMBER_HINT; return hint == NUMBER_HINT ? DefaultNumber(x) : DefaultString(x); }
可以用以下例子做验证:
1 2 3 4 5 6 7 8 9 10
var d = newDate(); d.valueOf = function () { return1; }; d + 1; // => "1Tue Sep 20 2016 22:10:23 GMT+0800 (中国标准时间)" var a = []; a.valueOf = function () { return1; }; a + 1; // => 2;
// ECMA-262, section 11.8.5, page 53. The 'ncr' parameter is used as // the result when either (or both) the operands are NaN. functionCOMPARE(x, ncr) { var left; var right; // Fast cases for string, numbers and undefined compares. if (IS_STRING(this)) { if (IS_STRING(x)) return %_StringCompare(this, x); if (IS_UNDEFINED(x)) return ncr; left = this; } elseif (IS_NUMBER(this)) { if (IS_NUMBER(x)) return %NumberCompare(this, x, ncr); if (IS_UNDEFINED(x)) return ncr; left = this; } elseif (IS_UNDEFINED(this)) { if (!IS_UNDEFINED(x)) { %$toPrimitive(x, NUMBER_HINT); } return ncr; } elseif (IS_UNDEFINED(x)) { %$toPrimitive(this, NUMBER_HINT); return ncr; } else { left = %$toPrimitive(this, NUMBER_HINT); } right = %$toPrimitive(x, NUMBER_HINT); if (IS_STRING(left) && IS_STRING(right)) { return %_StringCompare(left, right); } else { var left_number = %$toNumber(left); var right_number = %$toNumber(right); if (NUMBER_IS_NAN(left_number) || NUMBER_IS_NAN(right_number)) return ncr; return %NumberCompare(left_number, right_number, ncr); } }