JavaScript数据类型和转换

原创
2020/08/09 01:22
阅读数 433

JavaScript 是一种多范式的动态语言,它包含类型、运算符、标准内置(built-in)对象和方法。它的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。

数据类型

JavaScript 是一种有着动态类型的动态语言。JavaScript 中的变量与任何特定值类型没有任何关联,并且任何变量都可以分配(重新分配)所有类型的值。

let str = 'Hello World!'
str = 123

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 七种基本数据类型:
    • null:空,表示空对象的特殊关键字,JavaScript 大小写敏感的,因此 nullNullNULL或变体完全不同。
    • undefined:未定义,和 null 一样是特殊关键字,表示变量未赋值时的属性。
    • Boolean:布尔,有 2 个值分别是:true 和 false。
    • Number:数字,包括整数或浮点数。
    • String:字符串,一串表示文本值的字符序列。
    • Symbol:符号,表示一种实例是唯一且不可改变的数据类型(ECMAScript 2015 新增类型)。
    • BigInt:任意精度的整数,可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。
  • 以及对象(Object):它用于存储各种键值集合和更复杂的实体。

虽然这些数据类型相对来说比较少,但是通过他们你可以在程序中开发有用的功能。对象和函数是这门语言的另外两个基本元素。你可以把对象当作存放值的一个命名容器,然后将函数当作你的程序能够执行的步骤。

  • Function:函数,每个 JavaScript 函数实际上都是一个 Function 对象。
  • Array:数组,一个有序的元素集合,通过索引(index)来访问元素。
  • DateRegExpErrorMathMapSet等标准内置对象。

原始值

除了 Object 以外,所有类型都定义了表示在语言最低层面的不可变值,我们将这些值称为原始值。除了 null,所有原始类型都可以使用 typeof 运算符测试。typeof null 返回 object,因此必须使用 === null 来测试 null

除了 nullundefined,所有原始类型都有它们相应的对象包装类型,这为处理原始值提供可用的方法。当在原始值访问属性时,JavaScript 会自动将值包装到相应的包装对象中,并访问对象上的属性。然而,在 nullundefined 访问属性时,会抛出 TypeError 异常,这需要采用可选链运算符。

原始值 typeof返回值 包装对象
Null "object" N/A
Undefined "undefined" N/A
Boolean "boolean" Boolean
Number "number" Number
String "string" String
BigInt "bigint" BigInt
Symbol "symbol" Symbol

Null 类型

Null 类型只有一个值:null

Undefined 类型

Undefined 类型只有一个值:undefined。从概念上讲,undefined 表示没有任何值,null 表示没有任何对象(这也可以构成 typeof null === "object" 的接口)。当某些东西没有值时,该语言通常默认为 undefined

  • 没有值(return;)的 return 语句,隐式返回 undefined
  • 访问不存在的对象属性(obj.iDontExist),返回 undefined
  • 变量声明时没有初始化(let x;),隐式初始化为 undefined
  • 许多如 Array.prototype.find()Map.prototype.get() 的方法,当没有发现元素时,返回 undefined

null 是一个关键字,但是 undefined 是一个普通的标识符,恰好是一个全局属性。在实践中,这两个差异很小,因为 undefined 不应该被重新定义或者遮蔽。

Boolean 类型

Boolean 类型表示一个逻辑实体并且包括两个值:truefalse

Number 类型

Number 类型是一种基于 IEEE 754 标准的双精度 6* 位二进制格式的值。它能够存储 \(2^{-1074}\)(Number.MIN_VALUE)和 \(2^{1024}\)(Number.MAX_VALUE)之间的正浮点数,以及 \(-2^{-1074}\)\(-2^{1024}\) 之间的负浮点数,但是它仅能安全地存储在 \(-(2^{53} − 1)\)(Number.MIN_SAFE_INTEGER)到 \(2^{53} − 1\)(Number.MAX_SAFE_INTEGER)范围内的整数。超出这个范围,JavaScript 将不能安全地表示整数;相反,它们将由双精度浮点近似表示。

±(\(2^{-1074}\)\(2^{1024}\)) 范围之外的值会自动转换:

  • 大于 Number.MAX_VALUE 的正值被转换为 +Infinity。
  • 小于 Number.MIN_VALUE 的正值被转换为 +0。
  • 小于 -Number.MAX_VALUE 的负值被转换为 -Infinity。
  • 大于 -Number.MIN_VALUE 的负值被转换为 -0。

