js valueOf

toString的区别

valueOf():返回最适合该对象类型的原始值;
toString(): 将该对象的原始值以字符串形式返回。

在数值运算里,会优先调用valueOf(),如a + b;
在字符串运算里,会优先调用toString(),如alert(c)。
知乎来源

我在网上找到的其他例子,放在crhome里面跑,或者nodejsV7.7.2都是不同的
所以有理由相信,之前的js实现跟当前流行的是不一样的,下面以nodejs为主

var c = {  
    i: 10,
    valueOf: function () {
        console.log("valueOf --->")
        return this.i + 30;
    },
    toString: function () {
       console.log("toString --->")
       return this.i + 10;
    } 
}
console.log(c > 20)  
console.log(+c)  
console.log(c)  
console.log(String(c));  
console.log(Number(c));

//output
valueOf --->  
true  
valueOf --->  
40  
valueOf --->  
{ [Number: 40]
  i: 10,
  valueOf: [Function: valueOf],
  toString: [Function: toString] }
toString --->  
20  
valueOf --->  
40  

从上面结果可以看到,当对象尝试+、>等跟数字运算符相关的,都是优先调用Valueof的,而String强转才调用toString

算数运算符:+ - * / % ++ --
关系运算符:== != > >= < <=
逻辑运算符:&& || ! ^ & |
位运算符:& | ~ ^ >> << >>>

难题

遇到过这样的一道难题,大概让写出满足下面条件的函数

console.log(  
    f(10)(10) == 20 &&
    f(10)(20)(50) == 80 &&
    f(10)(20)(50)(100) == 180 &&
    f(10, 20, 50, 101) == 181 &&
    f(10, 20, 50)(102) == 182
)
上述为true

是的如果f返回的是一个数字,那么例子2的f(3)可能再去调用f(3)(2)呢?答案就是这个valueOf(如果函数对象没有toString的话,其实用toString也可以,如果没有valueOf的话,回去尝试调用toString)

知乎上某答案区别
为什么valueOf比toString好呢?贴一段v8代码Object -> Primitive的过程:如果是Date类型的话,走DefaultString,否则走DefaultNumberDefaultNumber会判断Object有木有valueOf方法,有的话,直接调用return,没有的话去判断toString方法

/* -------------------------------------
   - - -   C o n v e r s i o n s   - - -
   -------------------------------------
*/

// ECMA-262, section 9.1, page 30. Use null/undefined for no hint,
// (1) for number hint, and (2) for string hint.
function ToPrimitive(x, hint) {  
  // Fast case check.
  if (IS_STRING(x)) return x;
  // Normal behavior.
  if (!IS_SPEC_OBJECT(x)) return x;
  if (IS_SYMBOL_WRAPPER(x)) throw MakeTypeError('symbol_to_primitive', []);
  if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
  return (hint == NUMBER_HINT) ? %DefaultNumber(x) : %DefaultString(x);
}
// ECMA-262, section 8.6.2.6, page 28.
function DefaultNumber(x) {  
  if (!IS_SYMBOL_WRAPPER(x)) {
    var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
      var v = %_CallFunction(x, valueOf);
      if (%IsPrimitive(v)) return v;
    }

    var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
      var s = %_CallFunction(x, toString);
      if (%IsPrimitive(s)) return s;
    }
  }
  throw %MakeTypeError('cannot_convert_to_primitive', []);
}

当然我这道题跟上面那道答案的题是有点不同的,不过本质上是一样的
答案如下:

function f(){  
    let sum = 0;
    for(i in arguments) {
        sum += arguments[i];
    }
    function m(){
        for (i in arguments) {
            sum += arguments[i];
        }
        return m;
    }
    m.valueOf=function(){
        return sum;
    }
    return m;
}

这东西叫柯里化,本质上如果直接打印f(10)其实还是打印对象本身

console.log(f(10))  
//output
{ [Number: 10] valueOf: [Function] }