HTML5技术

一道面试题引发的对javascript类型转换的思考 - ChokCoco

字号+ 作者:H5之家 来源:H5之家 2017-03-06 17:00 我要评论( )

最近群里有人发了下面这题: 实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3add(1, 2, 3)(10) // 16add(1)(2)(3)(4)(5) // 15 对于一个好奇的切图仔来说,忍不住动手尝试了一下,看到题目首先想到的是会用到高阶函数以及Array.prototype.redu

最近群里有人发了下面这题:
实现一个函数,运算结果可以满足如下预期结果:

add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15

对于一个好奇的切图仔来说,忍不住动手尝试了一下,看到题目首先想到的是会用到高阶函数以及 Array.prototype.reduce()。

高阶函数(Higher-order function):高阶函数的意思是它接收另一个函数作为参数。在 javascript 中,函数是一等公民,允许函数作为参数或者返回值传递。

得到了下面这个解法:

function add() { var args = Array.prototype.slice.call(arguments); return function() { var arg2 = Array.prototype.slice.call(arguments); return args.concat(arg2).reduce(function(a, b){ return a + b; }); } }

验证了一下,发现错了:

add(1)(2) // 3 add(1, 2)(3) // 6 add(1)(2)(3) // Uncaught TypeError: add(...)(...) is not a function(…)

上面的解法,只有在 add()() 情形下是正确的。而当链式操作的参数多于两个或者少于两个的时候,无法返回结果。

而这个也是这题的一个难点所在,add()的时候,如何既返回一个值又返回一个函数以供后续继续调用?

后来经过高人指点,通过重写函数的 valueOf 方法或者 toString 方法,可以得到其中一种解法:

function add () { var args = Array.prototype.slice.call(arguments); var fn = function () { var arg_fn = Array.prototype.slice.call(arguments); return add.apply(null, args.concat(arg_fn)); } fn.valueOf = function () { return args.reduce(function(a, b) { return a + b; }) } return fn; }

嗯?第一眼看到这个解法的时候,我是懵逼的。因为我感觉 fn.valueOf() 从头到尾都没有被调用过,但是验证了下结果:

add(1) // 1 add(1,2)(3) //6 add(1)(2)(3)(4)(5) // 15

神奇的对了!那么玄机必然是在上面的 fn.valueOf = function() {} 内了。为何会是这样呢?这个方法是在函数的什么时刻执行的?且听我一步一步道来。

  valueOf 和 toString

先来简单了解下这两个方法:

Object.prototype.valueOf()

用  的话来说,valueOf() 方法返回指定对象的原始值。

JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。

记住上面这句话,下面我们会细说所谓的自动调用是什么意思。

Object.prototype.toString()

toString() 方法返回一个表示该对象的字符串。

每个对象都有一个 toString() 方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。

这里先记住,valueOf() 和 toString() 在特定的场合下会自行调用。

原始类型

好,铺垫一下,先了解下 javascript 的几种原始类型,除去 Object 和 Symbol,有如下几种原始类型:

在 JavaScript 进行对比或者各种运算的时候会把对象转换成这些类型,从而进行后续的操作,下面逐一说明:

  String 类型转换

在某个操作或者运算需要字符串的时候,会触发 Object 的 String 转换,举个例子:

var obj = {name: 'Coco'}; var str = '123' + obj; console.log(str); // 123[object Object]

转换规则:

上面的例子实际上是:

var obj = {name: 'Coco'}; var str = '123' + obj.toString();

其中,obj.toString() 的值为 "[object Object]"。

假设是数组:

var arr = [1, 2]; var str = '123' + arr; console.log(str); // 1231,2

上面 + arr 其实是调用了 + arr.toString() 。

但是,我们可以自己改写对象的 toString,valueOf 方法:

var obj = { toString: function() { console.log('调用了 obj.toString'); return '111'; } } alert(obj); // 调用了 obj.toString // 111

上面 alert(obj) ,obj 会自动调用自己的 obj.toString() 方法转化为原始类型,如果我们不重写它的 toString 方法,将输出 [object Object] ,这里我们重写了 toString ,而且返回了一个原始类型字符串 111 ,所以最终 alert 出了 111。

上面的转化规则写了,toString 方法需要存在并且返回原始类型,那么如果返回的不是一个原始类型,则会去继续寻找对象的 valueOf 方法:

下面我们尝试证明如果 toString() 方法不可用的时候系统会调用 valueOf() 方法,下面我们改写对象的 valueOf:

var obj = { toString: function() { console.log('调用了 obj.toString'); return {}; }, valueOf: function() { console.log('调用了 obj.valueOf') return '110'; } } alert(obj); // 调用了 obj.toString // 调用了 obj.valueOf // 110

从结果可以看到,当 toString 不可用的时候,系统会再尝试 valueOf 方法,如果 valueOf 方法存在,并且返回原始类型(String、Number、Boolean)数据,返回valueOf的结果。

那么如果,toString 和 valueOf 返回的都不是原始类型呢?看下面这个例子:

var obj = { toString: function() { console.log('调用了 obj.toString'); return {}; }, valueOf: function() { console.log('调用了 obj.valueOf') return {}; } } alert(obj); // 调用了 obj.toString // 调用了 obj.valueOf // Uncaught TypeError: Cannot convert object to primitive value

可以发现,如果 toString 和 valueOf 方法均不可用的情况下,系统会直接返回一个错误。

  Number 类型转换

上面描述的是 String 类型的转换,很多时候也会发生 Number 类型的转换:

与 String 类型转换相似,但是 Number 类型刚好反过来,先查询自身的 valueOf 方法,再查询自己 toString 方法:

按照上述步骤,分别尝试一下:

var obj = { valueOf: function() { console.log('调用 valueOf'); return 5; } } console.log(obj + 1); // 调用 valueOf // 6

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 记一次和阿里某总监对话引发的思考:说说你框架的设计思路和优点亮点! - 路过秋天

    记一次和阿里某总监对话引发的思考:说说你框架的设计思路和优点亮点

    2016-12-20 11:00

  • Web前端面试题集锦 - wangwen896

    Web前端面试题集锦 - wangwen896

    2016-10-04 17:00

  • 很强大的HTML+CSS+JS面试题(附带答案) - Angelasp

    很强大的HTML+CSS+JS面试题(附带答案) - Angelasp

    2016-10-04 16:00

  • 前端开发面试题 - wangwen896

    前端开发面试题 - wangwen896

    2016-10-03 14:00

网友点评
s