Skip to content

JavaScript 数据类型与内置方法

JavaScript 的数据类型体系、类型检测、类型转换,以及各类型原型链上的常用方法。


基本类型(7 种)

js
// 1. string
const str = 'hello'
const str2 = "hello"
const str3 = `hello ${str}`

// 2. number(整数和浮点数共用一种类型)
const int = 42
const float = 3.14
const nan = NaN           // typeof NaN === 'number'(历史遗留)
const inf = Infinity
const negInf = -Infinity

// 3. boolean
const bool = true
const bool2 = false

// 4. undefined(变量已声明但未赋值,或函数无返回值)
let unsetVar
console.log(unsetVar)            // undefined
console.log(undefined === undefined) // true

// 5. null(主动赋值,表示"空"或"无")
const emptyValue = null
console.log(null === null) // true
console.log(null == undefined) // true(宽松相等)
console.log(null === undefined) // false

// 6. symbol(唯一标识符)
const sym = Symbol('desc')
const sym2 = Symbol('desc')
console.log(sym === sym2)  // false(每个 Symbol 都唯一)
console.log(sym.description) // 'desc'

// 7. bigint(大整数,超出 Number 安全整数范围)
const big = 9007199254740991n
const big2 = BigInt(9007199254740991)
console.log(big === big2)  // true
console.log(big + 1n)      // 9007199254740992n
// console.log(big + 1)    // ❌ TypeError:不能混合运算

基本类型的存储

栈内存(Stack):
┌──────────┐
│ str: 0x1 │ ──→ 堆内存: "hello"
├──────────┤
│ num: 42  │  (直接存值)
├──────────┤
│ flag: true│
└──────────┘

基本类型存储在栈中(string 除外,字符串值存在堆中,栈中存引用),访问速度快。


引用类型

js
// 普通对象
const obj = { name: '张三', age: 25 }

// 数组(本质是对象,typeof [] === 'object')
const arr = [1, 2, 3]

// 函数(本质是对象,可调用)
function fn() {}
const arrow = () => {}
const asyncFn = async () => {}

// 日期
const date = new Date()

// 正则
const regex = /\d+/g
const regex2 = new RegExp('\\d+', 'g')

// Map / Set
const map = new Map([['a', 1]])
const set = new Set([1, 2, 3])

// Promise
const promise = new Promise((resolve) => resolve(42))

// 其他内置对象
new Error('错误')
new RegExp(/\d+/)
new WeakMap()
new WeakSet()

基本类型 vs 引用类型

特性基本类型引用类型
存储位置栈(Stack)堆(Heap),栈中存引用地址
赋值方式值拷贝引用拷贝(共享同一对象)
比较方式比较值比较引用地址
可变性不可变可变
typeof返回具体类型大部分返回 'object'
js
// ---- 基本类型:值拷贝 ----
let price = 10
let discount = price
discount = 20
console.log(price) // 10(price 不受影响)

// ---- 引用类型:引用拷贝 ----
let obj1 = { count: 10 }
let obj2 = obj1   // obj2 和 obj1 指向同一个对象
obj2.count = 20
console.log(obj1.count) // 20(被修改了!)

// ---- 比较 ----
console.log(1 === 1)               // true(值相同)
console.log('a' === 'a')           // true(值相同)
console.log({ a: 1 } === { a: 1 }) // false(不同对象,不同地址)
console.log(obj1 === obj2)         // true(同一对象,同一地址)

函数参数传递

js
function change(price, obj) {
  price = 100       // 基本类型:修改的是副本
  obj.name = '李四' // 引用类型:修改的是同一对象
  obj = { age: 30 } // 重新赋值引用:断开连接,不影响外部
}

let price = 10
let person = { name: '张三' }

change(price, person)
console.log(price)              // 10(没变)
console.log(person.name)    // '李四'(被修改了)
console.log(person.age)     // undefined(obj 重新赋值不影响外部)

类型检测

typeof

js
typeof 'hello'       // 'string'
typeof 42            // 'number'
typeof true          // 'boolean'
typeof undefined     // 'undefined'
typeof Symbol()      // 'symbol'
typeof 42n           // 'bigint'
typeof null          // 'object'(历史遗留 bug,不能改)
typeof {}            // 'object'
typeof []            // 'object'(数组也是 object)
typeof function(){}  // 'function'
typeof class C {}    // 'function'
typeof new Date()    // 'object'
typeof /\d+/         // 'object'

instanceof

检测构造函数的 prototype 是否在对象的原型链上。

