JavaScript是一种脚本语言

JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。

JavaScript 是 web 开发人员必须学习的 3 门语言中的一门:

  1. HTML 定义了网页的内容
  2. CSS 描述了网页的布局
  3. JavaScript 控制了网页的行为
  4. 因为你没有选择。在Web世界里,只有JavaScript(TypeScript是JS的超集)能跨平台、跨浏览器驱动网页,与用户交互。

核心特性:

  • JavaScript 是一种轻量级的编程语言。
  • JavaScript 是可插入 HTML 页面的编程代码。
  • JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行

JavaScript 让静态的网页拥有了 “生命力”,实现了用户与页面的实时交互。它的跨平台特性与丰富的生态系统,使其从最初的网页脚本语言,成长为贯穿全栈开发的通用语言,是 Web 开发人员不可或缺的核心技能,也是进入前端、全栈开发领域的必备基础。

Flash背后的ActionScript曾经流行过一阵子,不过随着移动应用的兴起,没有人用Flash开发手机App,所以它目前已经边缘化了。相反,随着HTML5在PC和移动端越来越流行,JavaScript变得更加重要了。并且,新兴的Node.js把JavaScript引入到了服务器端,JavaScript已经变成了全能型选手。

JavaScript一度被认为是一种玩具编程语言,它有很多缺陷,所以不被大多数后端开发人员所重视。很多人认为,写JavaScript代码很简单,但这是完全错误的理解。JavaScript确实很容易上手,但其精髓却不为大多数开发人员所熟知。编写高质量的JavaScript代码更是难上加难。

JavaScript历史

https://liaoxuefeng.com/books/javascript/history/index.html

要了解JavaScript,我们首先要回顾一下JavaScript的诞生。

在上个世纪的1995年,当时的网景公司正凭借其Navigator浏览器成为Web时代开启时最著名的第一代互联网公司。

由于网景公司希望能在静态HTML页面上添加一些动态效果,于是叫Brendan Eich这哥们在两周之内设计出了JavaScript语言。你没看错,这哥们只用了10天时间。

为什么起名叫JavaScript?原因是当时Java语言非常红火,所以网景公司希望借Java的名气来推广,但事实上JavaScript除了语法上有点像Java,其他部分基本上没啥关系。

ECMAScript

因为网景开发了JavaScript,一年后微软又模仿JavaScript开发了JScript,为了让JavaScript成为全球标准,几个公司联合ECMA(European Computer Manufacturers Association)组织定制了JavaScript语言的标准,被称为ECMAScript标准。

所以简单说来就是,ECMAScript是一种语言标准,而JavaScript是网景公司对ECMAScript标准的一种实现。

那为什么不直接把JavaScript定为标准呢?因为JavaScript是网景的注册商标。

不过大多数时候,我们还是用JavaScript这个词。如果你遇到ECMAScript这个词,简单把它替换为JavaScript就行了。

JavaScript版本

JavaScript语言是在10天时间内设计出来的,虽然语言的设计者水平非常NB,但谁也架不住“时间紧,任务重”,所以,JavaScript有很多设计缺陷,我们后面会慢慢讲到。

此外,由于JavaScript的标准——ECMAScript在不断发展,最新版ECMAScript 6标准(简称ES6)已经在2015年6月正式发布了,所以,讲到JavaScript的版本,实际上就是说它实现了ECMAScript标准的哪个版本。

由于浏览器在发布时就确定了JavaScript的版本,加上很多用户还在使用IE6这种古老的浏览器,这就导致你在写JavaScript的时候,要照顾一下老用户,不能一上来就用最新的ES6标准写,否则,老用户的浏览器是无法运行新版本的JavaScript代码的。

不过,JavaScript的核心语法并没有多大变化。

JavaScript数据类型

JavaScript 是一种动态类型语言,这意味着变量的类型可以在运行时改变。

1
2
3
var x;           // 现在 x 是 undefined
x = 7; // 现在 x 是数值 (number)
x = "Bill"; // 现在 x 是字符串 (string)

JavaScript 中的数据类型主要分为两大类:原始类型 (Primitive Types)引用数据类型 (Object Types)

原始类型 (Primitive Types)

Number (数值)

