三、JavaScript 内置对象(一)

277 阅读10分钟

前言

在前面的学习中我们已经了解到,JavaScript 里的引用值类型都最终继承自 Object,而除了 Object,JavaScript 还提供了非常多的内置对象,这些内置对象极大地丰富了 JavaScript 的内容,为其提供了非常丰富且强大的能力。本文将介绍一些常用的内置对象,以及他们的特性。了解所有的 JavaScript 内置对象可查阅文档 MDN JavaScript 内置对象

1. 包装对象

1.1 Boolean

Booleanboolean 字面量的对象包装器,它有两个作用:

  • 直接调用 Boolean(source),将值转为 boolean 字面量
  • 作为构造函数调用,创建 Boolean 对象
const a = 1;
const b = Boolean(a);
console.log("b =", b); // b = true

const c = new Boolean(a);
console.log("c =", c); // c = Boolean { true }
console.log("typeof c:", typeof c); // typeof c: object

从上面的结果可以看出来,Boolean 包装对象和字面量值的使用和含义是不同的:

  • 布尔值字面量的值类型为 boolean
  • Boolean 包装对象的值类型为 object

所以 Boolean 这个函数通常只会把它用来将其他类型的值转为 boolean 类型的值,也就是直接调用它,而不是把它作为构造函数来使用。

一般来说,我们在开发中不会使用 Boolean 来将目标值转为 boolean 类型,通过 !! 这样两次执行 逻辑非 操作,可以更快捷地将目标值转为 boolean 类型

也就是说:Boolean(source) 等价于 !!source

1.2 Number

Numbernumber 字面量的对象包装器,它有三个作用:

  • 提供一系列内置的静态数值/静态操作方法
  • 直接执行 Number(source),将值转为数字
  • 作为构造函数,创建 Number 对象

Number 对象提供的属性和方法见文档 MDN Number

image.png

文档的左侧列出了 Number 提供的静态属性、静态方法,以及实例方法,这些实例方法是所有 Number 对象实例和 number 类型字面量都可以使用的。

Boolean 对象一样,将 Number 作为构造函数使用时,最终得到的结果并不是一个数字,而是一个对象。

const a = "123";
const b = Number(a)
console.log("b =", b); // b = 123

const c = new Number(a);
console.log("c =", c); // c = Number {123}
console.log("typeof c:", typeof c); // typeof c: object

将一个值转换为数字字面量时,有三种方式:

  • 直接使用 Number()
  • 使用 Number.parseInt()
  • 使用 Number.parseFloat()

Number()

转换规则:

  • nullundefined 转换为 0
  • true 转换为 1false 转换为 0
  • 空字符串 "" 转换为 0
  • 形如 整数部分.小数部分 的字符串,可以不包含小数点和小数部分,此时直接转换为对应的数值表示
  • 其他的所有值都转换为 NaN

还有一种将其他值转为数字的方法:直接使用一元运算符 +。它的转换规则和使用 Number() 的规则是一样的,只是简洁一点。

Number.parseInt()

Number.parseInt() 用于将字符串转换为十进制数字 (非字符串的值会先被强制转化为字符串),也可用于数字整数部分的进制转换。

  • 转换规则:Number.parseInt() 从第一个非空白字符开始,直到遇到非数字字符为止 (与进制有关) ,将这个连续的字符串片段转换为对应的数字表示。
  • 注意:0x 这个开头会被视为十六进制前导,不会被计入检测到的数字中。
console.log(Number.parseInt("0x10")); // "0x10" 被视为十六进制的值,转换成十进制后输出:16

第二个参数 radix 对判定规则的影响

  • 如果进制为 2,则 2~9 也会被视为非数字
  • 如果进制是 11,则 a/A 也会被视为数字
  • 如果进制是 36,则 a-z/A-Z 都会被视为数字

总结:一个数字、英文字符是否被视为数字,与指定的进制有关,进制的值为 2~36,有可能被视为数字的字符有 0~9 的数字和 a-z/A-Z 英文字母。

console.log(Number.parseInt("0x10", 16)); // 此时进制可以写也可以不写,都会以十六进制输出 16
console.log(Number.parseInt("0b1")); // 默认十进制,输出的结果为第一个数字 0
console.log(Number.parseInt("0b1", 16)); // 此时指定十六进制,输出的结果为十六进制转十进制的值 177
console.log(Number.parseInt("z")); // 默认十进制,z 不能被解析为数字,输出结果 NaN
console.log(Number.parseInt("z", 36)); // 指定三十六进制,z 被解析为数字,输出三十六进制转十进制结果 35

