JS核心知识-数据转换

JavaScript语言的基础知识中,情况复杂,面试中容易出错,在工作中无处不在的:数据类型转换应当算一个。顾名思义就是从一种数据类型转换为另一种数据类型

JavaScript发展到目前有的数据类型: Undefined,Null, Boolean, Number, String, Symbol, BigInt, Object

由于JavaScript是动态解释性语言,因此在进行一些操作(例如+、==、!=、++等操作符或if的表达式)时,在运行时按照特定的规则进行数据转换,保证数据类型一致进行后续代码正常运行。这也被称为隐式转换 。JavaScript也提供一些API,帮助我们主动、意图清晰地进行数据转换。也称为显示转换 例如parseIntNumber()toString()String()等等。

先看看隐式转换中,各种数据类型之间的转换的。再讲解主要显示转换。开始吧!!!

隐式转换

在ECMAScript规范的7.1中提到数据在隐式转换中会按照特定抽象操作进行数据转换。

1. 所有数据类型转换为Boolean

所有数据类型转换成Boolean类型时,会通过ECMAScript规范中ToBoolean抽象操作进行转换,接收一个任何类型值的参数argument

ToBoolean抽象操作返回的情况如下:

  • argument值为Boolean类型,直接返回argument
  • argument值为undefinednull+0-0NaN空字符串,则返回false
  • 其他则返回true

⚠️注意

在ECMAScript规范7.1.2中提到,如果web浏览器支持\[IsHTMLDDA]插槽时,如果argument是一个对象且具有此插槽时,转换为Boolean类型时返回false 。目前只有document.all 具备上述条件。但是在HTML规范规定2.6.4.1中规定,当document.allnull/undefined进行==比较时为true。这个家伙确实很诡异的,幸好现在已经废弃。

js 复制代码
if(value) {
    // 进入此处前时 先调用ToBoolean(value) 如果是true则进入反之进入不了.
}
!value // 类似于!ToBoolean(value)
if (document.all) {
  // 此处不能进入
}
document.all == undefined // true
document.all == null // true

2. 所有数据类型转换为Number类型

所有数据类型转换为Number类型情况就较为复杂。ECMAScript通过ToNumber抽象操作进行转换,也接收一个任何类型值的参数argument

ToNumber抽象操作返回情况如下:

  • argument为Number类型,返回argument
  • argument为SymbolBigInt类型,抛出TypeError异常
  • argument为undefined,返回NaN
  • argument为nullfalse,返回 0
  • argument为true,返回 1
  • argument为String类型时,较为复杂,情况如下:
    • 空字符串(例如'', ' '), 返回 0
    • 清空前后空格后为纯数字的字符串,返回对应数字
    • 字符串中包含非数字字符,返回NaN
    • 其他进制(例如八进制、十六进制特定格式)的字符串,转为十进制的数字
    • 'Infinity' 或者 '-Infinity'转换为 Infinity / -Infinity
  • argument为Object类型时,需要通过ToPrimitive(argument, NUMBER)抽象操作转化为原始值,然后ToNumber规则进行转换。

ToPrimitive抽象操作

ToPrimitive抽象操作接受两个参数:

  • argument:需要转换原始类型的值
  • preferredType: 需要转换的合适类型,可选的参数值:default、string、number

ToPrimitive按照preferredType的值不同处理方式也不同,情况如下:

  • 当preferredType为string时,先调用对象的toString方法,如转换结果不是原始值类型,再调用对象的valueOf方法;如还不是原始值,则抛出异常 Uncaught TypeError Cannot convert object to primitive value
  • 当preferredType为number时,先调用对象的valueOf方法,如转换结果不是原始值类型,再调用对象的toString方法;如还不是原始值,则抛出异常 Uncaught TypeError Cannot convert object to primitive value
  • 当preferredType为default时,按照preferredType为number处理

原始值转换示例:

js 复制代码
+Symbol('number') // Uncaught TypeError: Cannot convert a Symbol value to a number.
+BigInt(1) // Uncaught TypeError: Cannot convert a BigInt value to a number.
+undefined // NaN
+null // 0
+' ' // 0
1 + false // 1
1 + true // 2
+'123' //  123
+'0o123' // 83
+'0x11' // 17
+'12abc' // NaN
+'Infinity' // Infinity 

对象转换示例

js 复制代码
const counter = {
  valueOf() {
    console.log("valueOf");

    return {};
  },
  toString() {
    console.log("toString");
    return "42";
  },
};
console.log(+counter); // "428"
counter.valueOf = function () {
  console.log("new valueOf");
  return "43";
};
counter.toString = function () {
  console.log("new toString");
  return "42";
};
console.log(+counter); 
/* 依次打印:
valueOf
toString
42
new valueOf
43
*/
+[] // 0 先调用valueOf() 返回数组对象 然后调用toString()返回'' 空字符串按照原始值转换处理为0
+[2] // 2 先调用valueOf() 返回数组对象[2] 然后调用toString()返回'2' 按照原始值转换处理为2
+[1,2] // NaN 先调用valueOf() 返回数组对象[1,2] 然后调用toString()返回'1,2' 按照原始值转换处理为NaN

3. 所有类型转换为String类型

所有数据类型转换为String类型整个流程与转换为Number类型类似。ECMAScript通过ToString抽象操作进行转换,也接收一个任何类型值的参数argument