JavaScript 不区分整数和浮点数,所有数字都属于 Number 类型。

基本的数值字面量格式是十进制整数,十进制整数可以像下面这样直接在代码中输入:

1
let intNum = 666; // 整数

JS可以直接的在字面表示不同进制的数字,八进制的值第一位必须是零0,后面每一位数的范围在0~7。如果某一位数超出范围,首位的0会被忽略,后面的数值会按照十进制来解析。用前缀0表示的八进制字面量在严格模式下是无效的,会报错。需要用前缀0o表示。

1
2
3
let octalNum1 = 070; // 八进制的 56 
let octalNum2 = 079; // 无效的八进制数值——解析为 79
let octalNum3 = 08; // 无效的八进制数值——解析为 8

十六进制的值前两位必须是0x,后面每一位十六进制数字的范围在09及AF,字母A~F可以大写也可以小写。

1
2
let hexNum1 = 0xA; // 十六进制的 10 
let hexNum2 = 0x1f; // 十六进制的 31

进行算术运算时,所有八进制和十六进制的数值都会被视为十进制。

特殊数值

  • NaN (Not a Number):表示非数字值。

    • 任何涉及 NaN 的操作(例如 NaN/10)都会返回 NaN。

    • 重要特性:NaN == NaN 为 false。NaN 与任何值(包括它自身)都不相等。

    • 判断方法:使用全局函数 isNaN() 来判断一个值是否为 NaN。

      1
      2
      3
      4
      5
      console.log(isNaN(NaN));         // true 
      console.log(isNaN(10)); // false(10是一个数值)
      console.log(isNaN("10")); // false(可以被转换成数值 10)
      console.log(isNaN("blue")); // true(不能转换成数值)
      console.log(isNaN(true)); // false(可以被转换成数值 1)
  • Infinity (无穷大):表示超过 JavaScript 可表示的最大数值。

  • -Infinity (负无穷大):表示超过 JavaScript 可表示的最小数值。

浮点数的数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。如果小数点后面没有数字,或者本身表示的就是有一个整数(例如1.0),那么该值会被转换为整数保存。

1
2
3
4
let floatNum1 = 1.1; 
let floatNum2 = 1.; // 小数点后面没有数字——解析为 1
let floatNum3 = .1; // 有效,但不推荐
let floatNum4 = 10.0; // 整数——解析为 10

同样,js支持e,对于那些极大或极小的数值,可以用e表示法(即科学计数法)来表示。e后面的数字代表10的幂中的指数,该幂值用来与前面的数相乘。

1
2
let floatNum1 = 6.123e7; // 等于61230000
let floatNum2 = 3e-17; // 等于0.00000000000000003

浮点数值的最高精度是17位小数,但是进行算数运算时是有误差的。

浮点数问题:JavaScript 遵循 IEEE 754 标准,浮点数运算可能存在精度损失。

  • 示例:0.1 + 0.2 !== 0.3 (结果是 0.30000000000000004)。

  • 避免方法:尽量避免直接比较浮点数,可以使用一个极小的误差范围进行比较:Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON (ES6 引入 Number.EPSILON 表示机器精度)。

  • 最可靠的是使用 toFixed() 方法对计算结果进行四舍五入:

    1
    2
    3
    4
    5
    6
    console.log((0.1 + 0.2).toFixed(1)); // 0.3
    console.log(((0.28 * 100 + 0.14 * 100) / 100).toFixed(2)); // 0.42
    // 要注意的是 toFixed() 方法返回的是字符串,如果是将计算结果展示到页面上是很方便的。
    // 如果是需要数值的情况,还要进行类型转换操作,例如使用"+"将其转换为数值:
    console.log(+(0.1 + 0.2).toFixed(1)); // 0.3
    console.log(typeof +(0.1 + 0.2).toFixed(1)) // number

大整数 (BigInt)

  • JavaScript 整数的精确范围是 -(2^53 - 1) 到 2^53 - 1 (即 15 位十进制数)。超过这个范围的整数运算可能不准确。
  • ES2020 引入 BigInt 类型,用于表示任意精度的整数。
  • 创建方法:在整数末尾添加 n (如 123n) 或调用 BigInt() 函数 (如 BigInt(“12345678901234567890n”))。
  • 限制:BigInt 不能有小数。不允许在 BigInt 和 Number 之间直接进行算术运算(除非显式类型转换)。

