系列文章
平凡前端之路_0ML与 HTML5
平凡前端之路_05.CSS与CSS3
平凡前端之路_14.ES与ES6
平凡前端之路_15.let 和 const声明
平凡前端之路_16.变量的解构赋值
平凡前端之路_17.展开操作符(Spread)/剩余参数(Rest)
平凡前端之路_18.字符串的扩展
平凡前端之路_19.数组的扩展
平凡前端之路_20.函数的扩展
对象的拓展
对象属性与方法简写
ES6 引入了对象字面量的增强写法,允许在属性名与变量名相同,或定义方法时进行简化。这不仅减少了代码冗余,还提升了可读性。
属性简写
当对象属性的键名与当前作用域中的变量名相同时,可以省略键名和冒号,直接写入变量名。
const na = '说你又不听';
const ge = '听又不懂';
const us = { na, ge };
us
方法简写
在对象中定义方法时,可以省略 function 关键字及冒号,直接使用函数名后跟参数列表。
const et = () => '听又不懂';
const obj = { et };
obj
动态属性名
可以通过方括号 [] 包裹变量、表达式或 Symbol 来实现动态属性名定义。
方括号[]会先计算内部表达式的值,将结果转换为字符串(Symbol 类型保持不变)作为最终属性名。
动态属性名分类与用法
根据属性名的类型,可以分为变量/表达式动态属性名和 Symbol 类型属性名,两者核心用法和区别如下:
| 操作类型 | 变量/表达式 | Symbol (唯一标识符) |
|---|
| 创建或定义属性(对象字面量中) | 使用计算属性名:const key = ‘name’;const obj = { [key]: ‘Value’ };支持任意表达式:{ [‘pre_’ + suffix]: val } | 使用 Symbol:const sym = Symbol(‘desc’);const obj = { [sym]: ‘Value’ }; |
| 关键区别与注意事项 | - 变量/表达式:键最终会被转换为字符串。 | - Symbol:Symbol类型的键保持为 Symbol类型 |
| 操作类型 | 变量/表达式 | Symbol (唯一标识符) |
|---|
| 动态添加/修改属性 | obj[dynamicKey] = newValue; | obj[symRef] = newValue;注意引用保持:必须持有 Symbol 的引用才能访问或修改。 |
| 关键区别与注意事项 | - 变量/表达式:如果键已存在则覆盖,不存在则新增。 | - Symbol:即使描述相同,Symbol(‘a’) !== Symbol(‘a’),因此必须使用同一个 Symbol 变量引用才能修改同一属性。 |
| 操作类型 | 变量/表达式 | Symbol (唯一标识符) |
|---|
| 获取属性值 | 方括号取值 (动态场景):obj[keyVar] | 必须使用方括号及 Symbol 引用:obj[symRef]无法通过字符串获取:obj[‘Symbol(desc)’] // undefined |
| 关键区别与注意事项 | - 变量/表达式:只要描述相同即可取值 | - Symbol:即使描述相同,Symbol(‘a’) !== Symbol(‘a’),因此必须使用同一个 Symbol 变量引用才能取值。 |
点符号 vs 动态方括号 特性对比
访问对象属性有两种语法:点语法 . 和方括号语法 [],两者特性区别如下:
| 特性 | 点符号 (.) | 动态方括号 ([]) |
|---|
| 语法格式 | object.property | object[expression] |
| 属性名类型 | 必须是合法的 JavaScript 标识符(静态字符串)。 | 可以是任何字符串、变量、表达式、数字或 Symbol。 |
| 动态性 | 静态:属性名在编写代码时固定,无法使用变量值作为键。 | 动态:属性名在运行时计算,支持使用变量、拼接字符串或函数返回值作为键。 |
| 特殊字符支持 | 不支持:若属性名包含空格、连字符 - 或以数字开头,会报语法错误。 | 支持:可以访问包含空格、特殊字符或保留字(如 class, for)的属性名。 |
| 数字键支持 | 不支持:obj.123 会导致语法错误。 | 支持:obj[123] 或 obj[‘123’] 均可正常访问。 |
| Symbol 支持 | 不支持:无法直接通过点符号访问 Symbol 类型的键。 | 支持:必须使用 obj[symVar] 形式访问 Symbol 键。 |
对象方法拓展静态方法
ES6 为对象新增了Object.values、Object.entries、Object.assign等实用静态方法,大幅简化了对象操作,让代码更简洁易读;静态方法不依赖实例,直接用于对象操作而非被对象调用。
对象属性获取与转换
键值对获取
| 方法 | 描述 |
|---|
| Object.values | 返回对象自身可枚举属性的值组成的数组 |
| Object.entries | 返回对象自身可枚举属性的键值对数组 |
| Object.values 方法接受一个参数 |
|---|
| Object.values 方法至少接受一个参数 |
| 参数传入为 0 个,会抛出错误。 |
| 参数传入 ≥2 个:只会生效第一个,其余参数会被忽略 |
| 参数为要遍历获取自身可枚举的值的对象 |
| 传入非对象参数会强制转换为对象 |
| 传入字符串参数会强制转换为字符串对象 Object(字符串),字符串对象有自己的可枚举属性 |
| 传入 undefined / null 基本类型参数,不能被强制转换为对象。→ 直接报错 Uncaught TypeError |
| 传入其他非字符串基本类型参数(如数字、布尔值等)会强制转换为相应对象,但基本没有自己的可枚举属性,返回一个空数组 |
| 传入 Map 和 Set 作为参数,没有可枚举的自有属性,返回一个空数组。 |
| 参数传 undefined / null ,不能被强制转换为对象。→ 直接报错 Uncaught TypeError |
| Object.values 方法不接受回调函数 |
| Object.values 返回一个新数组 |
|---|
| Object.values 返回的是数组,不是Iterator对象 |
| Object.values 返回的数组不包含 Symbol 键的属性值 |
const obj = { at: '懂又不做', br: '做你又做错' };
const va = Object.values(obj);
va
Object.values({ [Symbol()]: '懂又不做', foo: '做你又做错' });
const sa = Object.values('懂又不做')
sa
const ua = Object.values(undefined );
const na = Object.values(null);
const nu = Object.values(266);
const bo = Object.values(true);
const bi = Object.values(266n);
const sy = Object.values(Symbol('做你又做错'));
nu
bo
bi
sy
const ma = Object.values(new Map([['懂又不做', '做你又做错'],['错又不认', 42]]));
const se = Object.values(new Set(['懂又不做', '做你又做错']));
ma
se
| values 相似对比 | 方法类型 | 调用方式 | 返回值 |
|---|
| Object.values | Object 静态方法 | Object.values(obj) | 真数组 |
| Array.prototype.values | Array 原型方法 | arr.values() | 迭代器(Iterator) |
| Set.prototype.values | Set 原型方法 | set.values() | 迭代器(Iterator) |
| Map.prototype.values | Map 原型方法 | map.values() | 迭代器(Iterator) |
| URLSearchParams.prototype.values | URLSearchParams 原型方法 | params.values() | 迭代器(Iterator) |
| FormData.prototype.values | FormData 原型方法 | formData.values() | 迭代器(Iterator) |
| Object.entries 方法接受一个参数 |
|---|
| Object.entries 至少接受一个参数 |
| 参数传入为 0 个,会抛出错误。 |
| 参数传入 ≥2 个:只会生效第一个,其余参数会被忽略 |
| 参数为要遍历获取自身可枚举的键值对的对象 |
| 传入非对象参数会强制转换为对象 |
| 传入字符串参数会强制转换为字符串对象 Object(字符串),字符串对象有自己的可枚举属性 |
| 传入 undefined / null 基本类型参数,不能被强制转换为对象。→ 直接报错 Uncaught TypeError |
| 传入其他非字符串基本类型参数(如数字、布尔值等)会强制转换为相应对象,但基本没有自己的可枚举属性,返回一个空数组 |
| 传入Map 和 Set 作为参数,没有可枚举的自有属性,返回一个空数组。 |
| Object.entries 方法不接受回调函数 |
| Object.entries 返回一个新数组 |
|---|
| Object.entries 返回的是数组,一个二维数组,不是Iterator对象。 |
| Object.entries 返回数组的每个子数组包含两个元素:[key, value]。 |
| Object.entries 返回的数组不包含 Symbol 键的键值对。 |
const obj = { at: '错又不认', br: '认又不改' };
const entries = Object.entries(obj);
entries
Object.entries({ [Symbol()]: '错又不认', foo: '认又不改' });
const sa = Object.entries('错又不认');
sa
const ua = Object.entries(undefined );
const na = Object.entries(null);
const nu = Object.entries(266);
const bo = Object.entries(true);
const bi = Object.entries(266n);
const sy = Object.entries(Symbol('认又不改'));
nu
bo
bi
sy
const ma = Object.entries(new Map([['错又不认', '认又不改'],['改又不服', 42]]));
const se = Object.entries(new Set(['错又不认', '认又不改']));
ma
se
| entries 相似对比 | 方法类型 | 调用方式 | 返回值 |
|---|
| Object.entries | Object 静态方法 | Object.entries (obj) | 真数组 |
| Array.prototype.entries | Array 原型方法 | arr.entries() | 迭代器(Iterator) |
| Set.prototype.entries | Set 原型方法 | set.entries() | 迭代器(Iterator) |
| Map.prototype.entries | Map 原型方法 | map.entries() | 迭代器(Iterator) |
| URLSearchParams.prototype.entries | URLSearchParams 原型方法 | params.entries() | 迭代器(Iterator) |
| FormData.prototype.entries | FormData 原型方法 | formData.entries() | 迭代器(Iterator) |
键值对转换
| 方法 | 描述 |
|---|
| Object.fromEntries | 返回一个对象,将键值对列表(如 Map 或数组)转换为对象,是 Object.entries() 的逆操作。 |
| Object.fromEntries 方法接受一个参数 |
|---|
| Object.fromEntries 至少接受一个参数 |
| 参数传入为 0 个,会抛出错误。 |
| 参数传入 ≥2 个:只会生效第一个,其余参数会被忽略 |
| 参数为可迭代对象,且可迭代对象中的每个元素应包含两个值,分别作为结果对象的键和值。 |
| 能直接正确处理 默认迭代器为 entries 的可迭代对象(如Map、 URLSearchParams、FormData) 作为参数。 |
| Object.fromEntries方法不接受回调函数 |
| Object.fromEntries方法返回一个新对象 |
|---|
| Object.fromEntries 返回的对象中,重复键出现时,后出现的键值会覆盖先前的值。 |
| 即使描述相同,Symbol(‘a’) !== Symbol(‘a’),因此Object.fromEntries 返回的对象中,必须是同一个 Symbol 变量引用,后出现的键值会覆盖先前的值。。 |
| Object.fromEntries 返回的对象 的键名只能是字符串或 Symbol。 |
| Object.fromEntries 返回的对象 的键名返回非字符串/非 Symbol 类型(如数字、布尔值、对象)时,会被强制转换为字符串。 |
| Object.fromEntries 创建的 Symbol 键属性无法通过 Object.entries 完整恢复。 |
| Object.fromEntries 处理的 Map、URLSearchParams 、FormData等无法通过 Object.entries 完整恢复。 |
let ar1 = Object.fromEntries([ ['认又不改', '改又不服'], ['不服你不说', 42]])
ar1
let a2 = Object.fromEntries(['认又不改', '改又不服'].entries())
ar2
let ar3 = Object.fromEntries([ ['认又不改'], ['改又不服']])
ar3
let ar4 = Object.fromEntries([ ['认又不改', '改又不服', 'extra'], ['不服你不说', 42]])
ar4
let se1 = Object.fromEntries(new Set([['认又不改', '改又不服'], ['不服你不说', 42]]))
se1
let se2 = Object.fromEntries(new Set(['认又不改', 'bar']).entries())
se2
let ma1 = Object.fromEntries(new Map([['认又不改', '认又不改'],['不服你不说', 42]]))
ma1
let ma2 = Object.fromEntries(new Map([['认又不改', '认又不改'],['不服你不说', 42]]).entries())
ma2
let us1 = Object.fromEntries(new URLSearchParams('foo=bar&baz=42'))
us1
let us2 = Object.fromEntries(new URLSearchParams('foo=bar&baz=42').entries())
us2
let formData = new FormData()
formData.append('认又不改', '改又不服')
formData.append('不服你不说', 42)
let fd1 = Object.fromEntries(formData )
fd1
let fd2 = Object.fromEntries(formData.entries())
fd2
对象操作与合并
对象合并与复制
| 方法 | 描述 |
|---|
| Object.assign | 将一个或者多个[源Source]中所有可枚举的自有属性复制到[目标Target],并返回修改后的[目标Target]。 |
| Object.assign 方法接受多个参数 |
|---|
| Object.assign 方法至少接受一个参数 |
| 参数传入为 0 个,会抛出TypeError 错误。 |
| 参数传入为 1 个,参数为[目标Target](若为 null 或 undefined 会报错,其他原始值会转为包装对象)。 |
| 参数传入 ≥2 个:第一个参数为[目标Target],后续参数为[源Source];所有[源Source]的可枚举属性都会合并到[目标Target]中(同名属性后者覆盖前者) |
| 第一个参数为[目标Target](若为 null 或 undefined 会报错,其他原始值会转为包装对象)。 |
| 其他参数为一个或多个包含要应用的属性的[源Source]。参数如果不是对象,会尝试转换为对象( 若为 null 或 undefined,则跳过该参数,不会报错,也不会合并任何属性;其他原始值会转为包装对象)。 |
| Object.assign 方法不接受回调函数 |
| Object.assign 返回是经过 ToObject 转换后的[目标Target]。 |
|---|
| [目标Target]原本是对象,则返回原对象引用 |
| [目标Target]是基本类型(非 null/undefined),则返回一个新生成的包装对象(引用不同) |
| Object.assign 会覆盖同名属性,且仅合并可枚举属性。 |
| Object.assign 返回的[目标Target]可包含 Symbol 键的属性, 仅合并可枚举属性。 |
const target = { at: '改又不服', br: '不服你不说t' },
source1 = { br: '不服你不说1', cv: '那叫我怎么办1' },
source2 = { cv: '那叫我怎么办2' };
const result1 = Object.assign(target, source1, source2);
result1
const symA = Symbol('改又不服');
const symB = Symbol('那叫我怎么办');
const targetSym = { [symA]: '改又不服' };
const sourceSym = { [symA]: '不服你不说', [symB]: '那叫我怎么办' };
const resultSym = Object.assign(targetSym, sourceSym);
resultSym
const obj1 = Object.assign('改又不服');
const obj2 = Object.assign('改又不服', {cv: '不服你不说'});
obj1
obj2
const obj2 = Object.assign('改又不服', '不服你不说');
const obj3 = Object.assign('改又不服', ['不','服']);
const obj4 = Object.assign('改又不服', {0: '不', 1: '服'});
const str = '改又不服';
const obj1 = Object.assign({}, str);
const obj2 = Object.assign({}, Object(str));
const obj3 = Object.assign({}, Object(str), '不服你不说');
obj1
obj2
obj3
const obj1 = Object.assign(undefined, { at: '改又不服' });
const obj2 = Object.assign(null, { at: '改又不服' });
const resultSafe = Object.assign({ at: '改又不服' }, null, undefined, { br: 2 });
resultSafe
const nu = Object.assign(266);
const bo = Object.assign(true);
const bi = Object.assign(266n);
const sy = Object.assign(Symbol('不服你不说'));
nu
bo
bi
sy
const ou = Object.assign({ at: '改又不服' }, 266, true, 266n, Symbol('不服你不说'));
ou
const ma = Object.assign(new Map([['改又不服', '不服你不说'],['那叫我怎么办', 42]]));
const se = Object.assign(new Set(['改又不服', '不服你不说']));
const ma1 = Object.assign(new Map([['改又不服', '不服你不说'],['那叫我怎么办', 42]]), { ma: 266});
const se1 = Object.assign(new Set(['改又不服', '不服你不说']), { ma: 266});
ma
se
Object.entries(ma1)
ma1.entries()
Object.entries(se1)
se1.entries()
const ma = Object.assign({ at: '认又不改' }, new Map([['改又不服', '不服你不说'],['那叫我怎么办', 42]]));
const se = Object.assign({ at: '认又不改' }, new Set(['改又不服', '不服你不说']));
ma
se
数据分组
| 方法 | 描述 |
|---|
| Object.groupBy | 根据回调函数返回值对可迭代对象的元素分组。 |
| Object.groupBy 接受两个参数 |
|---|
| Object.groupBy 至少接受两个参数。 |
| 参数传入 < 2 个,会抛出错误。 |
| 参数传入 ≥ 3 个, 只会生效前两个,其余参数会被忽略。 |
| 第一个参数为一个可迭代对象(通常是数组),包含需要被分组的元素。当不是可迭代对象时,会抛出错误。 |
| 第二个参数为对可迭代对象中的每个元素执行的函数。当不是函数时,会抛出错误。 |
| Object.groupBy 方法能接受一个回调函数,即第二个参数,是一个必需的参数,且必须是一个函数,该回调函数接受三个参数 |
|---|
| - element: 当前遍历到的元素(可迭代对象中的元素)。 |
| - index: 当前遍历到的元素的索引。 |
| - iterable: 调用 groupBy 方法时传入的整个可迭代对象本身。 |
| 回调函数的返回值用于构建结果对象的属性名,相同值的元素会被归入同一个组。 |
| 当回调函数无返回值时,默认返回 undefined,被转换为字符串 “undefined” 作为分组键。 |
| Object.groupBy 返回一个无原型的新对象 |
|---|
| Object.groupBy 返回的是 Object.create(null) 类型的对象(无原型)。 |
| Object.groupBy 返回的对象 的键名只能是字符串或 Symbol。 |
| Object.groupBy 返回的对象 的键名返回非字符串/非 Symbol 类型(如数字、布尔值、对象)时,会被强制转换为字符串。 |
| 如果需要更复杂的键类型(如对象、未转换的数字、NaN 等作为独立键),应使用 Map.groupBy,它返回一个 Map 对象,支持任意类型的键。 |
const array = [1, 2, 3, 4, 5, 6];
const result = Object.groupBy(array, (num) => num % 2 === 0 ? 'even': 'odd');
result
const array = [1, 2, 3, 4, 5, 6];
const result = Object.groupBy(array, (num, index) => num % 2 === 0 ? `even_mod${index%4}`: `odd_mod${index%4}`);
result
const array = [1, 2, 3, 4, 5, 6];
const even = Symbol('even');
const result = Object.groupBy(array, (num) => num % 2 === 0 ? even: Symbol('odd'));
result
| groupBy 相似对比 | 方法类型 | 调用方式 | 返回值 |
|---|
| Object.groupBy | Object 静态方法 | Object.groupBy(iterable, callbackFn) | 普通对象 |
| Map.groupBy | Map 静态方法 | Map.groupBy(iterable, callbackFn) | Map 对象 |
| Object.groupBy兼容性 | |
|---|
| Chrome | Chrome 117 |
| Edge | Edge 117 |
| Firefox | Firefox 119 |
| Safari | Safari 17.4 |
| Node | Node.js 21 |
值比较
严格值比较
| 方法 | 描述 |
|---|
| Object.is | 返回一个布尔值,比较两个值是否严格相等,与 === 类似,但处理了 NaN 和 +0 、 -0 的特殊情况。 |
| Object.is 接受两个参数 |
|---|
| 严格接受且仅处理前两个参数。 |
| 参数传入 ≥2 个:只会生效前两个参数,其余参数会被忽略。 |
| 参数传入 <2 个:缺失的参数会被视为 undefined。0 个参数时,相当于 undefined 相互比较;1个参数时,相当于参数与 undefined 相比较。 |
| Object.is 方法不接受回调函数 |
| Object.is 返回一个布尔值 |
|---|
| 若参数都是NaN,则两个值相同。返回 true。 |
| 若参数一个+0,一个-0,则两个值不同。返回 false。 |
| 若参数都是symbol,即使描述相同,Symbol(‘a’) !== Symbol(‘a’) ,因此必须是同一个 Symbol 变量引用,两个值才相同,返回 true,否则返回 false。 |
| 若参数是引用类型,不同数组/对象即使内容相同,若不是同一个变量引用 ,返回 false。 |
Object.is([], []);
const foo = { at: '不服你不说' }, bar = { at: '不服你不说' };
const sameFoo = foo;
Object.is(foo, foo);
Object.is(foo, bar);
Object.is(foo, sameFoo);
Object.is(+0, -0);
Object.is(-0, -0);
+0 === -0;
Object.is(NaN, NaN);
Object.is(NaN, 0 / 0);
Object.is(NaN, Number.NaN);
NaN === NaN;
应用与注意事项
应用场景
- 配置合并:使用
Object.assign() 合并默认配置和用户配置 - 数据转换:使用
Object.entries() + Object.fromEntries() 进行对象转换 - 数据分组:使用
Object.groupBy() 对数组数据进行分类统计 - 严格比较:使用
Object.is() 进行包含 NaN 和 ±0 的精确比较
注意事项
- 性能考虑:
Object.assign() 是浅拷贝,嵌套对象需递归处理 - Symbol 处理:Symbol 键属性在遍历和转换时需特殊处理
- 兼容性:
Object.groupBy() 需要较新浏览器/Node.js 版本支持 - 类型转换:注意基本类型参数的自动转换行为
- 错误处理:了解各方法对
undefined/null 的不同处理方式
知识题
| 题目 | 答案 |
|---|
| Object.values会保留属性名为 Symbol 值的属性么 | 不保留 ,Object.values 不保留 Symbol 类型的属性名 |
| Object.fromEntries会保留属性名为 Symbol 值的属性么 | 能保留 ,Object.fromEntries 能保留 Symbol 类型的属性名 |
| Object.fromEntries 是 Object.entries() 的逆操作,Object.entries 能完整恢复 Object.fromEntries 处理的数据 | 不能,Object.fromEntries 创建的 Symbol 键属性无法通过 Object.entries 完整恢复。 |
| new Boolean(false ) / Object(false ) / Boolean(false)的typeof 的结果是否一致 | 一致,都是 object |
| new String(‘266’) / Object(‘266’) / String(‘266’)的typeof 的结果是否一致 | 一致,都是 object |
| BigInt(266n) / Object(266n ) 的typeof 的结果是否一致 | 不一致,BigInt(45n) 为 bigint, Object(45n ) 为 object |
| new Number(266) / Object(266) /Number(266) 的typeof 的结果是否一致 | 一致,都是 object |
| Symbol(“test”) / Object(Symbol(“test”)) 的typeof 的结果是否一致 | 不一致,Symbol(“test”) 为 symbol, Object(Symbol(“test”)) 为 object |
| new Function() / Object(function(){}) 的typeof 的结果是否一致 | 一致,都是 function |
| Object.assign(45n) 的typeof 的结果 与 BigInt(45n) / Object(45n ) 的typeof 的结果 哪个是一致的 | Object.assign(45n) 的typeof 的结果是 object, 与 Object(45n )的 结果一致 |
| new Boolean(false) / Object(false) 与 false 在 if 语句中的区别 | new Boolean(false) / Object(false) 是一个对象,在 if 语句中会被判定为 true(因为所有对象都是真值),这与原始值 false 的行为截然不同 |