js
[] instanceof Array          // true
[] instanceof Object         // true(Array.prototype 继承自 Object.prototype)
{} instanceof Object         // true
new Date() instanceof Date   // true
/\d+/ instanceof RegExp      // true

// 基本类型不适用
'hello' instanceof String    // false
42 instanceof Number         // false

// 跨 iframe 检测可能失效

Object.prototype.toString.call(最准确)

js
const type = (value) => Object.prototype.toString.call(value)

type('hello')              // '[object String]'
type(42)                   // '[object Number]'
type(true)                 // '[object Boolean]'
type(undefined)            // '[object Undefined]'
type(null)                 // '[object Null]'
type(Symbol())             // '[object Symbol]'
type(42n)                  // '[object BigInt]'
type({})                   // '[object Object]'
type([])                   // '[object Array]'
type(new Date())           // '[object Date]'
type(/\d+/)               // '[object RegExp]'
type(new Map())            // '[object Map]'
type(new Set())            // '[object Set]'
type(new Promise(() => {})) // '[object Promise]'
type(function(){})         // '[object Function]'

Array.isArray

js
Array.isArray([])          // true
Array.isArray({})          // false
Array.isArray('hello')     // false
Array.isArray({ length: 0 }) // false

类型转换

大白话解释: 类型转换就像"换衣服"。JavaScript 有时候需要把一种类型"换成"另一种类型才能进行运算。比如 '5' + 2 需要把数字 2 换成字符串 '2',结果是 '52'

为什么要理解类型转换?

  • 避免隐式转换坑'5' - 2 是 3,但 '5' + 2'52',不同运算符转换规则不同
  • 理解 ===== 的区别== 会隐式转换,=== 不会
  • 排查奇怪的 Bug[] + [] 是空字符串,[] + {}'[object Object]'

显式转换

js
// ---- 转字符串 ----
String(123)         // '123'
String(true)        // 'true'
String(null)        // 'null'
String(undefined)   // 'undefined'
String(Symbol())    // 'Symbol()'
String(42n)         // '42'
;(123).toString()   // '123'
;(10).toString(2)   // '1010'(二进制)
;(255).toString(16) // 'ff'(十六进制)
JSON.stringify({ a: 1 })        // '{"a":1}'
JSON.stringify([1, 2])          // '[1,2]'
JSON.stringify(undefined)       // undefined(返回 undefined,不是字符串)
JSON.stringify(null)            // 'null'

// ---- 转数字 ----
Number('123')       // 123
Number('12.3')      // 12.3
Number('abc')       // NaN
Number('')          // 0
Number(true)        // 1
Number(false)       // 0
Number(null)        // 0
Number(undefined)   // NaN
Number(Symbol())    // ❌ TypeError
Number(42n)         // 42
parseInt('123px')   // 123(解析到非数字字符为止)
parseInt('0xff')    // 255(自动识别十六进制)
parseInt('abc')     // NaN
parseFloat('3.14px') // 3.14
+'42'               // 42(一元加号)
+'abc'              // NaN
+'  123  '          // 123(自动去空格)

// ---- 转布尔 ----
Boolean(0)          // false
Boolean(-0)         // false
Boolean('')         // false
Boolean(null)       // false
Boolean(undefined)  // false
Boolean(NaN)        // false
Boolean(false)      // false
// 以上 7 个是假值(Falsy),其余全部是真值(Truthy)
Boolean(1)          // true
Boolean('hello')    // true
Boolean({})         // true(对象都是 true)
Boolean([])         // true(数组也是 true)
Boolean(function(){}) // true
!!'hello'           // true(双感叹号转布尔)

隐式转换(运算符触发)

js
// ---- 字符串拼接 ----
'hello' + 123       // 'hello123'(数字转字符串)
'hello' + null      // 'hellonull'
'hello' + undefined // 'helloundefined'
'hello' + {}        // 'hello[object Object]'

// ---- 数学运算 ----
'5' - 2             // 3(字符串转数字)
'5' * 2             // 10
'5' / 2             // 2.5
'5' % 2             // 1
+'123'              // 123
-'123'              // -123

// ---- 比较运算 ----
'5' > 3             // true(字符串转数字)
'5' > '20'          // true(字符串比较:按字典序 '5' > '2')
'abc' > 'abd'       // false(按字典序比较)
null == undefined    // true
null === undefined   // false
NaN === NaN          // false(NaN 不等于任何值,包括自身)
Number.isNaN(NaN)    // true(推荐用这个判断)