常见数值方法

  • toString():将数字转换为字符串。
  • toExponential(digits):将数字转换为指数表示的字符串,并四舍五入到指定位数。
  • toFixed(digits):将数字格式化为指定小数位数的字符串(四舍五入)。
  • toPrecision(precision):将数字格式化为指定总位数的字符串(四舍五入)。
  • valueOf():返回数字的原始值。
  • Number():可以将字符串、布尔值甚至日期转换为数字。Number(new Date()) 返回自 1970-01-01 00:00:00 UTC 以来的毫秒数。

由于内存限制,JavaScript不能保存所有的数值。如果某次计算的结果得到了一个超出 JavaScript数值范围的值,那么这个数值将被自动转换成特殊的 Infinity 值。具体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转 换成 Infinity(正无穷)。

String (字符串)

和其他语言一样,js 的字符串用于表示文本数据。

创建方式:使用单引号 (’)、双引号 (“) 或反引号 (`) 包裹。

转义字符:使用  进行转义,如 (换行)、制表符)、'、"、\。

模板字符串 (Template Literals) (ES6):使用反引号 (`) 包裹,支持:

  • 多行字符串:可以直接在字符串中换行,无需 。

  • 字符串插值 (String Interpolation):使用 ${expression} 将变量或表达式嵌入字符串中。

    1
    2
    3
    4
    var name = "Alice";
    let age = 30;
    let greeting = `Hello, ${name}! You are ${age} years old.`;
    console.log(greeting); // "Hello, Alice! You are 30 years old."

字符串是不可变的 (Immutable):一旦创建,字符串的值就不能被改变。所有看似修改字符串的方法,实际上都返回一个新的字符串。

字符字面量:String 类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其 他用途的字符。这些字符字面量可以出现在字符串中的任意位置,而且也将被作为一个字符来解析。

image-20251111201059679

常用属性和方法

  • length:获取字符串的长度。
  • [index]:通过下标访问字符(只读)。
  • toUpperCase():转换为大写。
  • toLowerCase():转换为小写。
  • charAt(index):返回指定索引位置的字符。
  • indexOf(searchValue, fromIndex):返回指定值第一次出现的索引,未找到返回 -1。
  • lastIndexOf(searchValue, fromIndex):返回指定值最后一次出现的索引,未找到返回 -1。
  • startsWith(searchString, position):检查字符串是否以指定值开头。
  • endsWith(searchString, position):检查字符串是否以指定值结尾。
  • includes(searchString, position):检查字符串是否包含指定值。
  • substring(startIndex, endIndex):提取字符串的一部分,不包含 endIndex 处的字符。
  • slice(startIndex, endIndex):与 substring 类似,但可以接受负数索引(从字符串末尾开始计数)。
  • replace(searchValue, replaceValue):替换字符串中的一部分。
  • split(separator):将字符串分割成数组。

模板字符串相当于加强版的字符串,可以定义多行字符串。还可以利用${}在字符串中插入变量和表达式。这个就很像 spring 中引用配置了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let s1 = `hello
world!`;
console.log(s1);
// "hello"
// "world!"

let myName = '小明';
let s2 = `我叫${myName}`;
console.log(s2);
// "我叫小明"

let s3 = `我今年${2020-2010}岁`;
console.log(s3);
// "我今年10岁"

Boolean (布尔值)

JavaScript中也是一样,只有两个值:true (真) 和 false (假),也是使用逻辑运算符&& (与), || (或), ! (非)。

这两个值与数字值不是一回事,因此 true 不一定等于 1,而 false 也不一定等于 0。

布尔值转换:其他 ECMAScript 类型的值都有与布尔值等价的形式。可以调用 Boolean() 函数来将其他类型转换为布尔值。不同类型转换为布尔值的规则如下表:

image-20251111201216591

Undefined

Undefined 类型只有一个值,即undefined。在声明变量但是没有初始化时,这个变量的值就是undefined。

表示变量已声明但未赋值,或者访问对象上不存在的属性。

