面试题:['1', '2', '3'].map(parseInt) 输出什么?

配图源自 Freepik

一、背景

事情经过是这样的,前几天上班路上,跟往常一样拿起手机看头条、逛知乎、刷掘金嘛。

过程中,看到以下这个面试题:

['1', '2', '3'].map(parseInt) 输出什么?

其实,这题很简单,不就是类似 [0, 1, 2].filter(Boolean) 这种变形题目嘛,但我却没能马上说出答案。

我知道 parseInt() 的第二个参数跟进制数相关,但由于平常多数是缺省第二个参数,平常写项目也会启用 ESLint 的 radix 规则,但规则启用时也几乎是填写 10 作为实参,因为涉及其他进制数的情况确实很少很少...

所以,趁机再熟悉下 parseInt(string, radix) 这个函数,也是挺不错的。

回到上面的题目,分解一下,就是返回以下三个运算结果组成的数组嘛:

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

对于 parseInt() 函数,仅接收两个参数,所以第三个参数实际上没有任何作用,因此 ['1', '2', '3'].map(parseInt) 结果就是:

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

但这篇文章的重点并非是答案,我们应该关注 parseInt(string, radix) 函数本身。

二、八进制数表示法的前世今生

如果常用 ESLint 的同学,应该知道它有一个规则 radix 是跟 parseInt() 相关的。

看个例子,它们分别打印什么结果?

parseInt('071') // 57
parseInt('071', 10) // 71

有些本着求真的同学,将 parseInt('071') 拷到控制台发现,无论是 Chrome、Firefox 还是 Safari 都是打印出 71 而不是 57

我为什么写成 57 呢?是写错了吗?明明在浏览器中 parseInt('071') 都是打印出 71 呢!

先别急,我们知道在「严格模式」下,是不允许使用以 0 开头的八进制语法的。

"use strict"
var n = 071 // SyntaxError: Octal literals are not allowed in strict mode.

但我想,你有可能不知道的是,其实在 ES6 标准发布之前,ECMAScript 是没有八进制语法的,至于类似 071 这种八进制表示法它只是被所有浏览器厂商支持罢了。就好比如 Object.prototype.__proto__ 从来就不是 ECMAScript 的标准语法一样,但所有浏览器都支持罢了,标准语法是 Object.getPrototypeOf()

在 ES6 中提供了八进制数的标准规范:在数字前加上 0o 来表示八进制数,比如八进制的 710o71 表示。

回到 parseInt(string, radix) 与八进制的话题上,

当没有指定 radix 参数时,看看各家是如何解析八进制数的?

  • ES3「不提倡」将带有 0 开头的数值字符串解析为一个八进制。(不赞成,但没禁止)
  • ES5 规范中「不允许」parseInt 函数的实现环境把带有 0 开头的数值字符串解析为八进制数值,而是以 10 为基数(即十进制)进行解析。(规范禁止了,但浏览器没有按标准实现)
  • 各浏览器厂商大爷们:我偏不按你规范去实现,仍要把带有 0 开头的数值字符串解析成八进制数值。(我行我素)

本着求真的态度,拿出了上古神器去验证并得出结果:在 IE8 及以下浏览器 parseInt('071') 打印结果为 57(下图),而 IE9 及以上则为 71

Internet Explorer 8

随着 JavaScript 的飞速发展,浏览器厂商们都向标准靠近了,不再肆意我行我素了。至于浏览器 parseInt('071') 打印结果是 71,原因正是现在的浏览器 JS 引擎是以 10 为基数进行解析了。

尽管 2022 年了,但仍要兼容旧版(远古)浏览器,所以显式指定 radix 参数是非常有必要的。本节用一个比较典型的案例来说明,使用 parseInt 函数时,应当指定 radix 参数。

在 JavaScript 中,有四种进制数的表示语法:

  • 十进制:没有「前导零」的数值。
  • 二进制:以 0b0B 开头的数值。
  • 八进制:以 0o0O 开头的数值。浏览器等宿主环境也支持以「前导零」开头,且只有 0 ~ 7 的数组表示八进制数。
  • 十六进制:以 0x0X 开头的数值。

三、parseInt

语法

parseInt(string, radix)

解析一个字符串并返回指定基数的「十进制整数」或者 NaN

  • string
    被解析的值。若参数不是字符串类型,内部先隐式类型转换为字符串类型(即调用相应值的 toString() 方法)

  • radix(可选,取值范围 2 ~ 36 的整数)
    表示字符串的基数。但请注意,10 不是默认值!当参数 radix 缺省时,会有几种情况,下面会介绍。

注意点

  1. string 参数带有前导或尾随空白符(包括 \n\r\f\t\v 和空格),它们将会被忽略。换句话说,实际有意义的是第一个非空白符开始。
parseInt('  \n\r\f\t\v   11', 2) // 3,相当于 parseInt('11', 2)
  1. string 参数的「第一个非空格字符」不能转换为数字,或者当 radix < 2 || radix > 36 时,返回值为 NaN
parseInt('a11') // NaN
parseInt('11', 1) // NaN
parseInt('11', 37) // NaN

但请注意,并不是所有的字母开头的都返回 NaN。比如 parseInt('a11', 12) 返回值为 1453。因为超过十进制之后,字母也可能用于表示相应的进制数的。

  1. radix 参数为 undefinednull0 或缺省(未显式指定)时,JavaScript 引擎会假定以下情况:

    1. 如果 string 是以 0x0X 开头的,那么 radix 将会假定为 16,将其余部分当作十六进制数去解析。比如 parseInt('0xf') 相当于 parseInt('f', 16),结果为 15
    2. 如果 string 是以 0 开头的,那么 radix 在不同的环境下,有可能被假定为 8(八进制)或假定为 10(十进制)。具体选择哪一种作为 radix 的值,视乎运行 JavaScript 的宿主环境(前面提到过了)。因此,在使用 parseInt 时,一定要显式指定 radix 参数。
    3. 如何 string 是以任何其他值开头,radix 会被假定为 10(十进制)。
  2. parseIntstring 的解析规则是:从第一个非空白符开始,然后一直往后面查找,若有任意一个字符不能被转换为数值就会停止,那么最终被解析的字符串就是从开始到停止的这一截字符串。

parseInt('11a', 10) // 结果为 11,被解析的字符串为 '11'
parseInt('11a', 11) // 结果为 142,被解析的字符串为 '11a',因为在十一进制里面,a 是有意义的。

所以,在使用 parseInt 处理 BigInt 类型的时候,最终的返回值总是为 Number 类型(过程中会失去精度),其中 BigInt 类型的拖尾的 n 是会被丢弃的。

parseInt(1024n, 10) // 1024
parseInt(1024n, 36) // 46732,相当于 parseInt('1024', 36)

// 注意,有别于以下这个
parseInt('1024n', 36) // 1682375

原因非常的简单,前面也提到过的。当 parseInt 的第一个参数不是 String 类型时,会调用 BigInt.prototype.toString() 方法先转换为字符串,即 1024n.toString(),结果为 1024

四、结尾

回到文章开头的题目:

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

这时候,是不是就可以快速说出答案了:[1, NaN, NaN]

借机彻底弄懂了 parseInt() 的方法,可以满意地离开了,The end.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容