细节决定成败,聊聊JS的类型(下)

news2024/12/26 3:07:11

讲完了基本类型,我们来介绍一个现象:类型转换。

因为 JS 是弱类型语言,所以类型转换发生非常频繁,大部分我们熟悉的运算都会先进行类型转换。大部分类型转换符合人类的直觉,但是如果我们不去理解类型转换的严格定义,很容易造成一些代码中的判断失误。

其中最为臭名昭著的是 JavaScript 中的“ == ”运算,因为试图实现跨类型的比较,它的规则复杂到几乎没人可以记住。

这里我们当然也不打算讲解 == 的规则,它属于设计失误,并非语言中有价值的部分,很多实践中推荐禁止使用“ ==”,而要求程序员进行显式地类型转换后,用 === 比较。

其它运算,如加减乘除大于小于,也都会涉及类型转换。幸好的是,实际上大部分类型转换规则是非常简单的,如下表所示:

在这个里面,较为复杂的部分是 Number 和 String 之间的转换,以及对象跟基本类型之间的转换。我们分别来看一看这几种转换的规则。

StringToNumber

字符串到数字的类型转换,存在一个语法结构,类型转换支持十进制、二进制、八进制和十六进制,比如:

  • 30;

  • 0b111;

  • 0o13;

  • 0xFF。

此外,JavaScript 支持的字符串语法还包括正负号科学计数法,可以使用大写或者小写的 e 来表示:

  • 1e3;

  • -1e-2。

需要注意的是,parseInt 和 parseFloat 并不使用这个转换,所以支持的语法跟这里不尽相同。

在不传入第二个参数的情况下,parseInt 只支持 16 进制前缀“0x”,而且会忽略非数字字符,也不支持科学计数法。

在一些古老的浏览器环境中,parseInt 还支持 0 开头的数字作为 8 进制前缀,这是很多错误的来源。所以在任何环境下,都建议传入 parseInt 的第二个参数,而 parseFloat 则直接把原字符串作为十进制来解析,它不会引入任何的其他进制。

多数情况下,Number 是比 parseInt 和 parseFloat 更好的选择。

NumberToString

在较小的范围内,数字到字符串的转换是完全符合你直觉的十进制表示。当 Number 绝对值较大或者较小时,字符串表示则是使用科学计数法表示的。这个算法细节繁多,我们从感性的角度认识,它其实就是保证了产生的字符串不会过长。

具体的算法,你可以去参考 JavaScript 的语言标准。由于这个部分内容,我觉得在日常开发中很少用到,所以这里我就不去详细地讲解了。

装箱转换

每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。

前文提到,全局的 Symbol 函数无法使用 new 来调用,但我们仍可以利用装箱机制来得到一个 Symbol 对象,我们可以利用一个函数的 call 方法来强迫产生装箱。

我们定义一个函数,函数里面只有 return this,然后我们调用函数的 call 方法到一个 Symbol 类型的值上,这样就会产生一个 symbolObject。

我们可以用 console.log 看一下这个东西的 type of,它的值是 object,我们使用 symbolObject instanceof 可以看到,它是 Symbol 这个类的实例,我们找它的 constructor 也是等于 Symbol 的,所以我们无论从哪个角度看,它都是 Symbol 装箱过的对象:

1

2

3

4

var symbolObject = (function(){ return this; }).call(Symbol("a"));

console.log(typeof symbolObject); //object

console.log(symbolObject instanceof Symbol); //true

console.log(symbolObject.constructor == Symbol); //true

装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。

使用内置的 Object 函数,我们可以在 JavaScript 代码中显式调用装箱能力。

var symbolObject = Object(Symbol("a"));
console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true

 每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取:

1

2

var symbolObject = Object(Symbol("a"));

console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

在 JavaScript 中,没有任何方法可以更改私有的 Class 属性,因此 Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。

但需要注意的是,call 本身会产生装箱操作,所以需要配合 typeof 来区分基本类型还是对象类型。

拆箱转换

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。

对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。

拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。

var o = {
    valueOf : () => {console.log("valueOf"); return {}},
    toString : () => {console.log("toString"); return {}}
}
o * 2
// valueOf
// toString
// TypeError

我们定义了一个对象 o,o 有 valueOf 和 toString 两个方法,这两个方法都返回一个对象,然后我们进行 o*2 这个运算的时候,你会看见先执行了 valueOf,接下来是 toString,最后抛出了一个 TypeError,这就说明了这个拆箱转换失败了。