typeof undefined 返回 undefined,所以正常情况下,调用未声明的变量会报错,但是对未声明的变量使用typeof操作符,同样会返回”undefined”

Null

表示一个空值,通常由开发者明确赋值,表示“无对象”或“没有值”。Null 类型也是只有一个值,即null。

null表示一个空对象指针,typeof null返回 object(有人说这是一个历史遗留的 bug,但它仍然是原始类型)。

核心特点:

  • undefined 的区别null 是 “主动清空”(如 let obj = null; 表示释放对象引用),而 undefined 是 “被动未定义”。
  • 历史遗留问题typeof null 返回 object(JavaScript 早期实现的 bug,因 null 被误判为对象的空指针),但 null 本质是原始类型,可通过 Object.prototype.toString.call(null) 准确判断为 [object Null]
  • 常见用途:初始化变量(预留对象位置)、释放对象引用(帮助垃圾回收)、作为函数返回值表示 “无结果”。

注意:

  • null == undefined // true (值相等,不考虑类型)
  • null === undefined // false (类型不同,严格相等为假)

Symbol (ES6)

表示一个独一无二的值。主要用于定义对象的唯一属性键,避免属性名冲突。

Symbol() 函数返回一个 Symbol 值,该值是唯一的。

typeof Symbol() 返回 symbol

1
2
let s = Symbol();
console.log(typeof s) // "symbol"

该函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,使得在控制台显示,或者转换为字符串进行判断时容易区分

1
2
3
4
5
6
7
8
9
let s1 = Symbol('foo');
let s2 = Symbol('bar');

console.log(s1) // "Symbol(foo)"
console.log(s2) // "Symbol(bar)"

if ( s1.toString() === "Symbol(foo)" ) {
// do something...
}

Symbol() 函数的参数只有描述作用,即使两个 Symbol() 函数的参数相同 ,它们的返回值也是不相等的。

一般用 Symbol 值作为对象的属性名

因为每一个 Symbol 值都是不相等的,所以 Symbol 值可以用作唯一标识,用于对象的属性名,就能防止属性名的同名冲突。对于一个对象是由多个模块构成的情况下非常有用,避免某个 key 被改写或者覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
let s = Symbol();

// 写法一
let a = {};
a[s] = 'Hello!';

// 写法二
let a = {
[s]: 'Hello!'
};

console.log(a[s]); // "Hello!"

Symbol.for(key)会将这个Symbol 注册到全局 symbol 注册表,而普通的Symbol()不会。

Symbol.for() 调用不会直接生成新的 Symbol ,它会先从注册表中搜索给定的key是否存在。如果存在,那么返回的是同一个 Symbol,否则生成新的。

内置 Symbol:JavaScript 提供了一系列内置 Symbol(如 Symbol.iterator 用于定义对象的迭代器),用于扩展语言原生行为。

注意:Symbol 不能与其他类型直接拼接字符串(需显式转换:String(s1)s1.description)。

对象类型 (Object Types)

在 JavaScript 中,除了原始类型(Number、String、Boolean 等),其余所有值都是对象。对象是一组键值对的无序集合,可包含数据(属性)和函数(方法),是实现复杂逻辑和数据结构的核心载体。所有对象的基础是 Object 类型,其他对象(如数组、日期)均继承自它。

Object (对象)

Object 是 JavaScript 中最原生的对象类型,用于存储无序的键值对。

创建方式

  • 通过对象字面量:使用花括号 {} 定义。

    1
    2
    3
    4
    5
    6
    var person = {
    name: "zjm",
    age: 3,
    tags: ['js', "web", 'java'],
    'full-name': 'Zhu Jinming' // 属性名包含特殊字符时需用引号
    };
    • 特殊属性名处理:
      • 包含空格、特殊字符(如 -)或关键字时,需用引号包裹:'full-name': 'Zhang San'
      • 数值属性会自动转为字符串:5: true 等价于 '5': true
  • 通过 new Object() 构造函数

    1
    2
    3
    4
    5
    // 创建空对象
    let person = new Object();
    // 动态添加属性
    person.name = "小明";
    person.age = 20;

    这种方式通过构造函数实例化对象,适合逐步添加属性的场景。

