优秀的编程知识分享平台

网站首页 > 技术文章 正文

两个javascript类型转换问题的总结

nanyue 2024-11-01 12:45:39 技术文章 4 ℃


今天老K为大家分享两个有关javascript类型转换问题的总结。

1.写出下面表达式的输出内容

([][[]]+[])[+!![]]+([]+{})[!+[]+!![]]

这是一个拆箱转换和装箱转换的问题。如果对js类型转换不熟的话,这道题是很难解答的。现在我们就按照将复杂问题逐步拆分成简单问题的思路来简答。

第一步简化:

以最中间的+为基准将表达式分成两个:

([][[]]+[])[+!![]]
([]+{})[!+[]+!![]]

第二步简化:

将第一个表达式再细化拆分

([][[]]+[])
[+!![]]

将第二个表达式再细化拆分

([]+{})
[!+[]+!![]]

最终简化:

最后我们要细化拆分成优先解决这个表达式的问题

[][[]]   //underfined

这个表达式实际是找出一个空数组的未知下标的值,不确定的值输出是underfined。

知道了这个最小问题的返回值,我们就可以通过js的类型转化来求得表达式的值。

([][[]]+[])[+!![]]  // 通过转换实际是 "underfined"[1],最终得到字符串"n"
([]+{})[!+[]+!![]]  // 通过转换实际是 "[object Object]"[1],最终得到字符串"b"

对于 []+{} 的转换值为什么是"[object Object]",大家会有疑问?我引用《你不知道的 JavaScript(中卷)》第五章P102原文来解释一下:

还有一个坑常被提到(涉及强制类型转换,参见第 4 章):

[] + {}; // "[object Object]"

{} + []; // 0


表面上看 + 运算符根据第一个操作数([] 或 {})的不同会产生不同的结果,实则不然。 第一行代码中,{} 出现在 + 运算符表达式中,因此它被当作一个值(空对象)来处理。第4章讲过 [] 会被强制类型转换为 "",而 {} 会被强制类型转换为 "[object Object]"。但在第二行代码中,{} 被当作一个独立的空代码块(不执行任何操作)。代码块结尾不需要分号,所以这里不存在语法上的问题。最后 + [] 将 [] 显式强制类型转换(参见第 4 章) 为 0。

最终这个表达式的输出值为"nb"。

2.实现一个函数,运算结果可以满足如下预期结果:

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

这个题单纯用高阶函数和Array.prototype.reduce()是不能实现结果的。因为我们没有实现同时返回函数的值和函数本身继续调用。

为了同时实现这种效果,我们需要怎么做呢?

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

Object.prototype.toString()

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

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

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

看到这里你可能就有解题思路了。

在javascript中,函数也可以看成是一个object对象。如果想同时返回函数的值和函数本身,我们就可以重写函数的toString方法来改变函数的返回值以满足我们的需求。

解题方法:

function add () {
    console.log('进入add');
    var args = Array.prototype.slice.call(arguments);
    var fn = function(){
        var arg_fn = Array.prototype.slice.call(arguments);
        console.log('调用fn');
        return add.apply(null, args.concat(arg_fn));
    }
    fn.toString = function(){
        console.log('调用toString');
        return args.reduce(function(a,b){
            return a + b;
        })
    }
    return fn;
}

当调用一次add的时候,实际是返回fn这个function,实际也就是返回fn.toString();

add(1);
// 输出如下:
// 进入add
// 调用toString
// 1

当链式调用两次的时候:

add(1)(2);
// 输出如下:
// 进入add
// 调用fn
// 进入add
// 调用toString
// 3

当链式调用三次的时候:

add(1)(2)(3);
// 输出如下:
// 进入add
// 调用fn
// 进入add
// 调用fn
// 进入add
// 调用toString
// 6

可以看到,这里其实有一种循环。只有最后一次调用才真正调用到toString,而之前的操作都是合并参数,递归调用本身,由于最后一次调用返回的是一个fn函数,所以最终调用了函数的fn.toString,并且利用了reduce方法对所有参数求和。

有些文章也谈过改写valueOf也可以实现,但是本人亲自试了一下,没有效果。可能是浏览器版本的问题。这个大家也可以一块研究一下原因。



本文为原创内容,若转载请注明出处,转发感激不尽。

Tags:

最近发表
标签列表