到 String 的拆箱转换会优先调用 toString。我们把刚才的运算从 o*2 换成 String(o),那么你会看到调用顺序就变了。

1

2

3

4

5

6

7

8

var o = {

    valueOf : () => {console.log("valueOf"); return {}},

    toString : () => {console.log("toString"); return {}}

}

String(o)

// toString

// valueOf

// TypeError

在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。

1

2

3

4

5

6

7

8

var o = {

    valueOf : () => {console.log("valueOf"); return {}},

    toString : () => {console.log("toString"); return {}}

}

o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}

console.log(o + "")

// toPrimitive

// hello

结语

在本篇文章中,我们介绍了 JavaScript 运行时的类型系统。这里回顾一下今天讲解的知识点。
除了这七种语言类型,还有一些语言的实现者更关心的规范类型。

  • List 和 Record: 用于描述函数传参过程。

  • Set:主要用于解释字符集等。

  • Completion Record:用于描述异常、跳出等语句执行过程。

  • Reference:用于描述对象属性访问、delete 等。

  • Property Descriptor:用于描述对象的属性。

  • Lexical Environment 和 Environment Record:用于描述变量和作用域。

  • Data Block:用于描述二进制数据。

有一个说法是:程序 = 算法 + 数据结构,运行时类型包含了所有 JavaScript 执行时所需要的数据结构的定义,所以我们要对它格外重视。

最后我们留一个实践问题,如果我们不用原生的 Number 和 parseInt,用 JavaScript 代码实现 String 到 Number 的转换,该怎么做呢?请你把自己的代码留言给我吧!

更多补充内容请看:开发者网站

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/741015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

牛客练习赛113

A小红的基环树 题目描述 定义基环树为n个节点、n条边的、没有自环和重边的无向连通图。 定义一个图的直径是任意两点最短路的最大值。 小红想知道,n个节点构成的所有基环树中,最小的直径是多少? 思路分析 特判n3时为1,其他时候都…

行业追踪,2023-07-11,新增加 rps50 排名,汽车零部件回落 10 日均线,直接反弹

自动复盘 2023-07-11 成交额超过 100 亿 排名靠前,macd柱由绿转红 成交量要大于均线 有必要给每个行业加一个上级的归类,这样更能体现主流方向 rps 有时候比较滞后,但不少是欲杨先抑, 应该持续跟踪,等 macd 反转时参与…

Stable Diffusion - 提示词翻译插件 sd-webui-prompt-all-in-one

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/131649921 sd-webui-prompt-all-in-one 是一个基于 stable-diffusion-webui 的扩展,目的是提高 提示词/反向提示词 输入框的使用体验&…

汽车应用级MS35774/MS35774A低噪声 256 细分微步进电机驱动

MS35774/MS35774A 是一款高精度、低噪声的两相步进 电机驱动芯片,芯片内置功率MOSFET ,长时间工作的平均电 流可以达到 1.4A ,峰值电流 2A 。芯片集成了过温保护、欠压 保护、过流保护、短地保护、短电源保护功能。 主要特点 ◼ 2 相步进…

【python】Excel文件的插入操作

上篇文章写了pandas读取Excel文件的操作,但实际应用中,我们还需要对Excel指定单元格进行写入操作。 pandas模块写入 pandas模块方法写入,简单粗暴,就是把整个Excel文件重写 import pandas as pdfile_path 测试用例.xlsx # 读取Ex…

【Java基础】符号位、原码、补码、反码、位逻辑运算符、位移运算符、复合位赋值运算符

文章目录 前言&#xff1a;符号位、原码、补码、反码1.是什么2.各种码转换 一.二进制高低位1.1.什么是高低位1.2.高低位交换 二.位逻辑运算符位与运算 &位或运算 |异或运算 ^取反运算 ~ 三.位移运算符左位移运算符 <<右位移运算符 >>1.正数右移&#xff1a;2.负…

什么是QPS,什么是RT

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;什么是QPS&#xff0c;什么是RT ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你的加…

生产慢查询问题分析

1.问题描述 7月1日零点set_24出现大量慢查询告警&#xff0c;经DBA定位为子系统涉及的一条查询语句出现慢查询&#xff0c;引起set_24的cpu使用率突增&#xff0c;触发大量告警&#xff0c;查看生产执行计划发现慢查询为索引跳变引起&#xff1b;具体出现问题的sql语句如下&am…

