【图解面试】深入解析数据类型转换

news2024/11/28 10:51:01

将值从一种数据类型转换到另一种数据类型通常称为数据类型转换。在面试过程中大多数都是以代码输出题出现,但是要了解到具体的转换规则,彻底搞懂底层原理,才能应对变来变去的值类型~

转布尔类型

Boolean类型有两个字面值: true / false (注意:区分大小写,True/False 不是有效的布尔值)

转换方式

其他数据类型转为布尔值,使用Boolean函数

  • Null || Undefined 转 Boolean
let a = null; // false
let b = undefined; // false
  • String 转 Boolean
//  String -> Boolean
let a = 'abc'; // true
let b = ''; // false
  • Number 转 Boolean
// Number -> Boolean
let n1 = 123; // true
let n2 = 12.33; // true
let n3 = +0; // false
let n4 = -0; // false
let n5 = NaN; // false
let n6 = +Infinity; // true
let n7 = -Infinity; // true
  • Object 转 Boolean
let o1 = {}; // true
let o2 = { a: 123 }; // true
let o3 = null; // false
let o4 = []; // true
let o5 = [1, 2, 3]; // true

图解执行流程

在这里插入图片描述

转数值类型

Number数值类型,可以表示八进制(以0开头)、十进制、十六进制(0x开头)的数值。

转换方式

其他数据类型转换成数值类型有三种方式:

  • Number()

  • parseInt()

  • parseFloat()

Number()

Number()函数支持将各种数据类型转换为Number,根据ECMA-262,当执行Number(value)时,主要执行了以下流程:

  1. 检测value是否存在,如果不存在的话,定义 n = 0;

  2. 如果value存在,对value执行ToNumeric(),定义 n = ToNumeric(value)的返回结果

    • value 是对象,ToNumeric 内 执行 ToPrimitive(value,NUMBER)

      • 执行对象的valueOf(),对象有该方法并且该方法返回一个原始值,将返回的原始值转换为数值类型并返回

      • 否则执行对象的toString()方法,如果该方法返回一个原始值,则将原始值转换为数值类型并返回

      • 否则:抛出TypeError错误

    • value 是原始值,ToNumeric 内 执行 ToNumber(value)

  3. 如果Number不是通过new调用的,返回上面步骤定义的变量n值;

  4. 如果Number是通过new调用的,对n进行对象封装,返回生成的对象;

原始值转Number

let n10 = Number(123.15); // 123.15
let n11 = Number(123); // 123
let n12 = Number(undefined); // NaN
let n13 = Number(null); // 0
let n14 = Number(true); // 1
let n15 = Number(false); // 0
let n16 = Number('123'); // 123
let n17 = Number('123abc'); // NaN
let n18 = Number('abc123'); // NaN
let n19 = Number('016'); // 16
let n20 = Number('0xAF'); // 175
let n21 = Number('AF'); // NaN

对象转Number

const no1 = Number({}); // NaN
const no2 = Number([]); // 0 => [].valueOf = []; [].toString() = ''; Number('') = 0;
const no3 = Number(new Date()); // 1705500007233
const no4 = Number({ a: 123 }); // NaN
const no5 = Number([1, 2, 3]); // NaN [1, 2, 3].toString = '1,2,3'; Number('1,2,3') = NaN
const no6 = Number({
  a: 123,
  valueOf() {
    return '456'
  },
  toString() {
    return '789'
  }
}); // 456 => 执行valueOf函数,返回456

const no7 = Number({
  a: 123,
  valueOf() {
    return 'Num456'
  },
  toString() {
    return '789'
  }
}); // NaN => 执行valueOf函数,返回'Num456',Number('Num456') = NaN

const no8 = Number({
  a: 123,
  valueOf() {
    return new Number()
  },
  toString() {
    return '789'
  }
}); // 789 => 执行valueOf函数,返回值为非原始值,接着执行toString(),返回789
parseInt(string,radix)

该函数根据指定的基数对字符串的内容进行转换,从而产生一个整数。

  • 前导空格会被忽略

  • 如果检测到的第一个字符不是数值、加号或者减号,直接返回NaN;!!与Number(‘’) = 0不同,parseInt(‘’)为NaN

  • 如果第一个是数值,检测到的第一个非数值字符及后面的字符就会被舍弃

let p1 = ' 123'; // 123
let p2 = '123blue'; // 123
let p3 = '0xAF'; //175
let p4 = ''; //NaN => 空字符串检测到的第一个字符为非数值字符,所以返回也是NaN
let p5 = 'blue123'; //NaN
let p6 = '123.45'; //123
let p7 = '123b123'; //123
parseFloat(string)