如果 Number.parseInt() 的参数是数字,那么它会把这个数字按照 去尾法 转换为整数。

Number.parseFloat()

Number.parseFloat() 用于将字符串 (非字符串的值会先被强制转化为字符串) 转换为浮点数。

  • 转换规则:Number.parseFloat():从第一个非空白字符开始,遇到的第一个 . 被视为小数点,然后继续,直到遇到非数字字符为止,将这个连续的字符串片段转换为对应的数字表示。
  • 注意:Number.parseFloat() 只有一个参数,只能转换为十进制的浮点数。
console.log(Number.parsInt("233.3.3n")); // 233
console.log(Number.parsFloat("233.3.3n")); // 233
console.log(Number.parsInt("n233.3.3")); // NaN
console.log(Number.parsFloat("n233.3.3")); // NaN
console.log(Number.parsInt(233.6)); // 233
console.log(Number.parsFloat(233.6)); // 233.6

1.3 String

Stringstring 字面量的对象包装器,它有三个作用:

  • 提供一系列字符串的静态操作方法
  • 直接执行 String(source),将值转为字符串
  • 作为构造函数,创建 String 对象

字符串在日常开发中用得很多,ES6 之后增加了 模板字符串,为字符串提供了更丰富的功能,尤其是在字符串拼接方面。

字符串只有一个 length 属性,这个属性指的是字符串里代码单元的数量,有的字符被编码成一个代码单元,有的字符被编码成两个代码单元。

console.log("a".length); // 1
console.log("安".length); // 1
console.log("🍔".length); // 2。这个符号可以通过 `Win + ;` 两个按键调出来,仅限 Windows 平台

大部分的中文是编码一个代码单元,少部分的生僻字会被编码成两个代码单元。

目前,String 总共有三个静态方法,这些静态方法在一般的日常开发中使用很少,感兴趣的话可以查阅文档 MDN String 静态方法

最常用的是 String 的实例方法,这些方法包括裁剪字符串、拼接字符串、替换指定的字符、查找指定的字符等,这些方法无需硬背,只需要在使用的时候去翻阅 MDN String 实例方法 的文档就可以了。这些方法大多数有自己的特性,建议使用的时候多查阅,用多了、看多了就记住了。

2 Symbol

Symbol 单独列出来,虽然 symbol 是一种基础类型,但 Symbol() 并不能像 BooleanNumberString 那样作为构造函数来使用。

Symbol() 用来产生一个 symbol 类型的值,它返回的值都是唯一的,即使给这个函数调用传入相同的参数:

const s1 = Symbol(1);
const s2 = Symbol(1);
console.log("s1 等于 s2?", s1 == s2); // s1 等于 s2? false
console.log("s1 完全等于 s2?", s1 === s2); // s1 完全等于 s2? false

Symbol 主要是提供了一些静态属性和静态方法,实例的属性和方法只有继承自 Object 的那几个。

为什么要有 Symbol

为的是给遵循 ECMAScript 规范的编程语言提供 元编程 的能力。

Symbol 有几个关键的静态属性:

  • Symbol.iterator:定义对象的迭代器接口,这个迭代器接口可用于 for...of 循环。
  • Symbol.hasInstance:定义判断某对象是否为某构造器的实例的方法,这个方法会被用于 instanceof 操作符。
  • Symbol.toPrimitive:定义接受首选类型并返回对象原始值的表示的方法,这个方法会被用于强制类型转换。

翻阅 Symbol 的文档就会发现,它提供的这些能力大部分都是为了底层封装的,比如自己的 SDK、自己定义的数据结构等等。所以如果只是做普通的业务开发,一般来说不会接触到这个数据类型,只需要了解上面这三个比较关键的静态属性的含义即可。

3. 一般对象

3.1 BigInt

BigInt 是用来将值转换成 bigint 这个类型的,并提供了一些静态的属性和方法。

注意:BigInt 不可以作为构造函数调用!new BigInt(source) 会报语法错误。

bigint 这个数据类型是在 ES2020 才正式引入 ECMAScript 规范的,所以兼容性相对来说没有那么好,具体的可以查看文档 MDN BigInt 浏览器兼容性