对象属性的存取

  • 点表示法:person.name (常用)。
  • 方括号表示法:person['age'] (当属性名包含特殊字符、是变量或数字时必须使用)。

对象属性的操作

  • 添加属性

    直接赋值即可(无论对象是否为空):

1
person.gender = "male"; // 新增 gender 属性
  • 删除属性

    delete 运算符彻底删除属性(仅删除自身属性,不影响原型链):

1
2
delete person.age; // 删除 age 属性
console.log(person.age); // undefined(属性已不存在)
  • 判断属性是否存在

    • ‘属性名’ in 对象:检查属性是否存在于对象自身或原型链上:

      1
      2
      console.log('name' in person); // true(自身属性)
      console.log('toString' in person); // true(继承自 Object 原型)
    • 对象.hasOwnProperty(‘属性名’):仅检查属性是否为对象自身的属性(排除原型链):

      1
      2
      console.log(person.hasOwnProperty('name')); // true(自身属性)
      console.log(person.hasOwnProperty('toString')); // false(原型链属性)

JavaScript 中的对象可分为三类:

  1. 内置对象:由 ECMAScript 标准定义的对象,无需手动创建即可使用,例如:
    • Array(数组)、Math(数学工具)、Date(日期)、RegExp(正则)等。
  2. 自定义对象:开发者通过字面量或构造函数创建的对象,例如前面的 person 对象。
  3. 宿主对象:由执行环境(如浏览器、Node.js)提供的对象,不属于 ECMAScript 标准:
    • 浏览器环境:window(窗口对象)、document(文档对象,DOM)等。
    • Node.js 环境:global(全局对象)、fs(文件系统模块)等。

所有 Object 的实例(包括自定义对象、内置对象)都继承以下属性和方法:

方法 / 属性 作用
constructor 指向创建该对象的构造函数(例如 person.constructor 指向 Object)。
hasOwnProperty(prop) 检查 prop 是否是对象自身的属性(非原型链)。
isPrototypeOf(obj) 检查当前对象是否是 obj 的原型(用于原型链判断)。
propertyIsEnumerable(prop) 检查 prop 是否可通过 for...in 循环枚举(自身属性且可枚举)。
toLocaleString() 返回与地区相关的对象字符串表示(如 Date 会按本地时间格式返回)。
toString() 返回对象的字符串表示(默认是 [object Object],可被重写)。
valueOf() 返回对象的原始值表示(通常与 toString() 结果相同,可被重写)。

Array (数组)

数组是 JavaScript 中用于存储有序数据集合的引用类型,可包含任意类型元素(数值、字符串、对象等),是处理批量数据的核心工具。

创建方式:使用方括号 [] 或 new Array()。

1
2
var arr = [1, 2, "3", null, NaN, true]; // JavaScript 数组可以包含不同类型的数据
var arr2 = new Array(1, "sl", 2, 9);

特性

  • 动态长度:数组的长度可以随时改变。
    • arr.length 赋值可以改变数组大小。如果赋值过小,会截断数组;如果赋值过大,会创建 undefined 元素。
  • 越界访问:访问越界的索引不会报错,而是返回 undefined。

稀疏数组

数组中存在未赋值的索引时,会形成稀疏数组,未赋值位置为 empty(与 undefined 不同,empty 不会被 forEachmap 等方法遍历)。

1
2
3
const arr = [1, , 3]; // 索引1为empty
console.log(arr.length); // 3(长度仍为3)
arr.forEach(item => console.log(item)); // 仅打印 1、3(跳过empty)

数组与类数组的区别

  • 数组是 Array 类型的实例,自带数组方法(如 pushmap)。

  • 类数组(如函数参数arguments、DOM 集合document.querySelectorAll())是具有length属性的对象,但无数组方法,需通过Array.from()[...类数组]转为真数组:

    1
    2
    const likeArray = { 0: 'a', 1: 'b', length: 2 };
    const realArray = Array.from(likeArray); // 转为真数组 ['a', 'b']

数组的本质

数组本质是特殊的对象,索引本质是字符串类型的属性名(如 arr[0] 等价于 arr['0']),但数组会自动维护 length 属性,且索引按数值排序。