该函数通过将字符串参数的内容解释为十进制文字来生成一个Number值。

parseFloat的转换方式与parseInt的执行过程大致相等,但是不支持第二个参数,只能进行十进制数值转换,对于传入的16进制一直都是只会返回0(因为第二个值就是非数值了);

let p1 = ' 123'; // 123
let p2 = '123blue'; // 123
let p3 = '0xAF'; //0
let p4 = ''; //NaN => 空字符串检测到的第一个字符为非数值字符,所以返回也是NaN
let p5 = 'blue123'; //NaN
let p6 = '123.45'; //123.45
let p8 = '123.45.45'; //123.45 => 检测到的第一个小数点为有效小数点,后面的第二个小数点及后面的数据被舍弃
let p7 = '123b123'; //123

图解执行流程

在这里插入图片描述

转字符串类型

字符串类型具有不可变性,一个字符串一旦创建后就不可修改,如果想要修改某个字符串中的字符串值,必须先销毁原始的字符串后再保存到该变量中。

转换方式

toString()

几乎所有值都有的toString()方法,一般情况下toString()方法不支持参数,但是如果是数值类型调用toString方法,支持传递进制数表明转换成指定进制数的字符串;🙋‍♂️ 特别注意:null 和 undefined没有toString()

let ss1 = Symbol('abc'); // Symbol(abc)
let ss2 = Symbol(undefined); // Symbol()
let ss3 = undefined; // TypeError: Cannot read property '[object Object]' of undefined 【注意:null和undefined没有toString方法】
let ss4 = null; // TypeError: Cannot read property '[object Object]' of undefined
let ss5 = 123; // '123
let ss6 = 0; // '0'
let ss7 = Infinity; // 'Infinity'
let ss8 = true; // 'true'
let ss9 = false; // 'false'
let ss10 = [1, 2, 3]; // '1,2,3'
let ss11 = new Date(); // 'Tue Jan 30 2024 14: 46: 49 GMT +0800(中国标准时间)'
let ss12 = {
  a: 'abcd',
  valueOf() {
    return 'valueof'
  },
  toString() {
    return 'toString'
  }
}; // toString
let ss13 = {
  name: 'foo' // [object Object]
};
let ss14 = {
  a: 'abcd',
  valueOf() {
    return 'valueof'
  },
  toString() {
    return {}
  }
}; // {} 与String()不同,执行完toString,即使返回的不是原始值也会返回
[ss1, ss2, ss5, ss6, ss7, ss8, ss9, ss10, ss11, ss12, ss13, ss14].forEach(item => {
  console.log(item.toString())
})
String(value)

支持null和undefined的转换

  • 先判断value是否存在,如果不存在直接返回一个空字符串

  • 如果value存在且value是Symbol类型,返回Symbol(dec);

  • 如果value不是Symbol类型,返回对应的字符串格式

let s1 = Symbol('abc'); // Symbol(abc)
let s2 = Symbol(undefined); // Symbol()
let s3 = undefined; // 'undefined'
let s4 = null; // 'null'
let s5 = 123; // '123
let s6 = 0; // '0'
let s7 = Infinity; // 'Infinity'
let s8 = true; // 'true'
let s9 = false; // 'false'
let s10 = [1, 2, 3]; // '1,2,3'
let s11 = new Date(); // 'Tue Jan 30 2024 14: 46: 49 GMT +0800(中国标准时间)'
let s12 = {
  a: 'abcd',
  valueOf() {
    return 'valueof'
  },
  toString() {
    return 'toString'
  }
}; // toString
let s13 = {
  name: 'foo' // [object Object]
};
let s14 = {
  a: 'abcd',
  valueOf() {
    return 'valueof'
  },
  toString() {
    return {}
  }
}; // valueof
[s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14].forEach(item => {
  console.log(String(item))
})

图解执行流程

在这里插入图片描述

操作符转换

以上都是我们显示的对数据类型进行转换,但是当我们使用操作符进行一些计算时,会对值进行一些隐式转换,以下为操作符转换的一些规则

一元操作符

递增、递减、一元加/减

**转换原则:**作用于非数值类型,则会发生隐形转换,变量类型从作用值类型转换为 👉 数值类型,转换规则同【转数值类型】一致