// ---- 逻辑运算 ----
'' || 'default'     // 'default'(空字符串是假值)
0 || 42             // 42
null ?? 'default'   // 'default'(只判断 null/undefined)
0 ?? 42             // 0(0 不是 null/undefined)

经典面试题

js
[] + []             // ''(空字符串)
[] + {}             // '[object Object]'
{} + []             // 0({} 被解析为空代码块,+[] 转数字为 0)

true + true         // 2
true + false        // 1
1 + null            // 1
1 + undefined       // NaN

'1' + 2 + 3         // '123'(从左到右,先拼接)
1 + 2 + '3'         // '33'(先算 1+2=3,再拼接 '3')

[] == ![]           // true
// ![] → false
// [] == false → 0 == 0 → true

包装类型

基本类型在调用方法时,JS 引擎会临时创建包装对象,调用完立即销毁。

js
const str = 'hello'
console.log(str.length)       // 5(临时创建 String 包装对象)
console.log(str.toUpperCase()) // 'HELLO'

// 等价于引擎内部临时操作:
// const temp = new String('hello')
// temp.length → 5
// temp = null(用完销毁)

// ❌ 不要手动创建包装对象
const strObj = new String('hello')
console.log(typeof strObj)     // 'object'(不是 string!)
console.log(strObj === 'hello') // false
console.log(strObj == 'hello')  // true(会自动拆箱)

// Number / Boolean 同理
const numObj = new Number(42)
console.log(typeof numObj)     // 'object'
console.log(numObj + 1)        // 43(自动拆箱参与运算)

String 原型方法

查找

js
const str = 'Hello, World!'

// includes(searchStr) —— 判断是否包含指定字符串,返回布尔值
str.includes('World')     // true
str.includes('xyz')       // false

// indexOf(searchStr) —— 返回首次出现的索引,找不到返回 -1
str.indexOf('World')      // 7
str.indexOf('xyz')        // -1

// lastIndexOf(searchStr) —— 从后往前找,返回最后一次出现的索引
str.lastIndexOf('l')      // 10

// startsWith(searchStr) —— 判断是否以指定字符串开头
str.startsWith('Hello')   // true
str.startsWith('World')   // false

// endsWith(searchStr) —— 判断是否以指定字符串结尾
str.endsWith('!')         // true
str.endsWith('World')     // false

// search(regexp) —— 正则匹配,返回首次匹配的索引,找不到返回 -1
str.search(/world/i)      // 7(i 标志忽略大小写)
str.search(/\d+/)         // -1(没有数字)

// charAt(index) —— 返回指定索引的字符
str.charAt(0)             // 'H'
str.charAt(100)           // ''(越界返回空字符串)

// charCodeAt(index) —— 返回指定索引字符的 Unicode 编码
str.charCodeAt(0)         // 72('H' 的 Unicode 编码)

// at(index) —— ES2022,支持负数索引(从末尾算起)
str.at(0)                 // 'H'
str.at(-1)                // '!'(最后一个字符)
str.at(-2)                // 'd'

截取

js
const str = 'Hello, World!'

// slice(start, end) —— 截取 [start, end) 区间的字符串,支持负数
str.slice(7, 12)          // 'World'(索引 7 到 11)
str.slice(-6, -1)         // 'World'(负数从末尾算起:-6='W',-1='!',左闭右开)
str.slice(7)              // 'World!'(省略 end,截取到末尾)
str.slice(0)              // 'Hello, World!'(浅拷贝整个字符串)

// substring(start, end) —— 类似 slice,但不支持负数,负数当 0 处理
str.substring(7, 12)      // 'World'
str.substring(12, 7)      // 'World'(自动交换参数,保证 start < end)
str.substring(-3)         // 'Hello, World!'(负数当 0,从头开始)

转换

js
const str = '  Hello, World!  '

// toUpperCase() / toLowerCase() —— 大小写转换,返回新字符串
str.toUpperCase()         // '  HELLO, WORLD!  '
str.toLowerCase()         // '  hello, world!  '

// trim() —— 去除两端空格(包括 \t \n \r 等空白字符)
str.trim()                // 'Hello, World!'

// trimStart() / trimEnd() —— 只去除左侧/右侧空格
str.trimStart()           // 'Hello, World!  '
str.trimEnd()             // '  Hello, World!'

// padStart(targetLength, padStr) —— 在左侧填充到指定长度
str.padStart(20, '-')     // '-------Hello, World!'
str.padStart(20)          // '       Hello, World!'(默认填充空格)