数组的静态方法

Array 构造函数本身提供了静态方法,无需实例即可调用:

  • Array.isArray(value):判断一个值是否为数组(比 typeof 更可靠,typeof [] 返回 object

    1
    2
    Array.isArray([1, 2]); // true
    Array.isArray({}); // false
  • Array.of(...values):创建一个包含指定值的数组(解决 new Array(n)n 为数字时创建长度为 n 的空数组的问题)。

    1
    2
    Array.of(3); // [3](与 new Array(3) 不同,后者是 [empty × 3])
    Array.of(1, 2, 3); // [1, 2, 3]

常用方法

  • length:获取数组长度。
  • [index]:通过索引访问/修改元素。
  • indexOf(element):查找元素第一次出现的索引,未找到返回 -1。
  • push(element1, ...):向数组末尾添加一个或多个元素,并返回新长度。
  • pop():移除并返回数组的最后一个元素。
  • unshift(element1, ...):向数组开头添加一个或多个元素,并返回新长度。
  • shift():移除并返回数组的第一个元素。
  • slice(startIndex, endIndex):截取数组的一部分,返回一个新数组(不修改原数组)。
  • splice(startIndex, deleteCount, item1, ...):添加/删除数组元素(修改原数组)。
  • concat(array1, ...):拼接数组,返回一个新数组(不修改原数组)。
  • join(separator):将数组的所有元素连接成一个字符串。
  • sort(compareFunction):对数组进行排序。
    • 默认按字符串顺序排序,因此数值排序时需要提供比较函数:arr.sort((a, b) => a - b) (升序)。
  • reverse():反转数组元素。
  • fill(value, start?, end?):用 value 填充数组从 startend(不包含 end)的元素,修改原数组
  • copyWithin(target, start?, end?):将数组中 startend 的元素复制到 target 位置,修改原数组
  • 迭代方法 (ES5+)
    • forEach(callback):对数组的每个元素执行一次回调函数。
    • map(callback):创建一个新数组,其结果是调用提供的回调函数在每个元素上。
    • filter(callback):创建一个新数组,其中包含通过测试的所有元素。
    • reduce(callback, initialValue):对数组中的每个元素执行一个 reducer 函数(从左到右),将其结果汇总为单个返回值。
    • every(callback):测试数组的所有元素是否都通过了指定函数的测试。
    • some(callback):测试数组中是不是至少有一个元素通过了指定函数的测试。
  • 查找类方法(ES6+)
    • find(callback):返回数组中第一个满足回调条件的元素(未找到返回 undefined
    • findIndex(callback):返回数组中第一个满足回调条件的元素的索引(未找到返回 -1),类似 indexOf 但支持复杂条件。
    • includes(value):判断数组是否包含指定值(返回 true/false),与 indexOf 相比能正确识别 NaN
  • 数组转置与扁平化(ES6+)
    • flat(depth):将嵌套数组 “扁平化”,depth 为扁平化深度(默认 1,Infinity 表示无限深度)
    • flatMap(callback):先对数组元素执行 map,再对结果执行 flat(1)(常用于处理嵌套数组)

注意,sort 方法默认按 Unicode 编码排序,对特殊场景需自定义比较函数:

1
2
3
4
5
6
// 按对象属性排序
const users = [{age: 20}, {age: 18}];
users.sort((a, b) => a.age - b.age); // 按 age 升序

// 按字符串长度排序
['apple', 'banana', 'pear'].sort((a, b) => a.length - b.length);

Function (函数)

  • JavaScript 中的函数也是对象。typeof functionName 返回 “function”。
  • 将在“函数类型”部分详细介绍。

Date (日期)

  • 用于处理日期和时间。
  • 将在“日期类型”部分详细介绍。

typeof 运算符

  • typeof 运算符用于确定变量或表达式的类型。
    • typeof "" // “string”
    • typeof 666 // “number”
    • typeof true // “boolean”
    • typeof undefined // “undefined”
    • typeof null // “object” (历史遗留问题)
    • typeof [] // “object”
    • typeof {} // “object”
    • typeof function(){} // “function”
    • typeof Symbol() // “symbol”
    • typeof 1n // “bigint”