BigInt 的使用场景有限,因为一般的日常业务开发中不会使用到它,只有在特别巨大的数值场景下,才会使用到。

目前我所能想到的场景就是,从文件里以字符串的格式读取到数据,或者从后端以字符串的格式传递过来数据,然后在前端利用 BigInt 将对应的值转为 bigint 类型的值进行各种计算。此时就需要 BigInt 能将字符串转为 bigint 类型的值,所以 BigInt() 本身就是可以做这件事的。

BigInt 的实例只有继承自 Object 的几个方法,没有额外的实例方法。BigInt 本身也只提供了 asIntNasUintN 两个静态方法,用于将 BigInt 的值转换为特定范围内的整数。

3.2 Math

Math 这个内置对象只有静态属性和静态方法。

Math 的静态属性是数学中的一些常量,比如自然对数的底数 e 对应的是 Math.E,圆周率的 π 对应的是 Math.PI

Math 的静态方法是数学计算中的一些常见运算,比如 取绝对值 运算对应的是 Math.abs()四舍五入 运算对应的是 Math.round(),以及所有的三角函数运算,Math 里都有。

Math 的静态方法 Math.random() 是用来随机产生一个 0~1之间的小数的,基于这个方法,我们可以实现下面这样的一个工具函数,用于生成指定范围内的数字。


/**
 * @description 随机生成指定范围内的数字
 * @param { number } min 范围的最小值
 * @param { number } max 范围的最大值
 * @param { number } precision 保留的小数位数,仅支持自然数
 * @returns { number }
 */
export function sizedRandom(min, max, precision = 0) {
  try {
    // 先检查参数的合法性
    checkArgs();
  } catch (error) {
    // 存在不合法时,打印出对应的错误信息
    console.error(error);
    // 然后直接返回
    return;
  }

  return genResult();

  // 这个函数才是核心
  function genResult() {
    // 首先产生一个随机数
    const value = Math.random();
    // 然后确定结果的缩放倍数
    const rate = max - min;
    // 再对结果进行缩放
    const scaledValue = value * rate;
    // 再对结果保留小数
    const roundedValue = +scaledValue.toFixed(precision);
    // 最后加上最小值,得到结果
    const result = roundedValue + min;

    return result;
  }

  function checkArgs() {
    const types = [typeof min, typeof max, typeof precision];
    const argNames = ["min", "max", "precision"];
    const acceptTypes = ["number", "number", "number"];

    for (let index = 0; index < types.length; index++) {
      const type = types[index];
      const acceptType = acceptTypes[index];
      if (type !== acceptType) {
        throw new Error(`参数 ${argNames[index]} 仅允许为 "${acceptType}" 类型!`);
      }
    }

    if (precision < 0 || Math.round(precision) !== precision) {
      throw new Error("精度仅支持自然数!当前的精度为:", precision);
    }

    if (min > max) {
      throw new Error("最小值超过了最大值!", { min, max });
    }
  }
}

上面这段代码也许你会觉得复杂,但这样的工具函数才有足够的健壮性。

之前的学习过程中听过一句话:“在 JavaScript 开发中,永远不要相信用户的输入。” 我理解这句话时,“用户” 既指 “使用系统的人”,也指使用产出代码的 “开发者”。

使用 Math.random() 也可以实现随机产生一个布尔值。

/**
 * @description 随机产生一个布尔值
 * @returns { boolean }
 */
export function randomBoolean() {
  return Math.random() > 0.5;
}

3.3 Date

Date 用于处理时间日期。

Date 支持任意合法格式的日期字符串作为入参,如果不传入参,则默认创建当前时刻的日期对象。

Date 实例没有属性,只有方法,且 Date 本身提供了几个静态方法,用于获取时间日期或操作实例对象。

在日常开发中,我用得较多的是静态方法 Date.now(),这个静态方法直接返回当前时刻的时间戳。

实际项目中,原生 Date 一般使用的较少,使用第三方库的较多,目前比较流行的两个日期处理库是 moment.js中文文档) 和 dayjs中文文档)。

总结

本文介绍了 JavaScript 的一些基础内置对象,我简单地将其分为了 “包装对象” 和 “一般对象” 两类,包括 BooleanNumberStringSymbolBigIntMathDate。此外,还有其他的很多内置对象,如错误对象 Error、数组对象 Array等。有的对象不那么常用,有的将在后续的文章中深入讲解。

本文如有描述错误的地方,欢迎指正~