// padEnd(targetLength, padStr) —— 在右侧填充到指定长度
str.padEnd(20, '-')       // 'Hello, World!-------'

// repeat(count) —— 重复字符串指定次数
'abc'.repeat(3)           // 'abcabcabc'
'abc'.repeat(0)           // ''(0 次返回空字符串)

// 大小写转换常用于忽略大小写比较
const input = 'Hello'
input.toLowerCase() === 'hello' // true

替换

js
const str = 'Hello, World! Hello!'

// replace(search, replacement) —— 替换第一个匹配项,返回新字符串
str.replace('Hello', 'Hi')       // 'Hi, World! Hello!'(只替换第一个)
str.replace(/Hello/g, 'Hi')      // 'Hi, World! Hi!'(正则 + g 标志全局替换)

// replaceAll(search, replacement) —— ES2021,替换所有匹配项
str.replaceAll('Hello', 'Hi')    // 'Hi, World! Hi!'

// replace + 回调函数 —— 第二个参数可以是函数,接收 (match, group1, group2, ..., offset, string)
'hello-world'.replace(/-(\w)/g, (_, letter) => letter.toUpperCase())
// 'helloWorld'(转驼峰:匹配到 -w,捕获组为 w,转为大写 W)

// replaceAll + 正则 —— ⚠️ 正则必须带 g 标志,否则会抛 TypeError
'123-456'.replaceAll(/\d+/g, (match) => `[${match}]`)
// '[123]-[456]'
// ❌ '123-456'.replaceAll(/\d+/, 'x') // TypeError: replaceAll with non-global regex

拆分与合并

js
// split(separator, limit) —— 按分隔符拆分为数组
'a,b,c'.split(',')          // ['a', 'b', 'c'](按逗号拆分)
'hello'.split('')           // ['h', 'e', 'l', 'l', 'o'](按字符拆分)
'hello'.split()             // ['hello'](无参数返回整个字符串的数组)
'a--b--c'.split('--')       // ['a', 'b', 'c'](按多字符拆分)
'a--b--c'.split('--', 2)    // ['a', 'b'](limit 参数限制返回数量)

// join(separator) —— 数组合并为字符串
['a', 'b', 'c'].join('-')   // 'a-b-c'
['a', 'b', 'c'].join('')    // 'abc'
[1, 2, 3].join()            // '1,2,3'(默认逗号分隔)

链式调用

js
const result = '  Hello, World!  '
  .trim()
  .toLowerCase()
  .replace('world', 'js')
// 'hello, js!'

// 格式化手机号
'13812345678'
  .replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3')
// '138-1234-5678'

Number 原型方法与静态方法

实例方法

js
const num = 3.14159

// toFixed(digits) —— 保留指定小数位,返回字符串(四舍五入)
num.toFixed(0)            // '3'
num.toFixed(2)            // '3.14'
num.toFixed(5)            // '3.14159'

// toPrecision(precision) —— 指定有效数字位数,返回字符串
num.toPrecision(4)        // '3.142'(4 位有效数字)
num.toPrecision(2)        // '3.1'(2 位有效数字)

// toString(radix) —— 转为指定进制的字符串
;(255).toString()         // '255'(默认十进制)
;(255).toString(16)       // 'ff'(十六进制)
;(255).toString(2)        // '11111111'(二进制)
;(10).toString(8)         // '12'(八进制)
;(0.5).toString(2)        // '0.1'(二进制小数)

// toLocaleString() —— 按本地格式显示(千分位分隔等)
num.toLocaleString()      // '3.142'
;(1234567).toLocaleString('zh-CN') // '1,234,567'

静态方法

js
// ---- 解析 ----
// parseInt(string, radix) —— 解析字符串为整数,解析到非数字字符停止
Number.parseInt('123px')    // 123(遇到 p 停止)
Number.parseInt('abc')      // NaN(第一个字符就不是数字)
Number.parseInt('0xff')     // 255(自动识别 0x 前缀为十六进制)
Number.parseInt('0b1010')   // 10(自动识别 0b 前缀为二进制)
Number.parseInt('10', 2)    // 2(指定二进制解析:1*2 + 0 = 2)

// parseFloat(string) —— 解析字符串为浮点数
Number.parseFloat('3.14px') // 3.14(遇到 p 停止)
Number.parseFloat('1.23e5') // 123000(支持科学计数法)