NaN(“Not a Number”)是一个特殊种类的数值,当算术运算的结果不表示数值时,通常会遇到它。它也是 JavaScript 中唯一不等于自身的值。

BigInt 类型

BigInt 类型在 Javascript 中是一个数字的原始值,它可以表示任意大小的整数。使用 BigInt,你可以安全地存储和操作巨大的整数,甚至超过 Number 的安全整数限制(Number.MAX_SAFE_INTEGER)。BigInt 是通过将 n 附加到整数末尾或调用 BigInt() 函数来创建的。

String 类型

String 类型表示文本数据并编码为 UTF-16 代码单位的 16 位无符号整数值序列。字符串中的每个元素在字符串中占据一个位置。第一个元素的索引为 0,下一个是索引 1,依此类推。字符串的长度是它的元素的数量。

  • 使用 substring() 获取原始的子字符串。
  • 使用串联运算符(+)或 concat() 将两个字符串串联。

表示文本数据时候推荐使用字符串。当需要表示复杂的数据时,使用字符串解析并使用适当的抽象。

Symbol 类型

Symbol 是唯一并且不可变的原始值并且可以用来作为对象属性的键(如下)。在某些程序语言当中,Symbol 也被称作“原子类型”(atom)。symbol 的目的是去创建一个唯一属性键,保证不会与其他代码中的键产生冲突。

数字转换为字符串

  • 模板字符串:`${x}` 为嵌入的表达式执行上面的字符串强制转换步骤。
  • String() 函数:String(x) 使用相同的算法去转换 x,只是 Symbol 不会抛出 TypeError,而是返回 "Symbol(description)",其中 description 是对 Symbol 的描述。
  • 使用 + 运算符:"" + x 将其操作数强制转为原始值,而不是字符串,并且对于某些对象,其行为与普通字符串强制转换完全不同。

字符串转换为数字

  • 一元运算符:±x 完全按照数值强制转换步骤来转换。
  • 算术运算符:除了 + 有可能把运算子转为字符串,- / * / / 会把运算子自动转成数值。
  • Number() 函数:Number(x) 使用相同的算法转换 x,除了 BigInt 不会抛出 TypeError,而是返回它的 Number值,并且可能损失精度。
  • Number.parseFloat()Number.parseInt()Number() 相似,但只转换字符串,并且解析规则略有不同。

其他类型转换为布尔值

可使用一元运算符 `!` 进行显示转换。默认系统内部会自动调用 Boolean() 函数,因此除了 undefinednull±0NaN''(空字符串) 五个值,其他都是自动转为 true

类型强制转换

JavaScript 也是一个弱类型语言,这意味着当操作涉及不匹配的类型是否,它将允许隐式类型转换,而不是抛出一个错误。

const foo = 42
const result = foo + '1'

强制隐式转换是非常方便的,但是如果开发者不打算转换,或者打算向另一个方向转换,则会存在潜在的隐患。对于 symbolBigInt,JavaScript 总是不允许某些隐式类型转换。

原始值类型强制转换

强制原始值转换用于得到一个期望的原始值,但对实际类型应该是什么并没有强烈的偏好。通常情况下可以接受 StringNumberBigInt

如果值已经是原始值,则此操作不会进行任何转换。对象按以下顺序调用它的 [@@toPrimitive]()(将 hint 作为 default)、valueOf()toString() 方法,将其转换为原始值。注意,原始值转换会在 toString() 方法之前调用 valueOf() 方法,这与强制数字类型转换的行为相似,但与强制字符串类型转换不同。

[@@toPrimitive]() 方法,如果存在,则必须返回原始值——返回对象,会导致 TypeError。对于 valueOf()toString(),如果其中一个返回对象,则忽略其返回值,从而使用另一个的返回值;如果两者都不存在,或者两者都没有返回一个原始值,则抛出 TypeError

在强制转换为任意的原始类型时,[@@toPrimitive]() 方法总是优先调用。原始值的强制转换的行为通常与强制 number 类型类似,因为优先调用 valueOf();然而,有着自定义 [@@toPrimitive]() 方法的对象可以选择返回任意的原始值。

DateSymbol 对象是唯一重写 [@@toPrimitive]() 方法的对象。Date.prototype[@@toPrimitive]() 将 "default" hint 视为 "string",而 Symbol.prototype[@@toPrimitive]() 忽略 hint 并始终返回一个 symbol

数字类型强制转换

有两种数字类型:numberBigInt。有时候,该语言尤其希望是 numberBigInt;其他时候,它可能容忍并且根据运算对象的类型不同执行不同的运算。