【裸机开发】I2C 通信接口(三)—— I2C 底层驱动接口实现

目录 一、I2C 初始化 二、产生开始 / 停止信号 1、开始信号 2、重复开始信号 3、停止信号 三、向总线上发送数据&#xff08;阻塞模式&#xff09; 四、从总线上读取数据&#xff08;阻塞模式&#xff09; 五、整合&#xff1a;数据读写统一调用接口 一、I2C 初始化 初…

springboot驾校管理系统

框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 …

springboot+dynamic-datasource实现多数据源动态切换,非@DS注解方式

springbootdynamic-datasource实现多数据源动态切换&#xff0c;非注解 一、前言二、方案思路三、代码实现 一、前言 最近在分析SaaS平台多租户的功能&#xff0c;必然涉及数据库部分的功能&#xff0c;多租户的设计方案要考虑租户隔离数据和租户共享数据&#xff0c;共享数据…

concrt140.dll怎么修复?哪种修复方法更值得推荐

运行软件的时候&#xff0c;计算提示找不到concrt140.dll&#xff0c;第一次遇到的小伙伴肯定不知道是什么意思。concrt140.dll是Microsoft Visual C Redistributable for Visual Studio 2015的一部分。它是一个动态链接库文件&#xff0c;包含在Windows操作系统中。这个文件主…

【Python】瓶装液位检测系统

文章目录 概要效果图整体架构流程技术细节 概要 本代码是一个简单的GUI应用程序&#xff0c;用于瓶装液位检测系统。 效果图 整体架构流程 整体架构流程如下&#xff1a; 创建GUI窗口和必要的部件&#xff1a; 创建一个主窗口&#xff08;root&#xff09;作为GUI应用程序的…

分享一套开源充电桩云平台(v2.5.1)-- 支持二轮(电动自行车)、四轮(电动汽车)

开源充电桩云平台&#xff08;v2.5.1&#xff09; 支持二轮(电动自行车)、四轮(电动汽车) 后台体验地址 二轮后台体验地址&#xff0c;star star &#xff1a; 点我访问 四轮后台体验地址&#xff0c;star star &#xff1a; 点我访问 用户端二维码 公众号二维码 小程序二维…

攻防世界-WEB2

代码审计 首先进行代码审计 <?php $miwen"a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";function encode($str){$_ostrrev($str);// echo $_o;for($_00;$_0<strlen($_o);$_0){$_csubstr($_o,$_0,1);$__ord($_c)1;$_cchr($__);$_$_.$_c; …

2023- itwangyang - mac mysql 终端启动命令

在mac上使用mysql终端进行操作时&#xff0c;需要先启动mysql服务。以下是启动mysql服务的命令&#xff1a; sudo /usr/local/mysql/support-files/mysql.server start执行该命令后&#xff0c;会出现一些提示信息&#xff0c;等待一段时间后mysql服务就启动成功了。 接下来&…

【Java】面向对象基础 之 继承

一、继承 在前面的章节中&#xff0c;我们已经定义了Person类&#xff1a; class Person {private String name;private int age;public String getName() {...}public void setName(String name) {...}public int getAge() {...}public void setAge(int age) {...} }现在&am…

git报错:remote: Access denied (URL 403)

git报错&#xff1a;remote: Access denied fatal: unable to access ‘ https:/ /gitee. cohe requested URL 403 大概的原因&#xff0c;是之前更改了 可能因为我之前在git bash中配过ssh&#xff0c;系统已经将指向git的用户设置了别的位置&#xff0c;所以…

DAY44:动态规划(四)整数拆分(递归+DP递推都可以做,注意区别和理解)

文章目录 343.整数拆分思路1&#xff1a;递归法&#xff08;最直观的想法&#xff09;递归思路普通递归写法注意点&#xff1a;max的嵌套普通递归存在的问题 记忆化搜索递归写法时间复杂度 递归解法总结 思路2&#xff1a;动态规划&#xff08;注意递推的理解&#xff09;确认D…

Transformer 模型详解

Transformer模型 https://blog.csdn.net/m0_67084346/article/details/128138486 https://blog.csdn.net/benzhujie1245com/article/details/117173090 2017 年&#xff0c;Google 在论文 Attention is All you need 中提出了 Transformer 模型&#xff0c;其使用 Self-Atten…