// ---- 判断 ----
// isNaN(value) —— 严格判断是否为 NaN(不会先转数字)
Number.isNaN(NaN)           // true
Number.isNaN('abc')         // false(字符串不是 NaN)
Number.isNaN(Number('abc')) // true(先转数字得 NaN,再判断)

// isFinite(value) —— 判断是否为有限数(排除 Infinity、-Infinity、NaN)
Number.isFinite(42)         // true
Number.isFinite(Infinity)   // false
Number.isFinite(NaN)        // false

// isInteger(value) —— 判断是否为整数
Number.isInteger(42)        // true
Number.isInteger(42.0)      // true(42.0 等于 42)
Number.isInteger(42.5)      // false

// isSafeInteger(value) —— 判断是否在安全整数范围内(-2^53 ~ 2^53)
Number.isSafeInteger(42)    // true
Number.isSafeInteger(9007199254740992) // false(超出安全范围)

// ---- 常量 ----
Number.MAX_SAFE_INTEGER     // 9007199254740991(2^53 - 1)
Number.MIN_SAFE_INTEGER     // -9007199254740991
Number.MAX_VALUE            // 1.7976931348623157e+308(最大正数)
Number.MIN_VALUE            // 5e-324(最小正数,接近 0)
Number.EPSILON              // 2.220446049250313e-16(最小精度差)

浮点数精度问题

js
0.1 + 0.2                   // 0.30000000000000004
0.1 + 0.2 === 0.3           // false

// 原因:0.1 和 0.2 的二进制表示都是无限循环小数
// 0.1 → 0.0001100110011...
// 0.2 → 0.0011001100110...

// 解决方案
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON  // true
parseFloat((0.1 + 0.2).toFixed(1))           // 0.3
Math.round((0.1 + 0.2) * 10) / 10            // 0.3

Array 原型方法

增删(修改原数组)

js
const arr = [1, 2, 3, 4, 5]

// ---- 末尾操作 ----
// push(item1, item2, ...) —— 在末尾添加元素,返回新长度
arr.push(6)               // 返回 6(新长度),arr = [1,2,3,4,5,6]
arr.push(7, 8)            // 返回 8,arr = [1,2,3,4,5,6,7,8]

// pop() —— 删除末尾元素,返回被删除的元素
arr.pop()                 // 返回 8,arr = [1,2,3,4,5,6,7]

// ---- 头部操作 ----
// unshift(item1, item2, ...) —— 在头部添加元素,返回新长度
arr.unshift(0)            // 返回 9,arr = [0,1,2,3,4,5,6,7]

// shift() —— 删除头部元素,返回被删除的元素
arr.shift()               // 返回 0,arr = [1,2,3,4,5,6,7]

// ---- 任意位置操作 ----
// splice(start, deleteCount, ...items) —— 万能方法:删除、插入、替换
const arr2 = [1, 2, 3, 4, 5]

// 删除:从索引 1 开始删 2 个
arr2.splice(1, 2)         // 返回 [2, 3](被删元素),arr2 = [1, 4, 5]

// 插入:从索引 1 开始删 0 个,插入 2 和 3
arr2.splice(1, 0, 2, 3)   // 返回 [](没删),arr2 = [1, 2, 3, 4, 5]

// 替换:从索引 1 开始删 1 个,插入 99
arr2.splice(1, 1, 99)     // 返回 [2](被删元素),arr2 = [1, 99, 3, 4, 5]

查找

js
const arr = [1, 2, 3, 4, 5]

// indexOf(value) —— 返回首次出现的索引,找不到返回 -1
arr.indexOf(3)              // 2
arr.indexOf(99)             // -1

// lastIndexOf(value) —— 从后往前找,返回最后一次出现的索引
arr.lastIndexOf(3)          // 2

// includes(value) —— 判断是否包含指定值,返回布尔值
arr.includes(3)             // true
arr.includes(99)            // false

// find(callback) —— 返回第一个满足条件的元素,找不到返回 undefined
arr.find((x) => x > 3)     // 4(第一个大于 3 的元素)
arr.find((x) => x > 99)    // undefined

// findIndex(callback) —— 返回第一个满足条件的索引,找不到返回 -1
arr.findIndex((x) => x > 3) // 3

// findLast(callback) —— ES2023,从后往前找第一个满足条件的元素
arr.findLast((x) => x > 3) // 5

// findLastIndex(callback) —— ES2023,从后往前找第一个满足条件的索引
arr.findLastIndex((x) => x > 3) // 4