let d1 = '12'; // 13
let d2 = 'b12'; // NaN
let d3 = true; // 2
let d4 = false; // 1
let d5 = 123.12; // 124.12
let d7 = undefined; // NaN
let d8 = null; // 1

[d1, d2, d3, d4, d5, d7, d8].forEach(item => {
  console.log(++item)
})

布尔操作符

  • 将操作数转换为布尔类型进行操作,转换规则同【转布尔类型】一致

乘性操作符

乘法、除法、取余

  • 将操作值隐形转换为数值类型,转换规则同【转数值类型】一致

加性操作符

  • 加法:如果两个操作值都是数值,则正常计算,如果有一方是字符串,则另一个也转换为字符串,然后进行字符串拼接

  • 减法:如果两个操作值都是数值,则正常计算,如果有一方不是数值,则转换为数值后进行计算

关系操作符

  • 如果都是数值,则进行数值比较

  • 如果都是字符串,则逐个比较字符对应的编码

  • 如有任意操作符是数值,则转为数值进行比较

  • 如果是对象,则先调用valueOf,在调用toString

  • 如果任意一个是布尔值,则转为数值进行比较

相等操作符

两个操作数中有至少一个为原始值

  • 如果是布尔值,转换为数值比较

  • 如果一个字符串,一个数值,则转换为数值比较

  • 如果一个是对象,则调用valueof比较

  • null 和 Undefined 相等

  • null 和 Undefined 不能转换为其他类型比较

如果两个都是对象,则看两个对象是否指向同一个空间,如果是的话则相等,否则不等

数据类型扩展

typeof

在 JavaScript 中,typeof 被设计为一种安全的方式来检查变量的类型,即使那个变量从未被声明。这样设计主要是为了便于错误处理和检查变量是否存在。如果 typeof 不能用于未声明的变量,那么在尝试确定一个可能未声明的变量的类型时,将捕获引用错误(ReferenceError)typeof 的这种特殊行为允许开发者在不引发错误的情况下检查变量的存在和类型。一般可以用于为某个缺失的功能写 polyfill。

Undefined 类型

Undefined 类型只有一个值,那就是undefined。它表示一个变量声明了但是没有初始化

let message;
// 等同于下面表达式
let message = undefined;

虽然上面两种表示的含义相同,但是在实际开发中,一般 不要 显示的给一个变量赋值为 undefined;

undefined 与 undeclared
  • undefined 表示一个变量在作用域中已经声明,但是未初始化

  • undeclared 表示一个变量在作用域中尚未声明

这两者之间存在本质的差别。

变量message在执行时,因为已经声明但是没有初始化所以会输出undefined,但是对于没有声明过的info变量来说,执行console.log就会提示引用错误信息。

对于undeclared变量唯一能执行的是typeof操作,typeof具有错误处理机制,即使变量没有被声明过返回的也是undefined

let message;

console.log(message); // undefined
console.log(info); // ReferenceError: info is not defined

console.log(typeof message); // undefined
console.log(typeof info); // undefined
如何安全的获取undefined

表达式void ___没有返回值,所以结果是undefined。如果我们确实需要一个undefined,可以使用表达式 void 0 来获取

console.log(void 0) // undefined

Null类型

Null类型也是只有一个特殊值null,表示一个空对象指针。

与undefined不同的是,我们一般不显式给一个变量赋值为undefined,但是如果我们确定一个变量之后是一个对象,我们需要显式赋值一个null,表示当前变量是一个对象类型。📢 需要注意:使用 typeof 检测数据类型时,返回的为object;

let empty = null;
console.log(typeof empty); // object
console.log(empty == message) // true

关于为什么 typeof null === ‘object’

早期 JavaScript 实现:

  • JavaScript 的第一个版本中,值是通过一种称为“标签值”(tagged value)的方式表示的。每个值由一个低位的类型标签和实际的数据组成。

  • 在这种表示法中,对象的类型标签是 0。由于 null 表示为空,它被表示为全零的指针。因此,null 的类型标签也被误解为 0,导致 typeof null 返回 'object'

为什么没有修复:

尽管这被认为是 JavaScript 的一个设计错误,但修复它会导致向后兼容性问题。很多现有的网页和 JavaScript 代码依赖于 typeof null === 'object' 这一行为。改变这一行为可能会破坏大量现有的网页和应用程序,因此这个问题一直未被修复。

Number