ToString抽象操作返回情况如下:

  • argument是String类型,返回argument
  • arugment是Symbol类型,抛出TypeError异常
  • argument是nullundefined,返回 null / undefined
  • argument是Boolean类型,返回true / false
  • argument是Number类型,返回转换成十进制的数字字符串
  • argument是BigInt类型,返回转换成十进制的数字字符串
  • argument是Object类型,需要通过[ToPrimitive(argument, String)](#ToPrimitive(argument, String) "#toPrimitive")抽象操作转化为原始值,然后ToString规则进行转换。

显示转换

所有类型转换为Boolean类型

显示转换Boolean类型,主要通过Boolean函数进行转换。它的底层就是调用ToBoolean进行转换的。

js 复制代码
Boolean(Symbol('foo')); // true
Boolean(' '); // true
Boolean(''); // false
Boolean(null); // false
Boolean(undefined); // false
Boolean(NaN); // false
Boolean(0); // false
Boolean(document.all); // false

所有类型转换为Number类型

显示转换为Number类型主要有以下函数:NumberparseIntparsetFloat

Number函数

Number函数的实现与隐式转换略有不同,如果是BigInt类型,按照特定的规则进行转换,如果数值超出了Number类型的最大/最小范围的话,将返回一个「不准确」的值。

js 复制代码
Number(Symbol('foo')); // Uncaught TypeError: Cannot convert a Symbol value to a number
Number(' '); // 0
Number(false); // 0
Number(true); // 1
Number(0x11); // 17
Number(0o123); // 83
Number(BigInt(123)); // 123 如果是隐式转换则会抛出异常。
Number([]); // 0
Number({}); // NaN

parseInt/parseFloat函数

parseInt和parseFloat函数主要是对字符串转换为数字的函数,它们的规则是一致的,按照parseInt为例讲解。 parseInt函数接收两个参数:

    1. string: 必须是字符串类型。如果不是,将通过ToString抽象操作转换为String类型。string字符串从左侧第一个字符开始查找,查找出符合radix进制的值,如果遇到不符合的直接结束查找。不管后面是否有符合的字符。找到的内容按照radix的进制转换为十进制。
    1. radix: 可选参数,表示进制的基数。不传或为0,radix的值为10(特殊情况下,如果字符串以0x开头,radix值默认为16;如果以0o/0开头,radix值默认为8)。值的返回为2~36.如果超出范围,函数返回NaN。
js 复制代码
let arr = [27.2, 0, '0013', '14px', 123]
arr = arr.map(parseInt)
console.log(arr) // [27, NaN, 1, 1, 27]
/*
arr.map(parseInt) 依次执行
parseInt(27.2, 0) => parseInt('27', 10) => 27
parseInt(0, 1) => 1不在2~36的返回因此输出NaN
parseInt('0013', 2) => parseInt('001', 2) => 0*2^2+0*2^1+1*2^0 => 0 + 0 + 1 => 1 按权展开求和
parseInt('14px', 3) => parseInt('1', 3) => 1*3^0 => 1
parseInt(123, 4) => parseInt('123', 4) => 1*4^2+2*4^1+3*4^0 => 16 + 8 + 3 => 27
因此可见
arr = [27, NaN, 1, 1, 27]
*/

如果将parseInt('0013', 2)改为parseInt(0013, 2)结果为3

js 复制代码
parseInt(0013, 2) // 3
/*
  1. 0013按照8转换为10进制
    0*8^3+0*8^2+1*8^1+3*8^0 => 0 + 0 + 8 + 3 => 11
  2. parseInt(11, 2)
    1*2^1 + 1*2^0 => 2 + 1 => 3
  因此输出3
*/

所有类型转换为String类型

显示转换为String类型。主要有以下几种方式:String(value)、对象的toString方法。

String()

ECMAScript在底层实现String函数时,对Symbol类型进行处理,它返回调用SymbolDescriptiveString抽象操作的结果。不会像隐式转换那样抛出异常。其他类型按照ToString抽象操作处理。

js 复制代码
String({}); // '[object Object]'
String(Symbol('foo')); // 'Symbol(foo)' 隐式转换则会抛出异常。
String([]); // ''
String(false); // 'false'
String(null); // 'null'
String(undefined); // 'undefined'
String(123n); // '123'
String('0o123'); // '83'
String('0x11'); // '17'
String(123); // '123'

Object.toString()

处理null和undefined之外,原始值都有对应的构造函数,都具备toString属性,对象默认具有toString方法。自定义对象可以重写此方法。

js 复制代码
// 原始值之所以可以直接调用方法,运行时会自动装包和拆包, 
false.toString() // 'false' 相当于 Boolean(false).toString()
Symbol('foo').toString(); // "Symbol('foo')"
BigInt(1).toString(); // '1'
// 数字却不能这样
1.toSring() // 报错
var a = 1;
a.toString(); // '1'
null.toString() // 报错
undefined.toString() // 报错

总结

此文章主要讲述了JavaScript中核心知识-数据类型转换。数据类型转换分为隐式转换和显示转换。 以ECMAScript的规范从底层原理的方式讲解了各种数据类型直接转换的情况。JavaScript的核心知识还有许多。例如执行上下文、作用域和作用域链、this的指向、闭包等。下一期将讲解执行上下文的底层原理。ヾ( ̄▽ ̄)ByeBye

相关推荐
weixin_382395237 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__7 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.8 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~9 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华10 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子12 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅12 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni13 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学14 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端