强制数字类型转换与强制 number 类型转换几乎相同,只是 BigInt 会按原样返回,而不是引起 TypeError。强制数字类型转换用于所有算术运算,因为它们重载了 numberBigInt 类型。唯一例外的是一元加,它总是强制 number 类型转换。

  • 对于 Number 则总是返回自己。
  • undefined 变成了 NaN
  • null 变成了 0
  • true 变成了 1false 变成了 0
  • 如果它们包含数字字面量,字符串通过解析它们来转换。如果解析失败,返回的结果为 NaN。与实际数字字面量相比,它们有一些细微的差别。
    • 忽略前导和尾随空格/行终止符。
    • 前导数值 0 不会导致该数值成为八进制文本(或在严格模式下被拒绝)。
    • +- 允许在字符串的开头指示其符号然而,该标志只能出现一次,不得后跟空格。
    • Infinity-Infinity 被当作是字面量。在实际代码中,它们是全局变量。
    • 空字符串或仅空格字符串转换为 0
    • 不允许使用数字分隔符。
  • BigInt 抛出 TypeError,以防止意外的强制隐式转换损失精度。
  • Symbol 抛出 TypeError
  • 对象首先按顺序调用 [@@toPrimitive]()(将 "number" 作为 hint)、valueOf()toString() 方法将其转换为原始值。然后将生成的原始值转换为数值。

整数转换

一些操作需要整数,最值得注意的是那些适用于数组/字符串索引、日期/时间组件和数值基数的整数。执行上述数值强制转换步骤后,结果被截断为整数(通过丢弃分数部分)。如果数值为 ±Infinity,则按原样返回。如果数值是 NaN-0,则返回为 0。因此,结果总是整数(不是 -0)或 ±Infinity

值得注意的是,当转换到整数时,undefinednull 都会变成 0,因为 undefined 被转换为 NaNNaN 也变成了 0

固定宽度数值转换

JavaScript 有一些较低级别的函数,用于处理整数的二进制编码,最值得注意的是按位运算和 TypedArray 对象。按位运算总是将操作数转换为 32 位整数。在这些情况下,将值转换为数值后,然后首先截断小数部分,然后在整数的二进制的补码编码中取最低位,将数值归一化为给定的宽度。

字符串类型强制转换

许多内置操作首先将它们的参数强制转换为字符串。

  • 字符串按原样返回。
  • undefined 转换成 "undefined"
  • null 转换成 "null"
  • true 转换成 "true"false 转换成 "false"
  • 使用与 toString(10) 相同的算法转换数字。
  • 使用与 toString(10) 相同的算法转换 BigInt
  • Symbol 抛出 TypeError
  • 对于对象,首先,通过依次调用其 [@@toPrimitive]()(hint 为 "string")、toString()valueOf() 方法将其转换为原始值。然后将生成的原始值转换为一个字符串。

对象类型强制转换

许多内置操作首先将它们的参数强制转换为对象。

  • 对象则按原样返回。
  • undefinednull 则抛出 TypeError
  • NumberStringBooleanSymbolBigInt 等基本类型被封装成其对应的基本类型对象。

在 JavaScript 中实现相同效果的最佳方式是使用 Object() 构造函数。Object(x) 可以将 x 转换为对象,对于 undefinednull,它会返回一个普通对象而不是抛出 TypeError 异常。使用对象强制转换的地方包括:

  • for...in 循环的 object 参数。
  • Array 方法的 this 值。
  • Object 方法的参数,如 Object.keys()
  • 当访问基本类型的属性时进行自动转换,因为基本类型没有属性。
  • 在调用非严格函数时的 this 值。基本类型值被封装为对象,而 nullundefined 被替换为全局对象。

其他类型强制转换

所有的数据类型,除了 Null、Undefined 以及 Symbol,都有它们各自的强制过程。有三种不同的路径可以将对象转换为原始值:

  • 强制原始值转换:[@@toPrimitive]("default")valueOf()toString()
  • 强制数字类型转换、强制 number 类型转换、强制 BigInt 类型转换:[@@toPrimitive]("number")valueOf()toString()
  • 强制字符串类型转换:[@@toPrimitive]("string")toString()valueOf()

在所有情况下,[@@toPrimitive]() 如果存在,必须可调用并返回原始值,而如果它们不可调用或返回对象,valueOftoString 将被忽略。在过程结束时,如果成功,结果保证是原始值。然后,由此产生的原始值会进一步强制类型转换,具体取决于上下文。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部