最大值和最小值
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
console.log(Number.MIN_VALUE); // 5e-324
console.log(Number.MAX_VALUE + 1000); // 1.7976931348623157e+308 => 准确度丢失
console.log(Number.MIN_VALUE - 1000); // -1000 => 准确度丢失
console.log(Number.MAX_VALUE + Number.MAX_VALUE); // Infinity
console.log(-(Number.MAX_VALUE + Number.MAX_VALUE)); // -Infinity
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 => 能精准计算的最大值
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991 => 能精准计算的最小值
console.log(Number.MAX_SAFE_INTEGER + 10); // 9007199254741000 => 准确度丢失
console.log(Number.MIN_SAFE_INTEGER - 10); // -9007199254741000 => 准确度丢失
NaN
  • 当一个变量值想要返回数值的操作,但是失败了时,就是返回NaN;所有NaN的后续计算都会返回NaN
console.log(+0 / 0); // NaN
console.log(-0 / 0); // NaN
console.log(3 / 0); // Infinity
console.log(-3 / 0); // -Infinity
  • NaN 依旧还是数值类型,只是它是特殊的数值。没有任何变量值等于NaN,NaN本身也不与NaN相等
console.log(typeof NaN); // number
console.log(NaN === NaN) // false
  • window.isNaN 与 Number.isNaN
console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN('abc')) // false
console.log(isNaN('abc')) // true

window.isNaN 会对传入的参数进行数据类型转换,会先将参数转换为数值类型,然后再判断是否是NaN,所以执行window.isNaN时,字符串’abc’转换为数值类型时为 NaN,在进行isNaN判断为true;

参考资料

  1. JavaScript高级程序设计(第四版)第三章:3.3、3.4、3.5
  2. 你不知道的JavaScript(中卷)第四章
  3. ECMA262标准:https://tc39.es/ecma262/#sec-tonumber
  4. ECMA262标准:https://tc39.es/ecma262/#sec-toprimitive
  5. ECMA262标准:https://tc39.es/ecma262/#sec-stringtonumber

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

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

相关文章

调整Activation Function参数对神经网络的影响

目录 介绍: 数据集: 模型一(tanh) : 模型二(relu): 模型三(sigmoid) : 模型四(多层tanh): 模型五&am…

使用“快速开始”将数据传输到新的 iPhone 或 iPad

使用“快速开始”将数据传输到新的 iPhone 或 iPad 使用 iPhone 或 iPad 自动设置你的新 iOS 设备。 使用“快速开始”的过程会同时占用两台设备,因此请务必选择在几分钟内都不需要使用当前设备的时候进行设置。 确保你当前的设备已连接到无线局域网,并…

一篇带你彻底搞懂 Python 编程进阶之闭包

前言 在Python编程语言中,闭包是强大而灵活的语法,它为开发者提供了一种优雅而高效的方式来处理函数和代码结构。作为自动化测试和测试开发同学,弄懂它的作用及工作原理很有必要,面试中提及到的概率非常之大。 关于函数名的本质 …

APT攻击是什么?如何进行防护

随着网络技术的飞速发展,APT(Advanced Persistent Threat)攻击已经成为网络安全领域的一个重大问题。APT攻击是一种高度复杂的网络攻击,其目标是长期潜伏并逐步深入到目标网络中,以窃取敏感信息、破坏关键基础设施或制…

MyBatis 的注解实现方法

MyBatis 的注解实现方法 MyBatis 的注解实现方法引入依赖添加配置创建表创建实体类创建mapper接口InsertDeleteSelectResults和ResultMap通过配置文件解决 UpdateOptions MyBatis 的注解实现方法 引入依赖 在springBoot项目中下载了EditStarters插件的,可以直接在配置文件处右…

幻兽帕鲁怎么选择服务器

想要部署属于自己的幻兽帕鲁,首先需要拥有一台服务器,服务器是幻兽帕鲁运行的基础。游戏所需的服务器取决于游戏的规模、用户数量和功能需求。以下是一些通常需要考虑的服务器要求: 计算性能:包括cpu、内存、硬盘,cpu…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之TextPicker组件

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之TextPicker组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、TextPicker组件 TextClock组件通过文本将当前系统时间显示在设备上。支持不…

C++基础语法学习笔记

C Tutorial 1.基础语法 C 应用&#xff1a;操作系统、图形用户界面和嵌入式系统 C和C区别&#xff1a;C支持类和对象 C语法 #include <iostream> using namespace std;int main(){cout << "hello world!";return 0; }int main () { cout << &q…

vue之elementUi的el-select同时获取value和label的两种方法