// ⚠️ NaN 的查找
const arr2 = [1, NaN, 3]
arr2.indexOf(NaN)           // -1(indexOf 用 === 比较,NaN !== NaN)
arr2.includes(NaN)          // true(includes 用 SameValueZero 算法)

遍历(不修改原数组)

js
const arr = [1, 2, 3, 4, 5]

// forEach(callback) —— 遍历数组,无返回值,不能 break
arr.forEach((item, index, array) => {
  // item: 当前元素
  // index: 当前索引
  // array: 原数组
  console.log(index, item)
})

// map(callback) —— 映射,返回新数组(每个元素经过 callback 处理后的结果)
arr.map((x) => x * 2)         // [2, 4, 6, 8, 10]
arr.map((x, i) => `${i}:${x}`) // ['0:1', '1:2', '2:3', '3:4', '4:5']

// filter(callback) —— 过滤,返回满足条件的元素组成的新数组
arr.filter((x) => x > 2)      // [3, 4, 5]
arr.filter((x) => x % 2 === 0) // [2, 4](偶数)

// every(callback) —— 判断是否所有元素都满足条件,返回布尔值
arr.every((x) => x > 0)       // true(所有元素都大于 0)
arr.every((x) => x > 3)       // false

// some(callback) —— 判断是否有任意元素满足条件,返回布尔值
arr.some((x) => x > 4)        // true(5 > 4)
arr.some((x) => x > 99)       // false

// reduce(callback, initialValue) —— 累积计算,返回最终累积值
arr.reduce((acc, cur, index) => {
  return acc + cur
}, 0) // 15(累加:0+1+2+3+4+5)

// reduce 求最大值
arr.reduce((max, cur) => cur > max ? cur : max, -Infinity) // 5

// reduce 实现 flat(扁平化)
[[1, 2], [3, 4], [5]].reduce((acc, cur) => acc.concat(cur), [])
// [1, 2, 3, 4, 5]

排序

js
const arr = [3, 1, 4, 1, 5, 9]

// sort() —— 默认按字符串 Unicode 排序(不是数字大小!)
arr.sort()                   // [1, 1, 3, 4, 5, 9](巧合正确)
;[10, 9, 2].sort()          // [10, 2, 9](按字符串 '10' < '2',错误!)

// sort(compareFn) —— 自定义排序函数
// compareFn(a, b) 返回值:
//   负数 → a 排在 b 前面
//   正数 → b 排在 a 前面
//   0   → 顺序不变
arr.sort((a, b) => a - b)   // 升序 [1, 1, 3, 4, 5, 9]
arr.sort((a, b) => b - a)   // 降序 [9, 5, 4, 3, 1, 1]

// 对象排序
const users = [
  { name: '张三', age: 25 },
  { name: '李四', age: 30 },
  { name: '王五', age: 20 },
]
users.sort((a, b) => a.age - b.age) // 按年龄升序

// reverse() —— 反转数组(修改原数组)
arr.reverse()                // [9, 5, 4, 3, 1, 1]

转换与截取

js
const arr = [1, 2, 3, 4, 5]

// slice(start, end) —— 截取 [start, end) 区间,不修改原数组
arr.slice(1, 3)              // [2, 3](索引 1 到 2)
arr.slice(-2)                // [4, 5](最后两个)
arr.slice()                  // [1, 2, 3, 4, 5](浅拷贝整个数组)

// flat(depth) —— 扁平化嵌套数组,depth 为展开深度
;[1, [2, [3]]].flat()        // [1, 2, [3]](默认 depth=1)
;[1, [2, [3]]].flat(Infinity) // [1, 2, 3](完全展开)

// flatMap(callback) —— ES2019,先 map 再 flat(1),等价于 map().flat(1)
;[1, 2, 3].flatMap((x) => [x, x * 2]) // [1, 2, 2, 4, 3, 6]
;['hello world', 'foo bar'].flatMap((s) => s.split(' '))
// ['hello', 'world', 'foo', 'bar']

// join(separator) —— 数组合并为字符串
arr.join('-')                // '1-2-3-4-5'
arr.join('')                 // '12345'

// at(index) —— ES2022,支持负数索引
arr.at(0)                    // 1
arr.at(-1)                   // 5(最后一个)

// Array.from(arrayLike, mapFn) —— 将类数组/可迭代对象转为数组
Array.from('hello')          // ['h', 'e', 'l', 'l', 'o']
Array.from({ length: 3 }, (_, i) => i) // [0, 1, 2]
Array.from(new Set([1, 2, 2, 3]))       // [1, 2, 3](去重)

