苟日新,日日新,又日新
... 对象展开运算符| 场景 | 代码 | 说明 |
|---|---|---|
| 获取所有键 | Object.keys({a:1,b:2}) // ["a","b"] |
返回字符串数组 |
| 获取所有值 | Object.values({a:1,b:2}) // [1,2] |
返回值数组 |
| 获取键值对 | Object.entries({a:1,b:2}) // [[1]] |
返回二维数组 |
| 对象遍历 | Object.entries(obj).forEach(([k,v])=>...) |
遍历键值对 |
| 注意问题 | 说明 |
|---|---|
| 只返回可枚举属性 | 继承属性返回不到 |
| 键名都是字符串 | 返回的键数组都是字符串类型 |
| 顺序有保证 | 按对象创建顺序返回 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 合并对象 | Object.assign({},a,b) // 合并结果 |
合并多个对象 |
| 浅拷贝对象 | const copy = Object.assign({}, obj) |
复制对象 |
| 复制属性 | Object.assign(target, src) |
将源属性复制到目标 |
| 多对象合并 | obj1 = Object.assign(obj1, obj2, obj3) |
链式合并 |
| 注意问题 | 说明 |
|---|---|
| 浅拷贝 | 不深拷贝嵌套对象 |
| 会修改目标 | 修改目标对象本身 |
| 属性覆盖 | 同名属性后覆盖前 |
... 对象展开运算符| 场景 | 代码 | 说明 |
|---|---|---|
| 合并对象 | {...a,...b} // 合并结果 |
合并多个对象 |
| 复制对象 | {...obj} // 新对象 |
浅拷贝对象 |
| 添加属性 | ({...obj, name:"test"}) // 添加name |
添加新属性 |
| 删除属性 | ({...obj, a:undefined, ...{a:undefined}}) |
不推荐,用 delete |
| 注意问题 | 说明 |
|---|---|
| 浅拷贝 | 只拷贝第一层属性 |
| 不会修改原对象 | 返回新对象 |
| 后覆盖前 | 展开顺序决定覆盖关系 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 继承原型 | Object.create(proto) |
创建继承对象 |
| 继承方法 | const child = Object.create(parent) |
父级方法被继承 |
| 带描述符 | Object.create(proto, descriptors) |
添加属性描述符 |
| 原型链 | obj.__proto__ === parent |
原型关系设置 |
| 注意问题 | 说明 |
|---|---|
| 创建原型链 | 设置对象的原型 |
| 不会创建自身属性 | 只有继承属性 |
| 原型对象 | 第一个参数是原型对象 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 点语法访问 | obj.name |
简单属性名 |
| 方括号访问 | obj["name"] |
动态属性名 |
| 访问特殊名 | obj["123abc"] |
不能点语法访问 |
| 访问符号键 | obj[symbol] |
符号类型键名 |
| 注意问题 | 说明 |
|---|---|
| 点语法限制 | 特殊字符、数字开头不能用点 |
| 方括号支持动态 | 属性名可以是变量 |
| 属性不存在 | 返回 undefined 不是错误 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 定义属性 | Object.defineProperty(obj, "key", {value: 1}) |
定义单个属性 |
| 定义多个 | Object.defineProperties(obj, {k1:{value:1},k2:{value:2}}) |
定义多个属性 |
| 不可写属性 | obj.key = value |
严格模式下会报错 |
| 不可枚举属性 | for...in |
不遍历不可枚举属性 |
| 注意问题 | 说明 |
|---|---|
| 修改配置 | 配置项影响属性行为 |
| 默认配置 | 定义时设置可写、可枚举、可配置 |
| 严格模式 | 不可写属性设置会抛错 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 检查属性 | "name" in obj |
检查所有属性(含继承) |
| 检查自身 | obj.hasOwnProperty("name") |
仅检查自身属性 |
| 检查存在 | obj.name !== undefined |
检查值是否存在 |
| 检查枚举 | Object.getOwnPropertyDescriptor(obj, "name") |
获取属性描述符 |
| 注意问题 | 说明 |
|---|---|
| in 查继承 | 返回自身和继承属性 |
| hasOwnProperty 查自身 | 只返回自身属性 |
| undefined 是值 | undefined 可能真的是值 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 数组转对象 | Object.fromEntries([[2]]) |
键值对转对象 |
| Map 转对象 | Object.fromEntries(new Map()) |
Map 转对象 |
| 表单数据 | Object.fromEntries(new FormData()) |
表单转对象 |
| 反向转换 | Object.entries(obj) |
对象转键值对 |
| 注意问题 | 说明 |
|---|---|
| 较新特性 | ES2019 新增 |
| 键名转换 | 键名会自动转字符串 |
| 数组格式 | 必须是 [key, value] 格式 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 所有属性 | Object.getOwnPropertyNames(obj) |
自身所有属性名 |
| 忽略原型 | Object.getOwnPropertyNames(obj) |
只获取自身 |
| 数组返回 | 返回属性名数组 | 包含所有属性 |
| 空对象 | Object.getOwnPropertyNames({}) |
返回空数组 |
| 注意问题 | 说明 |
|---|---|
| 不查继承 | 不返回继承属性 |
| 包含符号键 | 还需 Object.getOwnPropertySymbols() |
| 返回数组 | 返回字符串数组 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 符号属性 | Object.getOwnPropertySymbols(obj) |
获取符号键属性 |
| 内部属性 | 一些库使用符号键隐藏属性 | 获取隐藏属性 |
| 组合获取 | const all = [...Object.getOwnPropertyNames(obj),...Object.getOwnPropertySymbols(obj)] |
获取所有键 |
| 迭代符号 | Reflect.ownKeys(obj) |
获取所有自身属性 |
| 注意问题 | 说明 |
|---|---|
| 获取符号键 | 单独获取 Symbol 类型键 |
| 隐藏属性 | 常用于库内部隐藏属性 |
| 需要组合 | 组合使用获取所有键 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 所有键 | Reflect.ownKeys(obj) |
获取所有自身键 |
| 含符号键 | 同时包含字符串和符号键 | 完整键列表 |
| 对象操作 | 用于框架实现属性遍历 | 框架级操作 |
| 组合返回 | 返回数组 | 字符串 + 符号 |
| 注意问题 | 说明 |
|---|---|
| ES6 新增 | Reflect 对象新增方法 |
| 所有键 | 包含所有类型键名 |
| 不可枚举 | 不可枚举也返回 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 定义属性 | Object.defineProperty(obj, "name", {value:"test"}) |
定义单个属性 |
| 描述符 | 设置 get/set/value/writable/enumerable/configurable | 属性描述符 |
| 访问器 | {get(){...},set(v){...}} |
定义访问器属性 |
| 只读属性 | {writable:false} |
创建只读属性 |
| 注意问题 | 说明 |
|---|---|
| 严格模式 | 不可写设置会抛错 |
| 配置限制 | 不可配置后不能再改 |
| getter 设置 | get/set 不能和 value/writable 混用 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 自定义 getter | obj = {get name(){return this._name}} |
自定义读取逻辑 |
| 自定义 setter | obj = {set name(v){this._name=v}} |
自定义写入逻辑 |
| 数据验证 | set age(v){if(v>0)this._age=v} |
写入时验证 |
| 响应式原理 | 类似 Vue/React 的响应式 | 实现响应式系统 |
| 注意问题 | 说明 |
|---|---|
| 必须配合 defineProperty | 定义器函数必须用此方法 |
| 性能影响 | getter/setter 可能降低性能 |
| 序列化 | 某些工具不序列化 getter/setter |
| 场景 | 代码 | 说明 |
|---|---|---|
| 严格相等 | Object.is(1, 1) // true |
严格相等判断 |
| 相等 NaN | Object.is(NaN, NaN) // true |
NaN 能正确判断 |
| 正负零 | Object.is(-0, 0) // false |
区分正负零 |
| 替代 === | 替代 === 的不足 |
更可靠的相等判断 |
| 注意问题 | 说明 |
|---|---|
| 替代 === | 比 === 更严格 |
| NaN 处理 | NaN 能正确识别为相等 |
| 特殊值 | 处理 +0/-0 特殊情况 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 冻结对象 | Object.freeze(obj) |
冻结对象 |
| 不可修改 | 不能新增/修改/删除属性 | 只读对象 |
| 不可变配置 | 属性描述符不可改 | 配置不能修改 |
| 不可逆 | 冻结后不可恢复 | 单向操作 |
| 注意问题 | 说明 |
|---|---|
| 浅冻结 | 不冻结嵌套对象 |
| 深冻结需递归 | 需递归冻结嵌套对象 |
| 严格模式 | 严格模式下修改会抛错 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 密封对象 | Object.seal(obj) |
密封对象 |
| 可修改值 | 值可以改,但属性不可删 | 可修改属性值 |
| 不可新增 | 不能新增或删除属性 | 属性固定 |
| 对比冻结 | seal 比 freeze 宽松 | 比 freeze 更灵活 |
| 注意问题 | 说明 |
|---|---|
| 可改值 | 可以修改属性值 |
| 不可删增 | 不能新增或删除属性 |
| 可配置 | 属性仍可配置 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 禁止扩展 | Object.preventExtensions(obj) |
禁止添加新属性 |
| 已删属性 | 已存在属性可删除 | 已有属性可操作 |
| 可修改 | 可以修改已有属性 | 修改已有值 |
| 不可逆 | 一旦禁止无法恢复 | 单向操作 |
| 注意问题 | 说明 |
|---|---|
| 只禁止扩展 | 不能添加新属性 |
| 可删除修改 | 已有属性可删改 |
| 最宽松限制 | 对象限制中最宽松 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 检查属性 | Object.hasOwn(obj, "name") |
ES2022 新增 |
| 替代方法 | 替代 hasOwnProperty 调用 | 更简洁写法 |
| 安全调用 | 不用担心对象覆盖 | 不会出错 |
| 现代写法 | Object.hasOwn(obj,key) |
推荐用法 |
| 注意问题 | 说明 |
|---|---|
| 较新特性 | ES2022 新增 |
| 方法调用 | 直接作为方法调用 |
| 不继承 | 只检查自身属性 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 基础解构 | const {name, age} = user |
提取属性值 |
| 重命名 | const {name:n} = user |
提取并重命名 |
| 默认值 | const {name="默认"} = user |
提供默认值 |
| 嵌套解构 | const {user:{name}} = data |
嵌套解构 |
| 注意问题 | 说明 |
|---|---|
| 属性不存在 | 返回 undefined |
| 重命名 | 变量名和属性名可以不同 |
| 嵌套限制 | 嵌套不能太长 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 动态键名 | const key = "name"; obj[key] = "test" |
动态属性名 |
| 表达式键名 | obj[{[key]: value}] |
对象计算属性名 |
| 符号键名 | obj[{[symbol]: value}] |
符号类型键名 |
| 函数键名 | obj[{[fn()]: value}] |
函数返回值作为键名 |
| 注意问题 | 说明 |
|---|---|
| ES6 新增 | 计算属性名语法 |
| 方括号 | 使用方括号包裹表达式 |
| 键名计算 | 键名在创建时计算 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 设置原型 | obj.__proto__ = proto |
设置对象原型 |
| 获取原型 | Object.getPrototypeOf(obj) |
获取对象原型 |
| 检查原型 | obj instanceof Constructor |
检查原型链 |
| 原型链遍历 | Object.getPrototypeOf(obj) !== null |
遍历原型链 |
| 注意问题 | 说明 |
|---|---|
| 不推荐__proto__ | 不推荐直接设置 proto |
| 推荐 getPrototypeOf | 推荐使用 Object.getPrototypeOf |
| 原型链查找 | 属性查找遵循原型链 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 方法简写 | const obj = {add(a,b){return a+b}} |
简写方法定义 |
| 箭头函数 | const obj = {add: (a,b)=>a+b} |
箭头函数方法 |
| 构造器简写 | constructor(){} |
简写构造函数 |
| 生成器方法 | *gen(){yield 1} |
简写生成器 |
| 注意问题 | 说明 |
|---|---|
| 简写语法 | ES6 新增方法简写 |
| 函数名 | 方法名自动设为函数名 |
| 原型设置 | 方法定义在原型上 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 属性拷贝 | Object.assign(target, source) |
浅拷贝属性 |
| 多个源 | Object.assign({}, a, b, c) |
拷贝多个对象 |
| 覆盖规则 | 后面的覆盖前面的 | 后覆盖前规则 |
| 原型属性 | 不拷贝原型链 | 只拷贝自身属性 |
| 注意问题 | 说明 |
|---|---|
| 浅拷贝 | 不深拷贝嵌套对象 |
| 修改目标 | 会修改目标对象 |
| 返回目标 | 返回被修改的目标对象 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 对象转 Map | new Map(Object.entries(obj)) |
转 Map 对象 |
| Map 转对象 | Object.fromEntries(new Map()) |
Map 转对象 |
| 键值对处理 | [...objEntries] |
直接迭代键值对 |
| 数据转换 | 在 Map 和对象间转换 | 数据格式转换 |
| 注意问题 | 说明 |
|---|---|
| 键名转字符串 | 对象键名都是字符串 |
| Map 支持任意 | Map 的键可以是任意类型 |
| 数据一致 | 转换后保持数据一致 |
| 场景 | 代码 | 说明 |
|---|---|---|
| 删除属性 | const {key, ...rest} = obj |
删除某个属性 |
| 添加属性 | {...obj, key: value} |
添加新属性 |
| 修改属性 | {...obj, key: newValue} |
修改属性值 |
| 不可变更新 | 类似 React state 更新 | 不可变数据更新 |
| 注意问题 | 说明 |
|---|---|
| 不可变操作 | 不修改原对象 |
| 浅拷贝 | 只拷贝第一层 |
| 新对象 | 返回新对象 |