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

相关推荐
xuyanzhuqing2 小时前
代码共享方案-多仓库合并单仓库
前端
RaidenLiu2 小时前
Riverpod 3:重建控制的实践与性能优化指南
前端·flutter
学习中的小胖子2 小时前
React的闭包陷阱
前端
不卷的攻城狮2 小时前
【精通react】(五)react 函数时组件为什么需要 hooks?
前端
十八朵郁金香3 小时前
深入解析:ES6 中 class 与普通构造器的区别
前端·ecmascript·es6
JohnYan3 小时前
工作笔记 - 一个浏览器环境适用的类型转换工具
javascript·后端·设计模式
索迪迈科技3 小时前
CommonJS与ES6模块的区别
前端·ecmascript·es6
前端Hardy3 小时前
12个被低估的 CSS 特性,让前端开发效率翻倍!
前端·javascript·css
前端Hardy3 小时前
HTML&CSS:精美的3D折叠卡片悬停效果
前端·javascript·css