// Array.of(...items) —— 用参数创建数组(解决 Array() 的歧义)
Array.of(1, 2, 3)           // [1, 2, 3]
Array.of(3)                 // [3](不是 [undefined, undefined, undefined])

判断

js
Array.isArray([])            // true
Array.isArray({})            // false
Array.isArray('hello')       // false
Array.isArray({ length: 0 }) // false

// 判断是否为空数组
arr.length === 0             // true

修改原数组 vs 不修改原数组

修改原数组不修改原数组
push / popslice
unshift / shiftconcat
splicemap / filter
sort / reverseflat / flatMap
fill / copyWithinreduce / reduceRight
find / findIndex / includes
join / at / Array.from

Object 常用方法

获取键 / 值

js
const obj = { a: 1, b: 2, c: 3 }

// Object.keys(obj) —— 返回自身可枚举属性名组成的数组
Object.keys(obj)              // ['a', 'b', 'c']

// Object.values(obj) —— 返回自身可枚举属性值组成的数组
Object.values(obj)            // [1, 2, 3]

// Object.entries(obj) —— 返回自身可枚举键值对组成的二维数组
Object.entries(obj)           // [['a',1], ['b',2], ['c',3]]

// 配合 for...of 遍历对象
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value)     // 'a' 1, 'b' 2, 'c' 3
}

// 配合 Object.fromEntries(ES2019)将键值对转回对象
Object.fromEntries([['x', 10], ['y', 20]]) // { x: 10, y: 20 }

合并与复制

js
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 3, c: 4 }

// Object.assign(target, ...sources) —— 合并到 target,后面的覆盖前面的同名属性
Object.assign({}, obj1, obj2) // { a: 1, b: 3, c: 4 }

// 展开运算符(推荐,更简洁)
const merged = { ...obj1, ...obj2 } // { a: 1, b: 3, c: 4 }

// ---- 浅拷贝(只拷贝一层,嵌套对象仍是引用) ----
const copy1 = Object.assign({}, obj1)
const copy2 = { ...obj1 }

// ---- 深拷贝(完全独立的副本) ----
// structuredClone —— Web API(非 ES 标准),Chrome 98+(2022)支持,支持循环引用、Date、Map、Set 等
const deep = structuredClone(obj1)

// JSON 方式(有限制:不能拷贝 undefined、函数、Symbol、循环引用、Date 会变字符串)
const deep2 = JSON.parse(JSON.stringify(obj1))

// ⚠️ 浅拷贝的坑
const original = { name: '张三', address: { city: '北京' } }
const shallow = { ...original }
shallow.address.city = '上海'
console.log(original.address.city) // '上海'(被修改了!因为 address 是引用)

冻结 / 密封

js
const obj = { a: 1, b: { c: 2 } }

// Object.freeze(obj) —— 完全冻结:不能增删改属性(浅冻结)
Object.freeze(obj)
obj.a = 99         // 静默失败(严格模式报 TypeError)
obj.d = 4          // 静默失败(不能新增)
delete obj.a       // 静默失败(不能删除)
Object.isFrozen(obj) // true

// ⚠️ freeze 只冻结第一层!嵌套对象仍可修改
obj.b.c = 99       // ✅ 可以修改(b 对象没有被冻结)

// Object.seal(obj) —— 密封:不能增删属性,但可以修改已有属性的值
const obj2 = { a: 1 }
Object.seal(obj2)
obj2.a = 99        // ✅ 可以改
obj2.b = 2         // 静默失败(不能新增)
delete obj2.a      // 静默失败(不能删除)
Object.isSealed(obj2) // true

// 对比
// freeze:不能增、不能删、不能改
// seal:  不能增、不能删、可以改

属性描述符

js
const obj = {}

Object.defineProperty(obj, 'name', {
  value: '张三',
  writable: false,       // 不可写
  enumerable: false,     // 不可枚举(不出现在 for...in / Object.keys 中)
  configurable: false,   // 不可删除或重新配置
})

obj.name = '李四'         // 静默失败(writable: false)
console.log(obj.name)    // '张三'

Object.keys(obj)         // [](enumerable: false,不出现)

delete obj.name          // 静默失败(configurable: false)

// 获取属性描述符
Object.getOwnPropertyDescriptor(obj, 'name')
// { value: '张三', writable: false, enumerable: false, configurable: false }

