`

JavaScript Puzzlers 解密(一):为什么 ["1", "2", "3"].map(parseInt) 返回 [1, NaN, NaN]?

 
阅读更多

JavaScript Puzzlers! 被称为 javascript 界的专业八级测验,感兴趣的 jser 可以去试试。 我试了一下, 36 道题只做对了 19 道, 算下来正确率为 53%,还没有及格。

第一题为 ["1", "2", "3"].map(parseInt) 的返回值。

> ["1", "2", "3"].map(parseInt)
[1, NaN, NaN]

在 javascript 中 ["1", "2", "3"].map(parseInt) 为何返回不是 [1, 2, 3] 却是 [1, NaN, NaN]

我们首先回顾一下 parseInt() 个 map() 两个函数的用法:

parseInt() 函数

定义和用法

parseInt() 函数可解析一个字符串,并返回一个整数。

语法

parseInt(string, radix)
参数 描述
string 必需。要被解析的字符串。
radix

可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。

如果省略该参数或其值为 `0`,则数字将以 10 为基础来解析。如果它以 `"0x"` 或 `"0X"` 开头,将以 16 为基数。

如果该参数小于 2 或者大于 36,则 `parseInt()` 将返回 `NaN`。

返回值

返回解析后的数字。

说明

当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。

举例:

  1. 如果 string 以 "0x" 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。

  2. 如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。

  3. 如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。

提示和注释

注释:只有字符串中的第一个数字会被返回。

注释:开头和结尾的空格是允许的。

提示:如果字符串的第一个字符不能被转换为数字,那么 parseInt() 会返回 NaN

实例

在本例中,我们将使用 parseInt() 来解析不同的字符串:

parseInt("10");         // 返回 10 (默认十进制)
parseInt("19",10);      // 返回 19 (十进制: 10+9)
parseInt("11",2);       // 返回 3 (二进制: 2+1)
parseInt("17",8);       // 返回 15 (八进制: 8+7)
parseInt("1f",16);      // 返回 31 (十六进制: 16+15)
parseInt("010");        // 未定:返回 10 或 8

map 方法

对数组的每个元素调用定义的回调函数并返回包含结果的数组。

array1.map(callbackfn[, thisArg])
参数 定义
array1 必需。一个数组对象。
callbackfn 必需。一个接受**最多**三个参数的函数。对于数组中的每个元素,`map` 方法都会调用 `callbackfn` 函数一次。
thisArg 可选。可在 `callbackfn` 函数中为其引用 `this` 关键字的对象。如果省略 `thisArg`,则 `undefined` 将用作 `this` 值。

返回值

其中的每个元素均为关联的原始数组元素的回调函数返回值的新数组。

异常

如果 callbackfn 参数不是函数对象,则将引发 TypeError 异常。

备注

对于数组中的每个元素,map 方法都会调用 callbackfn 函数一次(采用升序索引顺序)。 不为数组中缺少的元素调用该回调函数。

除了数组对象之外,map 方法可由具有 length 属性且具有已按数字编制索引的属性名的任何对象使用。

回调函数语法

回调函数的语法如下所示:

function callbackfn(value, index, array1)

可使用最多三个参数来声明回调函数。

下表列出了回调函数参数。

回调参数 定义
value 数组元素的值。
index 数组元素的数字索引。
array1 包含该元素的数组对象。

修改数组对象

数组对象可由回调函数修改。

下表描述了在 map 方法启动后修改数组对象所获得的结果。

`map` 方法启动后的条件 元素是否传递给回调函数
在数组的原始长度之外添加元素。 否。
添加元素以填充数组中缺少的元素。 是,如果该索引尚未传递给回调函数。
元素被更改。 是,如果该元素尚未传递给回调函数。
从数组中删除元素。 否,除非该元素已传递给回调函数。

示例

下面的示例阐释了 map 方法的用法。

// 定义回调函数
// 计算圆的面积
function AreaOfCircle(radius) { 
    var area = Math.PI * (radius * radius); 
    return area.toFixed(0); 
} 

// 定义一个数组,保护三个元素
var radii = [10, 20, 30]; 

// 计算 radii 的面积. 
var areas = radii.map(AreaOfCircle); 

document.write(areas); 

// 输出: 
// 314,1257,2827

下面的示例阐释 thisArg 参数的用法,该参数指定对其引用 this 关键字的对象。

// 定义一个对象 object,保护 divisor 属性和 remainder 方法
// remainder 函数求每个传入的值的个位数。(即除以 10 取余数)
var obj = { 
    divisor: 10, 
    remainder: function (value) { 
        return value % this.divisor; 
    } 
} 

// 定义一个包含 4 个元素的数组
var numbers = [6, 12, 25, 30]; 

// 对 numbers 数组的每个元素调用 obj 对象的 remainder 函数。
// map 函数的第 2 个参数传入 ogj。 
var result = numbers.map(obj.remainder, obj); 
document.write(result); 

// 输出: 
// 6,2,5,0

在下面的示例中,内置 JavaScript 方法用作回调函数。

// 对数组中的每个元素调用 Math.sqrt(value) (求平方根)
var numbers = [9, 16]; 
var result = numbers.map(Math.sqrt); 

document.write(result); 
// 输出: 3,4

[9, 16].map(Math.sqrt) 回调函数,输出的结果是 [3, 4]。 但是为什么 ["1", "2", "3"].map(parseInt) 却返回 [1,NaN,NaN]

网站给出的提示是:

what you actually get is [1, NaN, NaN] because parseInt takes two parameters (val, radix) and map passes 3 (element, index, array)

简单翻译一下就是

parseInt 需要 2 个参数 (val, radix), 而 map 传递了 3 个参数 (element, index, array)」。


通过上面的解释,我们可以看出,如果想让 parseInt(string, radix) 返回 NaN,有两种情况:

  1. 第一个参数不能转换成数字。

  2. 第二个参数不在 2 到 36 之间。

我们传入的参数都能转换成数字,所以只能是第二种可能。

到底是不是呢?我们重新定义 parseInt(string, radix) 函数:

var parseInt = function(string, radix) {
    return string + "-" + radix;
};

["1", "2", "3"].map(parseInt);

输出结果为:

["1-0", "2-1", "3-2"]

看见,map 函数将数组的值 value 传递给了 parseInt 的第一个参数,将数组的索引传递给了第二个参数。 第三个参数呢?我们再加一个参数

var parseInt = function(string, radix, obj) {
    return string + "-" + radix + "-" + obj;
};

["1", "2", "3"].map(parseInt);

输出结果:

["1-0-1,2,3", "2-1-1,2,3", "3-2-1,2,3"]

我们再继续增加参数:

var parseInt = function(string, radix, obj, other) {
    return string + "-" + radix + "-" + obj + "-" + other;
};

["1", "2", "3"].map(parseInt);

输出结果:

["1-0-1,2,3-undefined", "2-1-1,2,3-undefined", "3-2-1,2,3-undefined"]

第四个参数为 undefined,看见 map 确实为 parseInt 传递了三个参数。就像作者写道的:

(element, index, array)
  1. 数组的值

  2. 数组的索引

  3. 数组

UPDATE 原文勘误:(谢谢 米粽粽 提醒)

["1", "2", "3"].map(parseInt)

应该对应的是:

[parseInt("1", 0), parseInt("2", 1), parseInt("3", 2)]

parseInt("3", 2) 的第二个参数是界于 2-36 之间的,之所以返回 NaN 是因为 字符串 "3" 里面没有合法的二进制数,所以 NaN

我们还可以继续试验:

> ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"].map(parseInt)
[1, NaN, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

只有当第二个参数是 1 的时候返回 NaN,其它情况都返回 1

> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"].map(parseInt)
[1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15, 17, 19, 21]

简单列举一下:

parseInt("1", 0);    // 十进制 1
parseInt("2", 1);    // 第二个参数不在 2-36 直接
parseInt("3", 2);    // 二进制 NaN
parseInt("4", 3);    // 三进制
parseInt("5", 4);
parseInt("6", 5);
parseInt("7", 6);
parseInt("8", 7);
parseInt("9", 8);
parseInt("10", 9);   // 九进制 (1*9+0 = 9)
parseInt("11", 10);  // 十进制 (1*10+1 = 11)
parseInt("12", 11);
parseInt("13", 12);
parseInt("14", 13);
parseInt("15", 14);
parseInt("16", 15);

(全文完)

52
0
分享到:
评论
3 楼 p2227 2014-02-19  
现在iteye支持markdown啊
2 楼 yu633 2014-02-19  
格式真漂亮,看下来思路非常清晰。
1 楼 mfkvfn 2014-02-19  
15/36 

相关推荐

    Java™ Puzzlers: Traps, Pitfalls, and Corner Cases.chm

    Puzzle 3: Long Division Puzzle 4: It's Elementary Puzzle 5: The Joy of Hex Puzzle 6: Multicast Puzzle 7: Swap Meat Puzzle 8: Dos Equis Puzzle 9: Tweedledum Puzzle 10: Tweedledee Chapter 3. Puzzlers ...

    Java解惑(谜题)CHM中英文双版本

    谜题3:长整除 谜题4:初级问题 谜题5:十六进制的趣事 谜题6:多重转型 谜题7:互换内容 谜题8:Dos Equis 谜题9:半斤 谜题10:八两 Java谜题2——字符谜题 谜题11:最后的笑声 谜题12:ABC 谜题13:...

    JavaSIG-Puzzlers.pdf

    《JavaSIG-Puzzlers.pdf》是一本由Joshua Bloch与Neal Gafter共同编写的书籍,该书主要探讨了Java编程语言中的一些非常规问题。这些问题是开发者在日常工作中可能会遇到但又容易忽视的陷阱或难点。书中通过一系列的...

    Java Puzzlers 中文版(Java解惑)

    Java Puzzlers 中文版(Java...为什么是四分之一?因为在所有的 int 数值中,有一半都是负数,而 isOdd 方 法对于对所有负奇数的判断都会失败。在任何负整数上调用该方法都回返回 false ,不管该整数是偶数还是奇数。

    JAVA PUZZLERS(JAVA 解惑)中英文

    6. **代码分析**:每个谜题后都有详细的解答,分析了代码为什么会得到预期之外的结果,并解释了Java语言背后的工作原理。 7. **阅读体验**:无论你是初级程序员还是经验丰富的开发者,《JAVA PUZZLERS》都能提供新...

    Addison.Wesley.Professional.Java.Puzzlers.Traps.Pitfalls.and.Corner.Cases

    Java™ Puzzlers features 95 diabolical puzzles that educate and entertain. Anyone with a working knowledge of Java will understand the puzzles, but even the most seasoned veteran will find them ...

    Addison.Wesley.Java.Puzzlers.Jun.2005

    《Addison.Wesley.Java.Puzzlers.Jun.2005》一书由Joshua Bloch和Neal Gafter联合编著,由Pearson Education, Inc.于2005年出版。这本书的标题《Java Puzzlers: Traps, Pitfalls, and Corner Cases》揭示了其内容...

    Golang_Puzzlers.zip_Go_ Go_ Go!_golang_go语言核心36讲

    1. **Go语言基础**:Go语言是由Google开发的一种静态类型的、编译型的、并发型的、垃圾回收的语言。它的语法简洁明了,易于学习,同时提供了丰富的内置类型和控制结构,如接口和切片。 2. **并发模型**:Go语言的一...

    Java解惑(中文版)

    puzzle 3:long pision puzzle 4:it s elementary puzzle 5:the joy of hex puzzle 6:multicast puzzle 7:swap meat puzzle 8:dos equis puzzle 9:tweedildum puzzle 10:tweedledee 3 przzlers with ...

    Java Puzzlers完整中文版

    尽管在一个方法声明中看到一个 throws 子句是很常见的,但是在构造器的声明 中看到一个 throws 子句就很少见了。下面的程序就有这样的一个声明。那么, 它将打印出什么呢? public class Reluctant { private ...

    java puzzlers 中文版.doc

    java puzzlers 中文版

    kotlin-puzzlers,科特林拼图集.zip

    《Kotlin Puzzlers:揭秘科特林编程的奇妙世界》 Kotlin,这款由JetBrains公司开发的现代化编程语言,自2011年诞生以来,已经在全球范围内获得了广泛的认可和应用。作为对Java语言的补充和改进,Kotlin在设计时就...

    java-puzzlers源代码

    这本书通过一系列精心设计的编程谜题(即"puzzlers"),帮助读者深入理解Java语言的内在机制和陷阱。源代码是书中各个谜题的实现,对于学习和理解Java的微妙之处非常有帮助。 首先,我们来看"java-puzzlers"这个...

    Java puzzlers(java 解惑)附源码

    总之,《Java Puzzlers》结合源码,为我们提供了一个深入了解Java语言特性和陷阱的宝贵资源。通过对这些puzzlers的解密,我们不仅可以避免编程中的常见错误,还能提升我们的编程技能和代码质量。阅读这本书,不仅是...

    Java+Puzzlers(中英文并且带源码)

    《Java+Puzzlers》是一本深受欢迎的Java编程书籍,它主要探讨了在Java编程中容易被忽视或误解的一些小细节。这本书分为中文和英文两个版本,同时附带源代码,帮助读者深入理解问题并进行实践。"Puzzlers"在这里指的...

    java puzzlers code

    如果你认为将一个对象的引用设置为null就能立即释放其占用的内存,那么你可能就掉入了陷阱。在Java的垃圾回收机制中,对象是否可达是由一系列复杂的规则决定的。只有当对象不可达时,垃圾回收器才会回收其内存。因此...

    Android代码-kotlin-puzzlers

    Kotlin is a pragmatic programming languages that was designed to avoid many well-known Java Puzzlers. This is a collection that I have found so far. What is a puzzler? A puzzler is some code that ...

    <好书>java解惑(java puzzlers),过来挑战吧

    每个puzzler都会引导读者思考为什么这段代码会以某种意想不到的方式运行,然后提供详细的解答,解释背后的语言原理。这些谜题涵盖了从基本语法到高级特性的各种主题,包括但不限于类型转换、对象引用、内存管理、多...

Global site tag (gtag.js) - Google Analytics