一、通过ref的形式&#xff08;推荐&#xff09; <template><div class"root"><el-selectref"optionRef"v-model"value"placeholder"请选择"style"width: 250px"><el-optionv-for"item in optio…

Kubernetes实战(二十三)-k8s event监控利器kube-eventer对接企微告警

1 背景 监控是保障系统稳定性的重要组成部分&#xff0c;在Kubernetes开源生态中&#xff0c;资源类的监控工具与组件监控比较多。 cAdvisor&#xff1a;kubelet内置的cAdvisor&#xff0c;监控容器资源&#xff0c;如容器cpu、内存&#xff1b;Kube-state-metrics&#xff1…

自动驾驶:Apollo如何塑造人类的未来出行

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言1. 什么是自定义指令&#xff1f;2. Apollo中的自定义指令2.1 查询中的自定…

JSON巨匠:FastJSON的序列化解析

Fastjson 简介 Fastjson 是一个 Java 库&#xff0c;可以将 Java 对象转换为 JSON 格式&#xff0c;当然它也可以将 JSON 字符串转换为 Java 对象。 Fastjson 可以操作任何 Java 对象&#xff0c;即使是一些预先存在的没有源码的对象。 Fastjson 源码地址&#xff1a;https://…

数据可视化 pycharts实现时间数据可视化

自用版 数据格式为&#xff1a; 运行效果为&#xff1a; from pyecharts import options as opts from pyecharts.charts import Polar, Page import csv filename "./hot-dog-places.csv" data_x [] data_y [] with open(filename) as f:reader csv.reade…

有向图查询所有环,非递归

图&#xff1a; 有向图查询所有环&#xff0c;非递归&#xff1a; import java.util.*;public class CycleTest {private final int V; // 顶点数private final List<List<Integer>> adjList; // 邻接表public CycleTest(int vertices) {this.V vertices;this.…

python基础——池

池的介绍&#xff1a; 提前创建进程池&#xff0c;防止创建的进程数量过多导致系统性能受到影响&#xff0c;在系统执行任务时&#xff0c;系统会使用池中已经创建进程/线程&#xff0c;从而防止资源的浪费&#xff0c;创建的进程/线程可以让多个进程使用&#xff0c;从而降低…

SW-LIMS实现化工企业危险化学品信息化管理

随着化工产业的不断发展,危险化学品的生产和使用在经济和社会发展中扮演着重要的角色。然而,危险化学品的安全隐患也同样日益突出,从危化品的生产到储存、运输和使用,如果控制不当,很容易造成安全事故,而一旦发生安全事故,不仅会造成巨大的经济损失,还将威胁到人类的生命健康与…

FFmpeg和Monibuka拉取rtsp(大华摄像头)视频流时未进行URLCode编码导致提示404等报错

场景 Monibucav4(开源流媒体服务器)在Windows上搭建rtmp服务器并实现拉取rtsp视频流以及转换flv播放&#xff1a; Monibucav4(开源流媒体服务器)在Windows上搭建rtmp服务器并实现拉取rtsp视频流以及转换flv播放_monibuca 搭建流媒体服务-CSDN博客 Nginx搭建RTMP服务器FFmpeg…

C++引用、内联函数、auto关键字介绍以及C++中无法使用NULL的原因

文章目录 一、引用1.1 引用概念1.2 引用特性1.3 常引用1.4 使用场景1.4.1 做参数1.4.2做返回值 1.5 引用和指针的区别1.6 小结一下 二、内联函数2.1 内联的概念2.2 内联的特性2.3 【面试题】 三、auto关键字(C11)3.1 类型别名思考3.2 auto简介 四、auto的使用细则4.1 基于范围的…

Aloudata 近期荣誉盘点!接连斩获技术创新、案例实践、投资价值等权威认可

近期&#xff0c;Aloudata 凭借持续的技术积累、丰富的产品与解决方案以及多样场景下的最佳实践案例&#xff0c;在数据智能技术创新、案例实践、投资价值等领域全面开花&#xff0c;接连荣获&#xff1a; 2023 金猿榜「大数据产业年度最具投资价值」企业&#xff0c;并携手首…

APP开发者对接穿山甲广告联盟,有哪些特点?收益如何?

穿山甲平台作为巨量引擎旗下的第三方广告变现平台&#xff0c;在行业内始终处于领先地位&#xff0c;是不少开发者首选的对接平台。 通过穿山甲广告GroMore的Bidding竞价能力&#xff0c;不断提升自身的变现效率&#xff0c;新手可直接上手&#xff0c;避免繁琐调优流程&#…