JavaScript 是一种多范式的动态语言,它包含类型、运算符、标准内置(built-in)对象和方法。它的语法来源于 Java 和 C,所以这两种语言的许多语法特性同样适用于 JavaScript。
数据类型
JavaScript 是一种有着动态类型的动态语言。JavaScript 中的变量与任何特定值类型没有任何关联,并且任何变量都可以分配(重新分配)所有类型的值。
let str = 'Hello World!'
str = 123
最新的 ECMAScript 标准定义了 8 种数据类型:
- 七种基本数据类型:
null:空,表示空对象的特殊关键字,JavaScript 大小写敏感的,因此null与Null、NULL或变体完全不同。undefined:未定义,和null一样是特殊关键字,表示变量未赋值时的属性。Boolean:布尔,有 2 个值分别是:true 和 false。Number:数字,包括整数或浮点数。String:字符串,一串表示文本值的字符序列。Symbol:符号,表示一种实例是唯一且不可改变的数据类型(ECMAScript 2015 新增类型)。BigInt:任意精度的整数,可以安全地存储和操作大整数,甚至可以超过数字的安全整数限制。
- 以及对象(
Object):它用于存储各种键值集合和更复杂的实体。
虽然这些数据类型相对来说比较少,但是通过他们你可以在程序中开发有用的功能。对象和函数是这门语言的另外两个基本元素。你可以把对象当作存放值的一个命名容器,然后将函数当作你的程序能够执行的步骤。
Function:函数,每个 JavaScript 函数实际上都是一个Function对象。Array:数组,一个有序的元素集合,通过索引(index)来访问元素。Date、RegExp、Error、Math、Map、Set等标准内置对象。
原始值
除了 Object 以外,所有类型都定义了表示在语言最低层面的不可变值,我们将这些值称为原始值。除了 null,所有原始类型都可以使用 typeof 运算符测试。typeof null 返回 object,因此必须使用 === null 来测试 null。
除了 null 和 undefined,所有原始类型都有它们相应的对象包装类型,这为处理原始值提供可用的方法。当在原始值上访问属性时,JavaScript 会自动将值包装到相应的包装对象中,并访问对象上的属性。然而,在 null 或 undefined 上访问属性时,会抛出 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 类型表示一个逻辑实体并且包括两个值:true 和 false。
Number 类型
Number 类型是一种基于 IEEE 754 标准的双精度 64 位二进制格式的值。它能够存储 (Number.MIN_VALUE)和 (Number.MAX_VALUE)之间的正浮点数,以及 - 和 - 之间的负浮点数,但是它仅能安全地存储在 -( − 1)(Number.MIN_SAFE_INTEGER)到 − 1(Number.MAX_SAFE_INTEGER)范围内的整数。超出这个范围,JavaScript 将不能安全地表示整数;相反,它们将由双精度浮点近似表示。
到 范围之外的值会自动转换: 范围之外的值会自动转换:
- 大于
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() 函数,因此除了 undefined、null、±0、NaN和 ''(空字符串) 五个值,其他都是自动转为 true。
类型强制转换
JavaScript 也是一个弱类型语言,这意味着当操作涉及不匹配的类型是否,它将允许隐式类型转换,而不是抛出一个错误。
const foo = 42
const result = foo + '1'
强制隐式转换是非常方便的,但是如果开发者不打算转换,或者打算向另一个方向转换,则会存在潜在的隐患。对于 symbol 和 BigInt,JavaScript 总是不允许某些隐式类型转换。
原始值类型强制转换
强制原始值转换用于得到一个期望的原始值,但对实际类型应该是什么并没有强烈的偏好。通常情况下可以接受 String 、Number 或 BigInt。
如果值已经是原始值,则此操作不会进行任何转换。对象按以下顺序调用它的 [@@toPrimitive]()(将 hint 作为 default)、valueOf() 和 toString() 方法,将其转换为原始值。注意,原始值转换会在 toString() 方法之前调用 valueOf() 方法,这与强制数字类型转换的行为相似,但与强制字符串类型转换不同。
[@@toPrimitive]() 方法,如果存在,则必须返回原始值——返回对象,会导致 TypeError。对于 valueOf() 和 toString(),如果其中一个返回对象,则忽略其返回值,从而使用另一个的返回值;如果两者都不存在,或者两者都没有返回一个原始值,则抛出 TypeError。
在强制转换为任意的原始类型时,[@@toPrimitive]() 方法总是优先调用。原始值的强制转换的行为通常与强制 number 类型类似,因为优先调用 valueOf();然而,有着自定义 [@@toPrimitive]() 方法的对象可以选择返回任意的原始值。
Date 和 Symbol 对象是唯一重写 [@@toPrimitive]() 方法的对象。Date.prototype[@@toPrimitive]() 将 "default" hint 视为 "string",而 Symbol.prototype[@@toPrimitive]() 忽略 hint 并始终返回一个 symbol。
数字类型强制转换
有两种数字类型:number 和 BigInt。有时候,该语言尤其希望是 number 或 BigInt;其他时候,它可能容忍并且根据运算对象的类型不同执行不同的运算。
强制数字类型转换与强制 number 类型转换几乎相同,只是 BigInt 会按原样返回,而不是引起 TypeError。强制数字类型转换用于所有算术运算,因为它们重载了 number 和 BigInt 类型。唯一例外的是一元加,它总是强制 number 类型转换。
- 对于
Number则总是返回自己。 undefined变成了NaN。null变成了0。true变成了1;false变成了0。
- 如果它们包含数字字面量,字符串通过解析它们来转换。如果解析失败,返回的结果为 NaN。与实际数字字面量相比,它们有一些细微的差别。
- 忽略前导和尾随空格/行终止符。
- 前导数值
0不会导致该数值成为八进制文本(或在严格模式下被拒绝)。 +和-允许在字符串的开头指示其符号然而,该标志只能出现一次,不得后跟空格。Infinity和-Infinity被当作是字面量。在实际代码中,它们是全局变量。- 空字符串或仅空格字符串转换为
0。 - 不允许使用数字分隔符。
BigInt抛出TypeError,以防止意外的强制隐式转换损失精度。Symbol抛出TypeError。- 对象首先按顺序调用
[@@toPrimitive]()(将 "number" 作为 hint)、valueOf()和toString()方法将其转换为原始值。然后将生成的原始值转换为数值。
整数转换
一些操作需要整数,最值得注意的是那些适用于数组/字符串索引、日期/时间组件和数值基数的整数。执行上述数值强制转换步骤后,结果被截断为整数(通过丢弃分数部分)。如果数值为 ±Infinity,则按原样返回。如果数值是 NaN 或 -0,则返回为 0。因此,结果总是整数(不是 -0)或 ±Infinity。
值得注意的是,当转换到整数时,undefined 和 null 都会变成 0,因为 undefined 被转换为 NaN,NaN 也变成了 0。
固定宽度数值转换
JavaScript 有一些较低级别的函数,用于处理整数的二进制编码,最值得注意的是按位运算和 TypedArray 对象。按位运算总是将操作数转换为 32 位整数。在这些情况下,将值转换为数值后,然后首先截断小数部分,然后在整数的二进制的补码编码中取最低位,将数值归一化为给定的宽度。
字符串类型强制转换
许多内置操作首先将它们的参数强制转换为字符串。
- 字符串按原样返回。
undefined转换成"undefined"。null转换成"null"。true转换成"true";false转换成"false"。- 使用与
toString(10)相同的算法转换数字。 - 使用与
toString(10)相同的算法转换BigInt。 Symbol抛出TypeError。- 对于对象,首先,通过依次调用其
[@@toPrimitive]()(hint 为 "string")、toString()和valueOf()方法将其转换为原始值。然后将生成的原始值转换为一个字符串。
对象类型强制转换
许多内置操作首先将它们的参数强制转换为对象。
- 对象则按原样返回。
undefined和null则抛出TypeError。Number、String、Boolean、Symbol、BigInt等基本类型被封装成其对应的基本类型对象。
在 JavaScript 中实现相同效果的最佳方式是使用 Object() 构造函数。Object(x) 可以将 x 转换为对象,对于 undefined 或 null,它会返回一个普通对象而不是抛出 TypeError 异常。使用对象强制转换的地方包括:
for...in循环的object参数。Array方法的this值。Object方法的参数,如Object.keys()。- 当访问基本类型的属性时进行自动转换,因为基本类型没有属性。
- 在调用非严格函数时的
this值。基本类型值被封装为对象,而null和undefined被替换为全局对象。
其他类型强制转换
所有的数据类型,除了 Null、Undefined 以及 Symbol,都有它们各自的强制过程。有三种不同的路径可以将对象转换为原始值:
- 强制原始值转换:
[@@toPrimitive]("default")→valueOf()→toString() - 强制数字类型转换、强制
number类型转换、强制BigInt类型转换:[@@toPrimitive]("number")→valueOf()→toString() - 强制字符串类型转换:
[@@toPrimitive]("string")→toString()→valueOf()
在所有情况下,[@@toPrimitive]() 如果存在,必须可调用并返回原始值,而如果它们不可调用或返回对象,valueOf 或 toString 将被忽略。在过程结束时,如果成功,结果保证是原始值。然后,由此产生的原始值会进一步强制类型转换,具体取决于上下文。