// 定义多个属性
Object.defineProperties(obj, {
  age: { value: 25, writable: true, enumerable: true },
  id: { value: 1, writable: false, enumerable: false },
})

原型操作

js
// 获取原型
Object.getPrototypeOf({})              // Object.prototype
Object.getPrototypeOf([])              // Array.prototype

// 设置原型
const proto = { greet() { return 'hi' } }
const obj = Object.create(proto)
obj.greet() // 'hi'

// 创建纯净对象(无原型)
const bare = Object.create(null)
bare.__proto__        // undefined
bare.toString         // undefined
bare instanceof Object // false

hasOwnProperty 与 in

js
const obj = { a: 1 }
const proto = { b: 2 }
Object.setPrototypeOf(obj, proto)

// hasOwnProperty:只检查自身
obj.hasOwnProperty('a')  // true
obj.hasOwnProperty('b')  // false(在原型上)

// in:检查整个原型链
'a' in obj               // true
'b' in obj               // true
'c' in obj               // false

// 推荐用 Object.hasOwn()(ES2022,更安全)
Object.hasOwn(obj, 'a')  // true
Object.hasOwn(obj, 'b')  // false

Math 常用方法

js
// ---- 常量 ----
Math.PI                    // 3.141592653589793(圆周率)

// ---- 取整 ----
Math.ceil(1.1)             // 2(向上取整)
Math.ceil(-1.1)            // -1(注意:-1 比 -1.1 大)
Math.floor(1.9)            // 1(向下取整)
Math.floor(-1.1)           // -2(注意:-2 比 -1.1 小)
Math.round(1.5)            // 2(四舍五入,.5 向上)
Math.round(-1.5)           // -1(负数 .5 向上,即向 0 方向)
Math.trunc(1.9)            // 1(截断小数部分,只保留整数)
Math.trunc(-1.9)           // -1

// ---- 绝对值 ----
Math.abs(-5)               // 5
Math.abs(5)                // 5

// ---- 最大 / 最小 ----
Math.max(1, 2, 3)         // 3
Math.min(1, 2, 3)         // 1
Math.max()                 // -Infinity(无参数返回 -Infinity)
Math.max(...[1, 2, 3])    // 3(配合展开运算符)

// ---- 幂运算 ----
Math.sqrt(9)               // 3(平方根)
Math.cbrt(27)              // 3(立方根,ES6)
Math.pow(2, 10)            // 1024(幂运算)
2 ** 10                    // 1024(ES7 幂运算符,推荐)

// ---- 随机数 ----
Math.random()              // [0, 1) 随机小数

// 生成 [min, max] 的随机整数
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}
randomInt(1, 100)          // 1~100 的随机整数

常见面试题

typeof null 为什么是 'object'

js
// JS 第一版的实现中,值的类型标签存储在低位
// 000: object
// 001: int
// 010: double
// 100: string
// 110: boolean
// null 的机器码是全 0,所以被误判为 object
typeof null // 'object'(永远修不了,因为会破坏现有代码)

typeof NaN 为什么是 'number'

js
// NaN 是 IEEE 754 标准中的一个特殊值
// 它属于 number 类型,只是表示"不是一个有效数字"
typeof NaN // 'number'
NaN === NaN // false
Number.isNaN(NaN) // true(推荐)
isNaN('abc') // true(会先转数字,不推荐)

[] == ![] 为什么是 true

js
// 1. ![] → false(对象是真值,取反得 false)
// 2. [] == false
// 3. 规则:布尔值先转数字 → 0
// 4. [] == 0
// 5. 规则:对象转原始值 → ''(空字符串调用 toString)
// 6. '' == 0
// 7. 规则:字符串转数字 → 0
// 8. 0 == 0 → true

如何判断一个变量是数组

js
const arr = [1, 2, 3]

// 最推荐
Array.isArray(arr)                      // true

// 其他方式
arr instanceof Array                    // true
Object.prototype.toString.call(arr) === '[object Array]' // true
arr.constructor === Array               // true
Array.prototype.isPrototypeOf(arr)      // true

如何判断空对象

js
const obj = {}

// 方案一
Object.keys(obj).length === 0           // true

// 方案二
JSON.stringify(obj) === '{}'            // true

// 方案三
Object.getOwnPropertyNames(obj).length === 0 // true

// 方案四(包含原型属性的判断用 for...in)
function isEmpty(obj) {
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) return false
  }
  return true
}

参考

个人学习笔记,部分内容借助 AI 辅助整理,仅供查阅参考,请以官方文档为准