故事的起因源于群中有人提的一个问题:
a(1)=1;
a(1)(2)=3;
a(1)(2)(3)=6;
还原出函数a
好吧,我刚看到这个题目的时候,第一反应就是递归的闭包,然后就写了下面这一版:
var a = function(num){
this.sum = this.sum || 0;
if(num){
this.sum += num;
return a;
}
var result = this.sum;
delete this.sum;
return result;
}
但是这样的执行是这样的:
a(1)(); // 1
a(1)(2)(); // 3
和题目要求的不一样,而且还有sum没有及时重置,所以,这个办法不行
然后想了一会儿没想出好办法,就去求助原公司大牛。果然大牛就是不一样,给出了下面这个解决方案
function a(x){
var list = [];
list.push(x);
var y = function(xx){
list.push(xx);
return y;
};
y.valueOf = function(){
return list.reduce(function(a,b){return a+b;},0);
}
return y;
}
利用valueOf和闭包里的一个list做的,果断膜拜了一下
然后,后来有看到群里有人给出了这样的答案:
function a(num){
function b(i){
return a(num+i);
}
b.toString = function(){
return num;
};
return b;
}
也是可以的,然后就比较好奇,这个valueOf和toString是怎样被执行的呢?
果断去补习了一下基础知识:
对于所有的js对象(除了null以外),都有valueOf和toString方法,这是为了更方便的对一个对象进行值的操作
比如说我们要比较两个object的大小的时候,就可以定义一下它的valueOf方法或者toString來比较大小,那么到底什么时候会调用这两个方法呢?
来看一个小测试:
var bbb = {
i: 10,
toString: function() {
console.log('toString');
return this.i;
},
valueOf: function() {
console.log('valueOf');
return this.i;
}
}
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方法,但是,我们发现(''+bbb)这个调用的是valueOf,而最后的===则什么都没有调用,那我们再试一下:
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
很和谐的全是toString,再看看valueOf的:
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
我们发现中间出现了[object Object],貌似是从Object那里继承过来的,我们把它去掉试试:
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方法,那么当遇到需要类型转换的时候,就会直接调用toString方法,然后如果只定义了valueOf方法的话,转换数字的时候会优先调用valueOf方法,然后在转换字符串的时候,如果实在没有toString可以调用才会用valueOf代替,对于有操作符的情况下(比如=),valueOf的优先级要比toString要高
感谢 小秦 同学纠正:当转换为数字类型的时候,如果 valueOf 返回的不是基础类型,才会调用 toString 方法
好了,那我们还有最后一个问题,(''+bbb),这个字符串拼接为什么是调用的valueOf呢?
这个问题应该是因为+操作符上,这里会有一个getValue操作,然后就调用valueOf方法了
嗯,好的,今天就这么多,有不对的地方欢迎指正~