valueOf和toString

有一道经典的题目:

var add = function() {___}; console.log(add(3)(4)(5)); // 输出60

  题目要求能无限相乘,请补充add函数。

  首先很显然,add函数必须返回一个带一个参数的function,不然无法进行后续的连乘操作;其次,乘到最后,没有参数了,add函数直接得把数值返回。其实这类似一个深搜的函数,根据n的值进行递归:

1 var add = function(n) {

2   if(n === 5) 

3     return n;

4   return n * add(n + 1);

5 }

6 console.log(add(3));

  第二行相当于没有参数时的判断操作,第四行相当于返回一个带参数的方法。此题的难点是值的累计,如果能设置一个全局变量保存值,将会方便很多,因为没有中间变量,而根据js函数特性又只能返回一个函数,不能返回函数与数字相乘的方式。如果能设置全局变量:

1 var add = function(n) {

2   ans *= n;

3   return add;

4 }

5 var ans = 1;

6 add(5)(4)(3);

7 console.log(ans);

  因为要返回一个函数,又要实现值的累计,又不能外加变量,所以只能将累计的值作为参数传入。

1 var add = function(a) {

2   var temp = function(b) {

3     return add(a * b);

4   }

5   return temp;

6 }

  那么,add(3)(4)(5)在以上代码中是如何运作的?第一步a=3,b=4,运算后变成add(3*4)(5),第二步,a=12,b=5,运算后变成add(3*4*5),第三步a=60传入,temp方法return了,在控制台看到如下:

function (b) {

   return add(a * b);

} 

  这时就要关注一下function的toString和valueOf方法。重写toString和valueOf方法,就能得到答案。

 1 var add = function(a) {

 2   var temp = function(b) {

 3     return add(a * b);

 4   }

 5   temp.toString = temp.valueOf = function() {

 6     return a;

 7   }

 8   return temp;

 9 }

10 console.log(add(3)(4)(5));

 

关于valueOf & toString:

  js里除了null外每个对象都有valueOf和toString方法,一般情况下对象会先调用toString方法,然后再调用valueOf。

var bbb = {

  toString: function() {

    console.log('toString');

    return 10;

  },

  valueOf: function() {

    console.log('valueOf');

    return 10;

  }

}



alert(bbb);// 10 toString

alert(+bbb); // 10 valueOf

alert(''+bbb); // 10 valueOf

alert(String(bbb)); // 10 toString

alert(Number(bbb)); // 10 valueOf

alert(bbb == '10'); // true valueOf

alert(bbb === '10'); // false

  这是司徒正美的一个例子,他也对结果作出了解释。如果转换为字符串时调用toString方法,如果是转换为数值时则调用valueOf方法,但其中有两个很不和谐,一个是alert(''+bbb),字符串合拼应该是调用toString方法……另一个我们暂时可以理解为===操作符不进行隐式转换,因此不调用它们。为了追究真相,我们需要更严谨的实验。

var aa = {

  i: 10,

  toString: function() {

    console.log('toString');

    return this.i;

  }

}

 

alert(aa);// 10 toString

alert(+aa); // 10 toString

alert(''+aa); // 10 toString

alert(String(aa)); // 10 toString

alert(Number(aa)); // 10 toString

alert(aa == '10'); // true toString

  因为aa没重写valueOf方法,在实际运行时,会先优先去寻找其toString方法,而比如alert(+aa)其实是应该优先调用valueOf方法的,但是木有valueOf,所以就退而求其次调用toString了。

var bb = {

   i: 10,

   valueOf: function() {

     console.log('valueOf');

     return this.i;

   }

 }

 

 alert(bb);// [object Object]

 alert(+bb); // 10 valueOf

 alert(''+bb); // 10 valueOf

 alert(String(bb)); // [object Object]

 alert(Number(bb)); // 10 valueOf

 alert(bb == '10'); // true valueOf

  而以上结果似乎不太和谐,顺着思路,没有toString方法,所以就调用valueOf,但是为何1和4输出的是[object object]。司徒正美说是继承了Object的toString方法,尝试去掉后:

Object.prototype.toString = null;

 

var cc = {

  i: 10,

  valueOf: function() {

    console.log('valueOf');

    return this.i;

  }

}

 

alert(cc);// 10 valueOf

alert(+cc); // 10 valueOf

alert(''+cc); // 10 valueOf

alert(String(cc)); // 10 valueOf

alert(Number(cc)); // 10 valueOf

alert(cc == '10'); // true valueOf

如果只重写了toString,对象转换时会无视valueOf的存在来进行转换。但是,如果只重写了valueOf方法,在要转换为字符串的时候会优先考虑toString方法。在不能调用toString的情况下,只能让valueOf上阵了。对于那个奇怪的字符串拼接问题,可能是出于操作符上,翻开ECMA262-5 发现都有一个getValue操作。嗯,那么谜底应该是揭开了。重写会加大它们调用的优化高,而在有操作符的情况下,valueOf的优先级本来就比toString的高。

你可能感兴趣的:(toString)