通过JavaScript 柯里化求加法运算引发的对JavaScript类型转换的思考
2017-02-13 10:49
686 查看
建议先看看下面的这两篇文章,便于本文的理解。
http://blog.csdn.net/u010533180/article/details/54598283
http://blog.csdn.net/u010533180/article/details/54427200
各大JavaScript 面试题基本都有柯里化方面的面试题,比如网易就有如下的面试题:
实现一个函数,运算结果可以满足如下预期结果:
看到这样的面试题,首先就想到了使用加法运算,如下:
经过验证发现输出的结果都是plus里面的定义。如下:
后来经过查询各方面资料,可以重写toString()和valueOf()方法得到最终的结果。
valueOf 和 toString
先来简单了解下这两个方法:
Object.prototype.valueOf()
用 MDN 的话来说,valueOf() 方法返回指定对象的原始值。
JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。
记住上面这句话,下面我们会细说所谓的自动调用是什么意思。
Object.prototype.toString()
toString() 方法返回一个表示该对象的字符串。
每个对象都有一个 toString() 方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
先了解下 javascript 的几种原始类型,除去 Object 和 Symbol,有如下几种原始类型:
在 JavaScript 进行对比或者各种运算的时候会把对象转换成这些类型,下面逐一说明:
String 类型转换
在某个操作或者运算需要字符串而该对象又不是字符串的时候,会触发该对象的 String 转换,会将非字符串的类型尝试自动转为 String 类型。系统内部会自动调用 toString 函数。举个例子:
转换规则:
如果 toString 方法存在并且返回原始类型,返回 toString 的结果。
如果 toString 方法不存在或者返回的不是原始类型,调用 valueOf 方法,如果 valueOf 方法存在,并且返回原始类型数据,返回 valueOf 的结果。
其他情况,抛出错误。
上面的例子实际上是:
假设是数组:
上面 + arr ,由于这里是个字符串加操作,后面的 arr 需要转化为一个字符串类型,所以其实是调用了 + arr.toString() 。
但是,我们可以自己改写对象的 toString,valueOf 方法:
上面 alert(objOverWrite) ,objOverWrite会自动调用自己的 obj.toString() 方法转化为原始类型,如果我们不重写它的 toString 方法,将输出 [object Object] ,这里我们重写了 toString ,而且返回了一个原始类型字符串 0 ,所以最终 alert 出了 0。
上面的转化规则写了,toString 方法需要存在并且返回原始类型,那么如果返回的不是一个原始类型,则会去继续寻找对象的 valueOf 方法,如果valueOf返回也不是原始类型,则直接抛出错误。
Number 类型转换
上面描述的是 String 类型的转换,很多时候也会发生 Number 类型的转换:
调用 Number() 函数,强制进行 Number 类型转换
调用 Math.sqrt() 这类参数需要 Number 类型的方法
obj == 1 ,进行对比的时候
obj + 1 , 进行运算的时候
与 String 类型转换相似,但是 Number 类型刚好反过来,先查询自身的 valueOf 方法,再查询自己 toString 方法:
如果 valueOf 存在,且返回原始类型数据,返回 valueOf 的结果。
如果 toString 存在,且返回原始类型数据,返回 toString 的结果。
其他情况,抛出错误。
按照上述步骤,分别尝试一下:
Boolean 转换
布尔比较时
if(obj) , while(obj) 等判断时
简单来说,除了下述 6 个值转换结果为 false,其他全部为 true:
Function 转换
我们定义一个函数如下:
如果我们仅仅是调用 fun而不是 fun() ,看看会发生什么?
试试直接输出fun.valueOf()
发现直接调用fun和调用fun.valueOf()方法,输出结果是一样的。现在让我们改写一下 fun函数的 valueOf 方法。
与 Number 转换类似,如果函数的 valueOf 方法返回的不是一个原始类型,会继续找到它的 toString 方法:
现在让我们看看之前提到的柯里化加法运算:
当调用一次 add 的时候,实际是是返回 plus 这个 function,实际是也就是返回 plus .valueOf();
当链式调用两次的时候:
当链式调用三次的时候:
我们通过谷歌浏览器输出的最后一条数据看到最终的结果前面有个function 代表是里面的plus方法返回的,但是我们重写了valueOf方法,则会调用这个valueOf 返回对应的原始值。如果没有重写valueOf或者toString()方法,则直接输出plus的定义了。
这里有个规律,如果只改写 valueOf() 或是 toString() 其中一个,会优先调用被改写了的方法,而如果两个同时改写,则会像 Number 类型转换规则一样,优先查询 valueOf() 方法,在 valueOf() 方法返回的是非原始类型的情况下再查询 toString() 方法。
建议读者可以仔细阅读下面这篇文章,讲的很详细。
http://www.cnblogs.com/coco1s/p/6509141.html
http://blog.csdn.net/u010533180/article/details/54598283
http://blog.csdn.net/u010533180/article/details/54427200
各大JavaScript 面试题基本都有柯里化方面的面试题,比如网易就有如下的面试题:
实现一个函数,运算结果可以满足如下预期结果:
add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15
看到这样的面试题,首先就想到了使用加法运算,如下:
function add(m) { var res = m; function plus(n) { res += n; return plus; } return plus; };
经过验证发现输出的结果都是plus里面的定义。如下:
function plus(n) { res += n; return plus; }
后来经过查询各方面资料,可以重写toString()和valueOf()方法得到最终的结果。
function add(m) {
var res = m;
function plus(n) { res += n; return plus; }
plus.valueOf= function () {
return res;
};
return plus;
};
valueOf 和 toString
先来简单了解下这两个方法:
Object.prototype.valueOf()
用 MDN 的话来说,valueOf() 方法返回指定对象的原始值。
JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。
记住上面这句话,下面我们会细说所谓的自动调用是什么意思。
Object.prototype.toString()
toString() 方法返回一个表示该对象的字符串。
每个对象都有一个 toString() 方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
先了解下 javascript 的几种原始类型,除去 Object 和 Symbol,有如下几种原始类型:
Number String Boolean Undefined Null
在 JavaScript 进行对比或者各种运算的时候会把对象转换成这些类型,下面逐一说明:
String 类型转换
在某个操作或者运算需要字符串而该对象又不是字符串的时候,会触发该对象的 String 转换,会将非字符串的类型尝试自动转为 String 类型。系统内部会自动调用 toString 函数。举个例子:
var obj = {name: 'huluwa'}; var str = '123' + obj; console.log(str); // 123[object Object]
转换规则:
如果 toString 方法存在并且返回原始类型,返回 toString 的结果。
如果 toString 方法不存在或者返回的不是原始类型,调用 valueOf 方法,如果 valueOf 方法存在,并且返回原始类型数据,返回 valueOf 的结果。
其他情况,抛出错误。
上面的例子实际上是:
var obj = {name: 'huluwa'}; var str = '123' + obj.toString(); 其中,obj.toString() 的值为 "[object Object]"。
假设是数组:
var arr = [1, 2]; var str = '123' + arr; console.log(str); // 1231,2
上面 + arr ,由于这里是个字符串加操作,后面的 arr 需要转化为一个字符串类型,所以其实是调用了 + arr.toString() 。
但是,我们可以自己改写对象的 toString,valueOf 方法:
var objOverWrite = { toString: function() { console.log('调用了 objOverWrite .toString'); return {}; }, valueOf: function() { console.log('调用了objOverWrite .valueOf') return '0'; } } alert(objOverWrite);//注意这里是alert // 调用了 objOverWrite.toString // 调用了 objOverWrite.valueOf // 0 var objOverWrite = { toString: function() { console.log('调用了 objOverWrite .toString'); return {}; }, valueOf: function() { console.log('调用了objOverWrite .valueOf') return '0'; } } console.log(objOverWrite+1);//这里使用了加法运算,如果直接输出objOverWrite 咋把定义直接输出 调用了objOverWrite .valueOf 01
上面 alert(objOverWrite) ,objOverWrite会自动调用自己的 obj.toString() 方法转化为原始类型,如果我们不重写它的 toString 方法,将输出 [object Object] ,这里我们重写了 toString ,而且返回了一个原始类型字符串 0 ,所以最终 alert 出了 0。
上面的转化规则写了,toString 方法需要存在并且返回原始类型,那么如果返回的不是一个原始类型,则会去继续寻找对象的 valueOf 方法,如果valueOf返回也不是原始类型,则直接抛出错误。
Number 类型转换
上面描述的是 String 类型的转换,很多时候也会发生 Number 类型的转换:
调用 Number() 函数,强制进行 Number 类型转换
调用 Math.sqrt() 这类参数需要 Number 类型的方法
obj == 1 ,进行对比的时候
obj + 1 , 进行运算的时候
与 String 类型转换相似,但是 Number 类型刚好反过来,先查询自身的 valueOf 方法,再查询自己 toString 方法:
如果 valueOf 存在,且返回原始类型数据,返回 valueOf 的结果。
如果 toString 存在,且返回原始类型数据,返回 toString 的结果。
其他情况,抛出错误。
按照上述步骤,分别尝试一下:
var obj = { valueOf: function() { console.log('调用 valueOf'); return 0; } } console.log(obj + 1); // 调用 valueOf // 6
var obj = { valueOf: function() { console.log('调用 valueOf'); return {}; }, toString: function() { console.log('调用 toString'); return 0; } } console.log(obj + 1); // 调用 valueOf // 调用 toString // 1
var obj = { valueOf: function() { console.log('调用 valueOf'); return {}; }, toString: function() { console.log('调用 toString'); return {}; } } console.log(obj + 1); // 调用 valueOf // 调用 toString // Uncaught TypeError: Cannot convert object to primitive value
Boolean 转换
布尔比较时
if(obj) , while(obj) 等判断时
简单来说,除了下述 6 个值转换结果为 false,其他全部为 true:
undefined null -0 0或+0 NaN ''(空字符串)
Boolean(undefined) // false Boolean(null) // false Boolean(0) // false Boolean(NaN) // false Boolean('') // false
Function 转换
我们定义一个函数如下:
function fun() { var a = 1; console.log(1); }
如果我们仅仅是调用 fun而不是 fun() ,看看会发生什么?
function fun() { var n = 1; console.log(1); } fun 输出结果为: function fun() { var n = 1; console.log(1); }
试试直接输出fun.valueOf()
function fun() { var n = 1; console.log(1); } fun.valueOf(); 输出结果为: function fun() { var n = 1; console.log(1); }
发现直接调用fun和调用fun.valueOf()方法,输出结果是一样的。现在让我们改写一下 fun函数的 valueOf 方法。
fun.valueOf = function() { console.log('调用 valueOf 方法'); return 2; } fun; // 输出如下: // 调用 valueOf 方法 // 2
与 Number 转换类似,如果函数的 valueOf 方法返回的不是一个原始类型,会继续找到它的 toString 方法:
fun.valueOf = function() { console.log('调用 valueOf 方法'); return {}; } fun.toString= function() { console.log('调用 toString 方法'); return 3; } fun; // 输出如下: // 调用 valueOf 方法 // 调用 toString 方法 // 3
现在让我们看看之前提到的柯里化加法运算:
function add(m) { console.log("进入add方法"); var res = m; function plus(n) { console.log("进入plus方法"); res += n; return plus; } plus.valueOf= function () { console.log("valueOf方法"); return res; }; plus.toString= function () { console.log("toString方法"); return res; }; return plus; };
当调用一次 add 的时候,实际是是返回 plus 这个 function,实际是也就是返回 plus .valueOf();
add(1); 进入add方法 valueOf方法 function 1
当链式调用两次的时候:
add(1)(2); 进入add方法 进入plus方法 valueOf方法 function 3
当链式调用三次的时候:
add(1)(2)(3); 进入add方法 进入plus方法 进入plus方法 valueOf方法 function 6
我们通过谷歌浏览器输出的最后一条数据看到最终的结果前面有个function 代表是里面的plus方法返回的,但是我们重写了valueOf方法,则会调用这个valueOf 返回对应的原始值。如果没有重写valueOf或者toString()方法,则直接输出plus的定义了。
这里有个规律,如果只改写 valueOf() 或是 toString() 其中一个,会优先调用被改写了的方法,而如果两个同时改写,则会像 Number 类型转换规则一样,优先查询 valueOf() 方法,在 valueOf() 方法返回的是非原始类型的情况下再查询 toString() 方法。
建议读者可以仔细阅读下面这篇文章,讲的很详细。
http://www.cnblogs.com/coco1s/p/6509141.html
相关文章推荐
- 一道面试题引发的对javascript类型转换的思考
- 一道面试题引发的对javascript类型转换的思考
- 一道面试题引发的对javascript类型转换的思考
- 一道面试题引发的对javascript类型转换的思考
- 一道面试题引发的对JavaScript类型转换的思考
- 一道面试题引发的对javascript类型转换的思考
- 一道面试题引发的对JavaScript类型转换的思考
- 一道面试题引发的对javascript类型转换的思考
- javascript不同类型数据之间的运算是如何转换的
- 一道面试题引发的对数据类型转换的思考
- Javascript常见的不同类型运算的转换方式
- 一道面试题引发的对 JS 类型转换的思考
- 一个VC编译错误引发的对显示类型转换的思考(static_cast、dynamic_cast和const_cast)
- javascript入门·简单运算和数据类型转换
- javascript不同类型数据之间的运算的转换方法
- 前端-javascript-不同数据类型间的运算,数据类型转换规则
- javascript不同类型数据之间的运算的转换方法
- javascript不同类型数据之间运算的转换
- 一条语句引发的思考:装箱和拆箱,空指针的类型转换
- 在JavaScript中也玩变量类型强行转换