# ES6 字符串、数组、对象的新增方法和常见应用

TIP

本章我们来学习 ES6 中为字符串、数组、对象新增的一些方法和应用

# 一、字符串的新增方法

方法 说明
padEnd 用一个字符串从当前字符串的末尾来填充当前字符串,使字符串达到指定长度。返回结果为填充后的新字符串。
padStart 用一个字符串从当前字符串的左侧来填充当前字符串,使字符串达到指定长度。
trimStart 方法会删除字符串开头的空白字符
trimEnd 方法会删除字符串末尾的空白字符
trim 方法从字符串的两端清除空格
startsWith 方法用来判断当前字符串是否以另外一个给定的子字符串开头
endsWith 方法用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的
repeat 构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本

# 1、padStart

TIP

用一个字符串从当前字符串的左侧来填充当前字符串,使字符串达到指定长度。

返回结果为填充后的新字符串

语法

padEnd(targetLength[, padString])
  • targetLength 当前字符串需要填充到的目标长度
  • padString 填充字符串,如果需要的话,会复复多次来填充

基本用法

console.log("abcd".padStart(7, "*")); // ***abcd
console.log("abcd".padStart(7, "@#")); // @#@abcd

注意事项

  • 如果targetLength的值 <= 当前字符串的长度,则返回当前字符串本身
console.log("abcd".padStart(4, "*")); // abcd
  • 如果 padString 长度过长,只会保留最左侧部分,其他部分会被截断
console.log("abcd".padStart(8, "01234")); // 0123abcd

# 2、padEnd

TIP

用一个字符串从当前字符串的末尾来填充当前字符串,使字符串达到指定长度。

返回结果为填充后的新字符串

语法

padEnd(targetLength[, padString])
  • targetLength 当前字符串需要填充到的目标长度
  • padString 填充字符串,如果需要的话,会复复多次来填充

基本用法

let str = "清心".padEnd(6, "爱你");
console.log(str); // 清心爱你爱你

let str2 = "清心".padEnd(5, "52");
console.log(str2); // 清心525

注意事项

  • 如果 targetLength 的值 <= 当前字符串的长度,则返回当前字符串本身
let str = "清心".padEnd(2, "爱你");
console.log(str); // 清心
  • 如果 padString 长度过长,只会保留最左侧部分,其他部分会被截断
str = "清心".padEnd(5, "520def");
console.log(str); // 清心520

案例应用

将以下 map 数据,在控制台以以下图形式显示出来

const map = new Map([
  ["luobo", 5],
  ["baicai", 2.5],
  ["doujiao", 3],
  ["huacai", 5],
]);

image-20230208170954825

map.forEach((v, k) => {
  console.log(`${k.padEnd(20, "-")}${v}`);
});

# 3、trimStart

TIP

trimStart() 方法会删除字符串开头的空白字符。trimLeft() 是此方法的别名

let str = "   love   ";
console.log(str.length); // 10
str = str.trimStart(); // 去掉前面的空白字符
console.log(str); // "love   "
console.log(str.length); // 7

# 4、trimEnd

TIP

trimEnd() 方法会删除字符串末尾的空白字符。trimRight() 是这个方法的别名。

let str = "   love   ";
console.log(str.length); // 10
str = str.trimEnd(); // 去掉字符串后面的空白字符
console.log(str); // "   love"
console.log(str.length); // 7

# 5、trim

TIP

trim() 方法从字符串的两端清除空格,返回一个新的字符串,而不修改原始字符串

let str = "   love   ";
console.log(str.length); // 10
str = str.trim();
console.log(str); // "love"
console.log(str.length); // 4

# 6、startsWith

TIP

startsWith() 方法用来判断当前字符串是否以另外一个给定的子字符串开头。如果是返回 true,否则返回false

注意:此方法,严格区分大小写

语法

str.startsWith(searchString[, position])
  • searchString 要搜索的子字符串
  • position 可选参数 在 str 中搜索 searchString 的开始位置,默认值为 0

基本用法

console.log("data_iconfont".startsWith("data_")); // true
console.log("data_iconfont".startsWith("data-")); // false
console.log("data_iconfont".startsWith("icon", 4)); // false
console.log("data_iconfont".startsWith("icon", 5)); // true

# 8、endsWith

TIP

endsWith() 方法用来判断当前字符串是否是以另外一个给定的子字符串“结尾”的。

如果是,返回结果为true,否则为 false

注意:此方法,严格区分大小写

语法

str.endsWith(searchString[, length])
  • searchString : 要搜索的子字符串
  • length : 作为 str 的长度。默认值为 str.length

基本用法

console.log("data_iconfont".endsWith("font")); // true
console.log("data_iconfont".endsWith("t")); // true
console.log("data_iconfont".endsWith("con", 9)); // true
console.log("data_iconfont".startsWith("con", 10)); // false

# 9、repeat 方法

TIP

构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。

语法

str.repeat(count);

介于 0+Infinity之间的整数。表示在新构造的字符串中重复了多少遍原字符串。

// "abc".repeat(-1); // 报错
console.log("abc".repeat()); // ""
console.log("abc".repeat(0)); // ""
console.log("abc".repeat(1)); // abc
console.log("abc".repeat(2)); // abcabc
console.log("abc".repeat(3)); // abcabcabc

# 二、数组新增方法

数组相关的实例方法

实例方法 说明 是否更改原数组
map 创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
some 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试
every 测试一个数组内的所有元素是否都能通过某个指定函数的测试
reduce reducer 逐个遍历数组元素,每一步都将当前元素的值与上一步的计算结果做相关操作,一下到没有更多元素相加
reduceRight 与 reduce 一样,只是他默认的从数组的尾元素开始,即从右向左
keys 返回一个遍历器对象,用来遍历所有的键名
values 返回一个遍历器对象,用来遍历所有的键值
entries 返回一个遍历器对象,用来遍历**[键名,键值]**组成的数组
fill 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
copyWithin 访方法浅复制数组的一部分到同一数组中的另一个位置,会覆盖原数组成员,但不会改变原数组的长度。 返回改变后的原数组
flat flat 方法用于将数组按指定层级来扁平化(展开)。返回值为一个新的数组
flatMap flatMap 方法相当于数组的 map 方法和 flat 方法的合并用法
at 返回数组指定索引的元素。索引值允许正数和负数。

关于数组的:includes、find、findIndex 方法,之前讲完,这里就不再重复

数组的静态方法

静态方法 说明
Array.from 对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
Array.of 通过可变数量的参数创建一个新的 Array 实例

# 1、map 方法

TIP

map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。

语法

map(function (element, index, array) {
  /* … */
}, thisArg);
// 或
map((element, index, array) => {});
  • 回调函数中的三个参数
    • element 表示数组中正在处理的当前元素
    • index 数组中正在处理的当前元素的索引
    • array 调用 map 方法的数组
  • thisArg 可选参数,用来更改回调函数中的 this 指向,如果回调函数为箭头函数,则 this 更改无效

基础应用

  • 将数组中每个元素乘以 2
const arr = [1, 2, 3];
const newArr = arr.map((value) => value * 2);
console.log(newArr); // [2, 4, 6]
  • 求数组中每个元素的平方
const arr = [1, 2, 3];
const newArr = arr.map((value) => Math.pow(value, 2));
console.log(newArr); // [1, 4, 9]
  • 获取字符串中每个字符对应的 ASCII 码的映射关系
const str = "hello";
const charCods = Array.prototype.map.call(str, (value) => {
  return {
    [value]: value.charCodeAt(0),
  };
});
console.log(charCods);

image-20230207201829381

回调函数中 this 指向

const arr = ["a", "b", "c"];
const obj = { a: 1, b: 2 };
arr.map(() => {
  console.log(this); // window
}, obj);

arr.map(function () {
  console.log(this); // { a: 1, b: 2 }
}, obj);

# 2、some 方法

TIP

some() 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。

即:只要有一个通过测试,返回值就为 true,否则为 false

语法

some(function (element, index, array) {
  /* … */
}, thisArg);
// 或
some((element, index, array) => {
  /* … */
});
  • 回调函数中的三个参数
    • element 表示数组中正在处理的当前元素
    • index 数组中正在处理的当前元素的索引
    • array 调用 map 方法的数组
  • thisArg 可选参数,用来更改回调函数中的 this 指向,如果回调函数为箭头函数,则 this 更改无效

基本用法

  • 测试数组中是否存在偶数
const result = [1, 3, 5, 7, 9].some((value) => value % 2 === 0);
console.log(result); // false

const result2 = [1, 4, 5, 7, 9].some((value) => value % 2 === 0);
console.log(result2); // true
  • 检测以下数组中,蔬菜价格是否有大于 10 元的
const arr = [
  { name: "白菜", price: 7 },
  { name: "西蓝花", price: 11 },
  { name: "豆角", price: 9 },
];

const result = arr.some((v) => v.price >= 10);
console.log(result); //true

# 3、every 方法

TIP

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试

即:只有数组中所有元素通过测试,返回值才为 true,否则为 false

语法

every(function (element, index, array) {
  /* … */
}, thisArg);
// 或
every((element, index, array) => {
  /* … */
});
  • 回调函数中的三个参数
    • element 表示数组中正在处理的当前元素
    • index 数组中正在处理的当前元素的索引
    • array 调用 map 方法的数组
  • thisArg 可选参数,用来更改回调函数中的 this 指向,如果回调函数为箭头函数,则 this 更改无效

基本用法

  • 检测数组中是否所有元素都小于 100
const result1 = [20, 30, 40, 50, 120].every((v) => v < 100);
console.log(result1); // false

const result2 = [20, 30, 40, 50, 10].every((v) => v < 100);
console.log(result2); // true

# 4、reduce 方法

TIP

reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。

否则,数组索引为 0 的元素将被作为初始值 initialValue,迭代器将从第二个元素开始执行(索引为 1 而不是 0)

语法

reduce(function (previousValue, currentValue, currentIndex, array) {
  /* … */
}, initialValue);
  • initialValue 可选参数,作为第一次调用 callback 函数时参数 previousValue 的值

回调函数中的 4 个参数,分别表示:

参数 说明
previousValue 上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]
currentValue 数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]
currentIndex 数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始
array 用于遍历的数组

基本用法

  • 没有传入 initialValue 参数时,第一次调用 callBackFn,即previousValue = array[0]currentValue = array[1]
const result = [1, 2, 3, 4].reduce(
  (previousValue, currentValue, currentIndex, array) => {
    console.log(previousValue, currentValue, currentIndex, array);
    return previousValue;
  }
);
回调函数调用 previousValue currentValue currentIndex array 返回值
第一次 1 2 1 [1, 2, 3, 4] 1
第二次 1 3 2 [1, 2, 3, 4] 1
第三次 1 4 3 [1, 2, 3, 4] 1

最终返回值为最后一次回调函数的返回值: 1

  • 传入 initialValue 参数时,即previousValue = initialValuecurrentValue = array[0]
const result = [1, 2, 3, 4].reduce(
  (previousValue, currentValue, currentIndex, array) => {
    console.log(previousValue, currentValue, currentIndex, array);
    return previousValue;
  },
  10
);
console.log(result);
回调函数调用 previousValue currentValue currentIndex array 返回值
第一次 10 1 0 [1, 2, 3, 4] 10
第二次 10 2 1 [1, 2, 3, 4] 10
第三次 10 3 2 [1, 2, 3, 4] 10
第四次 10 4 3 [1, 2, 3, 4] 10

最终返回值为最后一次回调函数的返回值: 10

案例应用

  • 求数组中所有元素之和
const result = [10, 20, 30, 40].reduce((previousValue, currentValue) => {
  return previousValue + currentValue;
});
console.log(result);
回调函数调用 previousValue currentValue currentIndex array 返回值
第一次 10 20 1 [1, 2, 3, 4] 30
第二次 30 30 2 [1, 2, 3, 4] 60
第三次 60 40 3 [1, 2, 3, 4] 100

最终返回值为:100

  • 数组去重
const result = [1, 2, 2, 3, 3, 4, 5].reduce((prev, current) => {
  if (!prev.includes(current)) {
    prev.push(current);
  }
  return prev;
}, []);
console.log(result); // [1, 2, 3, 4, 5]
  • 求数组中最大值
const result = [1, 2, 10, 4, 5, 40].reduce((prev, current) => {
  return Math.max(prev, current);
});
console.log(result); // 40
  • 计算数组中每个元素出现的次数
const result = ["a", "b", "a", "c", "b", "d"].reduce((prev, current) => {
  prev.set(current, (prev.get(current) || 0) + 1);
  return prev;
}, new Map());
console.log(result); // Map(4) {'a' => 2, 'b' => 2, 'c' => 1, 'd' => 1}

更多用法 参考 MDN 官方文档 (opens new window)

# 5、keys、values、entries 方法

方法 说明
keys() 返回一个遍历器对象,用来遍历所有的键名
values() 返回一个遍历器对象,用来遍历所有的键值
entries() 返回一个遍历器对象,用来遍历 [键名,键值] 组成的数组。
const arr = ["a", "b", "a"];

// 遍历数组的所有键名
for (let k of arr.keys()) {
  console.log(k); // 0 1 2
}
// 遍历数组的所有键值
for (let v of arr.values()) {
  console.log(v); // a b a
}
// 遍历由数组的 [键名,键值] 组成的数组,然后参于数组的解构赋值
for (let [k, v] of arr.entries()) {
  console.log(`${k} => ${v}`);
  // 结果
  // 0 => a    1 => b   2 => a
}

# 6、fill 方法

TIP

fill()方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。

返回结果:修改后的数组。

语法

fill(value, start, end); // 参数 start 和 end为可选参数
  • value:用来填充数组元素的值
  • start:可选参数,起始索引,默认值为 0 , 如果值为负数,则索引值会被自动计算为数组length+start
  • end: 可选参数,终止索引,默认值为 arr.length, 如果值为负数,则索引值会被自动计算为数组length+start

基本用法

console.log([1, 2, 3].fill("a")); //  ['a', 'a', 'a']
console.log([1, 2, 3].fill("a", 1)); // [1, 'a', 'a']
console.log([1, 2, 3, 4, 5].fill("a", 1, 4)); //  [1, 'a', 'a', 'a', 5]
console.log([1, 2, 3, 4, 5].fill("a", -4, -1)); // [1, 'a', 'a', 'a', 5]
console.log([1, 2, 3, 4, 5].fill("a", -1, -4)); //  [1, 2, 3, 4, 5]

注意事项

  • fill()方法被设计为一个通用方法,其方法内部的 this 不一定非要是数组,可以利用 call 来修改,也就是说,fill()方法,可以用来操作类数组对象
console.log([].fill.call({ a: 1, b: 2, c: 3 }));
console.log([].fill.call({ a: 1, b: 2, c: 3 }, 8));
console.log([].fill.call({ a: 1, b: 2, c: 3, length: 3 }, 8));
console.log([].fill.call({ length: 3 }, 8));

image-20230208150627405

  • fill() 中用来填充的数据,如果是一个引用类型,会导致执行同一个引用
const arr = [1, 2, 3, 4];
arr.fill({}, 1);
console.log(arr); // [1,{},{},{}]

arr[1].a = 1;
console.log(arr); // [1,{a:1},{a:1},{a:1}]

# 7、copyWithin

TIP

copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,会覆盖原有成员。不会改变原数组的长度。

返回结果为改变后的原数组

语法

copyWithin(target, start, end);
  • target 目标索引,从该位置开始替换数据
    • 如果为负数,其值相当于等于 arr.length + start
    • 如果大于 arr.length,将不发生拷贝。
    • 如果在 start 和 end 之后,复制的序列将被修改以符合 arr.length
  • start 可选,从该位置开始读取数据。默认值为 0 , 如果为负数,其值等于arr.length + start
  • end 可选,到该 位置停止读取数据。默认值为arr.length, 如果为负数,其值等于 arr.length + end

基本用法

[1, 2, 3, 4, 5].copyWithin(0, 2); //  [3, 4, 5, 4, 5]
[1, 2, 3, 4, 5, 6].copyWithin(2, 3, 5); // [1, 2, 4, 5, 5, 6]
[1, 2, 3, 4, 5, 6].copyWithin(4, 2, 5); // [1, 2, 3, 4, 3, 4];
[1, 2, 3, 4, 5, 6].copyWithin(1, -4, -2); // [1, 3, 4,4, 5, 6];
[1, 2, 3, 4, 5, 6].copyWithin(1, -2, -4); //  不变 [1, 2, 3, 4, 5, 6]
  • copyWithin方法,利用 call 方法改变内部 this,也可以用来操作类数组对象
let obj = { 0: "a", 1: "b", 2: "c", 3: "d", 4: "e", length: 5 };
obj = [].copyWithin.call(obj, 0, 1, 3);
console.log(obj); // {0: 'b', 1: 'c', 2: 'c', 3: 'd', 4: 'e', length: 5}

# 8、flat 方法

TIP

flat 方法用于将数组按指定层级来扁平化(展开)。

返回值为一个新的数组

语法

flat(depth);
  • depth 可选参数,默认值为 1 ,用来指定数组的展开层级。 如果值 <= 0,表示不展开

基本应用

  • 扁平化嵌套数组
const arr1 = [1, [2, [3, 4]], 5];
const arr2 = arr1.flat();
console.log(arr2);
console.log(arr1 === arr2); // false

console.log([1, [2, [3, 4]], 5].flat(2)); //  [1, 2, 3, 4, 5]

// 使用 Infinity,可以展开任意深度的嵌套
const arr = [1, [2, [3, 4, [5, [6], 7], 8], 9]];
console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 移除数组中的空项
const arr = [1, , , [, 2, 3, ,], 4];
console.log(arr.flat(0)); // [1,[, 2, 3, ,], 4]
console.log(arr.flat()); // [1, 2, 3, 4]

# 9、flatMap 方法

TIP

flatMap 方法相当于数组的 map 方法和 flat 方法的合并用法。

数组调用flatMap方法,相当于先调用数组的map,然后再将返回的结果数组再调用 flat() 方法展开,展开深度为 1。

返回值为一个新的数组。

语法

flatMap(function (currentValue, index, array) {
  /* … */
}, thisArg);
  • callBack 回调函数的三个参数
    • currentValue: 当前正在数组中处理的元素
    • index:可选的。数组中正在处理的当前元素的索引
    • array:可选的。被调用的 map 数组
  • thisArg 可选的,用来更改 callBack 回调函数中的 this 指向

基本用法:

const arr = [1, 2, 3, 4];
const res = arr.flatMap((x) => [x * 2]);
console.log(res);

// 上面代码 arr.flatMap((x) => [x * 2]) 的内部相当于执行了以下两步
const arr2 = arr.map((x) => [x * 2]);
const res2 = arr2.flat();
console.log(res2);
  • 将几句话的数组拆份成单个词组成的新数组
const arr = ["Happy New Year", "May you be happy and prosperous"];
const word = arr.flatMap((v) => v.split(" "));
console.log(word); // ['Happy', 'New', 'Year', 'May', 'you', 'be', 'happy', 'and', 'prosperous']

# 10、at 方法

TIP

返回数组指定索引的元素。索引值允许正数和负数。 负数表示从末尾开始。

语法

at(index); // index为整数,正数负数都可以

基本用法

[1, 2, 3].at(1); // 2
[1, 2, 3].at(-1); // 3

# 11、Array.from 方法

TIP

Array.form()方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

返回值:一个新的数组实例

语法

Array.from(
  arrayLike,
  function mapFn(element, index) {
    /* … */
  },
  thisArg
);

参数

  • arrayLike: 想要转换成数组的伪数组对象或可迭代对象
  • mapFn: 如果指定了该参数,新数组中的每个元素会执行该回调函数。 相当新生成的数组实例,再调用 map 方法
  • thisArg:可选参数,执行回调函数 mapFnthis 对象。 默认 this 指向window

基本应用

  • string 类型生成数组
Array.from("hello");
// ['h', 'e', 'l', 'l', 'o']
  • Set 生成数组
const set = new Set(["a", "b", "c", "d"]);
console.log(Array.from(set)); // ['a', 'b', 'c', 'd']
  • Map 生成二维数组
const map = new Map([
  ["a", 1],
  ["b", 2],
  ["c", 3],
]);
console.log(Array.from(map)); // [["a", 1],["b", 2],["c", 3]]

// 传入第二个参数
let res = Array.from(map, (v) => v[1]);
console.log(res); // [1, 2, 3]

// 传入第三个参数,修改this指向
let res = Array.from(map, (v, i) => this[i], ["foo", "bar", "zoo"]);
console.log(res); // [undefined, undefined, undefined]

// 修改this只能是普通函数
let res2 = Array.from(
  map,
  function (v, i) {
    return this[i];
  },
  ["foo", "bar", "zoo"]
);
console.log(res2);

# 12、Array.of 方法

TIP

Array.of() 方法通过可变数量的参数创建一个新的 Array 实例,而不考虑参数的数量或类型。

语法

Array.of(element0, element1, /* … ,*/ elementN);

基本用法

Array.of(); // []
Array.of(1); // [1]
Array.of(1, 2, 3); // [1, 2, 3]

此方法主要目的是:

弥补数组构造函数 Array() 的不足

Array.of()Array() 构造函数之间的区别在于对单个参数的处理:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个 length7 的空数组

这意味着一个由 7 个空槽组成的数组,而不是具有实际 undefined 值的槽

Array.of(7); // [7]
new Array(7); //  [empty × 7]

模拟 ArrayOf 方法

Array.of = function () {
  return [].slice.call(arguments);
};

# 三、对象新增方法

对象的静态方法

静态方法 说明
Object.is() 方法判断两个值是否为同一个值
Object.assign() 方法用于将源(source)对象的所有可枚举的自有属性复制到目标 target 对象
Object.freeze 方法可以冻结一个对象
Object.keys() 返回一个由一个给定对象的自身可枚举属性组成的数组
Object.values() 返回一个给定对象自身的所有可枚举属性值的数组
Object.entrie() 返回一个给定对象自身可枚举属性的键值对数组
Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符
Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符
Object.getOwnPropertyNames() 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组
Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组
Object.fromEntries() 方法把键值对列表转换为一个对象
Object.setPrototypeOf() 设置一个指定的对象的原型(即,内部 [[Prototype]] 属性)到另一个对象或 null
Object.getPrototypeOf() 方法返回指定对象的原型

# 1、Object.is 方法

TIP

Object.is() 方法判断两个值是否为同一个值。如果是同一个值,返回 true,否则返回 false。

其判断标准与===相似,唯一的差别在于他两对零值 和 NaN 的判断不同

  • ===中认为0,-0,+0是同一个值,NaN 和 NaN 是不同的值
  • Object.is()中认为0,-0,+0 三者中只有0、+0是同值
console.log(Object.is(1, 1)); // true
console.log(Object.is(true, 1)); // false
console.log(Object.is([], [])); // false

console.log(NaN === NaN); // false
console.log(0 === -0); // true
console.log(0 === +0); // true
console.log(-0 === +0); // true

console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(0, -0)); // false
console.log(Object.is(0, +0)); // true
console.log(Object.is(-0, +0)); // false

# 2、Objec.assign 方法

TIP

Object.assign()方法用于将源(source)对象的所有可枚举自有属性复制到目标 target 对象。

返回值:为 target 目标对象

语法

Object.assign(target, sources1, sources1,...)
  • target:目标对象,接收源对象属性的对象,也是修改后的返回值
  • sources1, sources1,...: 为源对象

基本用法

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
Object.assign(obj1, obj2);
console.log(obj1); // {a: 1, b: 2, c: 3, d: 4}

// 注意区分以下写法的不同
Object.assign(obj1, obj2); // 将obj2合并到obj1
const obj = { ...obj1, ...obj2 }; // 将obj1和obj2合并到一个新对象,然后赋值给obj

// 用Object.assign实现 const obj= {...obj1,...obj2} 写法如下
const obj = Object.assign({}, obj1, obj2);
  • 如果Object.assign方法只有一个参数
    • 该参数为一个对象,直接将该对象作为返回值返回。
    • 如果该参数不是一个对象类型,会先转换为对象类型,然后将其作为返回值返回。
    • 如果该参数是nullundefined,因为nullundefined不能转换为对象,所以会报错。
// 只有一个参数,且为对象类型,直接将该对象作为返回值返回
const obj1 = Object.assign({ a: 1, b: 2 });
console.log(obj1); // {a: 1, b: 2}

//只有一个参数,且为基本数据类型,将基转换为对象然后作为返回值返回
const obj2 = Object.assign(2);
console.log(obj2); // Number {2}

// 由于null和undefined不能转换为对象,者报错
const obj3 = Object.assign(null); // 报错
const obj4 = Object.assign(undefined); // 报错
  • Object.assign方法的源对象位置的参数
    • 如果是非对象类型的参数,则会将其自动转换为对象
    • 如果是nullundefined,则会直接忽略,并不会报错
const obj1 = { a: 1, b: 2 };
Object.assign(obj1, 1, "hello", null, undefined, true);
console.log(obj1); // {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o', a: 1, b: 2}

# 3、Object.assign 的注意事项

TIP

如果目标对象与源对象具有相同的属性,则目标对象中的属性将被源对象属性覆盖。

const obj1 = {
  color: ["红色", "黑色"],
  a: 1,
  b: 2,
};
const obj2 = {
  color: ["白色", "蓝色"],
  b: 3,
  c: 4,
};

Object.assign(obj1, obj2);
console.log(obj1);

image-20230208191020024

  • 将源对象中的属性复制到目标对象,其本质是浅拷贝
const obj1 = {};
const obj2 = {
  arr: [1, 2],
};
Object.assign(obj1, obj2);
obj2.arr.push(3); // 修改obj2的arr属性,其相当于修我以为了obj1的arr属性
console.log(obj1.arr); //  [1, 2, 3]
  • Ojbect.assign方法将只能将源对象上的自身可枚举属性复制到目标对象,只要是自身的可枚举属性,其属性名为 Symbol 类型也会被复制
const obj = { a: 1, b: 2 };
const obj2 = { c: 3, [Symbol()]: "Symbol" };
// 为obj2添加两个自身属性
Object.defineProperties(obj2, {
  name: {
    value: "清心", // 属性值
    writable: true, // 可写
    configurable: false, // 不可重匹配
    enumerable: true, // 可枚举
  },
  age: {
    value: 33,
    writable: true,
    configurable: false,
    enumerable: false, // 不可格举
  },
});

console.log(obj2); // {c: 3, name: '清心', age: 33, Symbol(): 'Symbol'}
Object.assign(obj, obj2);
console.log(obj); // {a: 1, b: 2, c: 3, name: '清心', Symbol(): 'Symbol'}
  • 如果源对象上有 get 和 set 函数,其相当于新增一个属性合并到原型中,但没有 get 和 set 函数
const obj1 = {
  a: 1,
  b: 2,
};
const obj2 = {
  _x: "a",
  get x() {
    return this._x;
  },
  set x(value) {
    if (x === 3) {
      throw new Error("值不能为3");
    } else {
      this._x = value;
    }
  },
};

Object.assign(obj1, obj2);
console.log(obj1); // {a: 1, b: 2, _x: 'a', x: 'a'}
obj1.x = 3;
console.log(obj1.x);

# 4、Object.assign 的常见应用

TIP

常用合并默认参数和用户参数

// userOptions为用户参数
const userInfo = (userOptions) => {
  // 默认参数
  const DEFAULTS = {
    username: "icoding",
    age: 0,
    sex: "male",
  };

  // 合并默认参数和用户参数
  const options = Object.assign({}, DEFAULTS, userOptions);
  console.log(options);
};

userInfo(); // {username: 'icoding', age: 0, sex: 'male'}
userInfo({}); // {username: 'icoding', age: 0, sex: 'male'}
userInfo({ username: "arry" }); // {username: 'arry', age: 0, sex: 'male'}
userInfo({ username: "清心", age: 18, sex: "female" }); // {username: '清心', age: 18, sex: 'female'}

# 6、keys、values、entries

方法 说明
keys 返回一个由一个给定对象的自身可枚举属性(不包括 Symbol 类型)组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致
values 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用 for...in 循环的顺序相同(区别在于 for-in 循环枚举原型链中的属性)。
entries 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性
const obj = {
  a: 1,
  b: 2,
  c: 3,
};
// 定义自身属性
Object.defineProperties(obj, {
  name: {
    value: "清心",
    enumerable: true,
  },
  age: {
    value: 33,
  },
});

const keys = Object.keys(obj);
console.log(keys); // ['a', 'b', 'c', 'name']

const values = Object.values(obj);
console.log(values); // [1, 2, 3, '清心']

const entries = Object.entries(obj);
console.log(entries); // [Array(2), Array(2), Array(2), Array(2)]

for...of 的结合应用

const obj = {
  a: 1,
  b: 2,
  c: 3,
};
// 遍历对象的所有自身可枚举属性名
for (let key of Object.keys(obj)) {
  console.log(key);
}
// 遍历对象的所有自身可枚举属性值
for (let values of Object.values(obj)) {
  console.log(values);
}
// 遍历对象的属性名与对应的属性值
for (let [key, value] of Object.entries(obj)) {
  console.log(`${key} => ${value}`);
}

注意和数组、Set、Map 的 keys、values、entries 方法对比

调用方式的不同

  • 对象是构造函数的方法,将对象传入的方式 Object.keys(obj)
  • 数组是实例的方法,通过实例对象就可以调用方法 [1, 2, 3].keys() ,而对象是没有的

返回值的不同

  • 对象的Object.keys()Object.values()Object.entries() 等方法是构造函数方法,返回的都是数组
  • 数组的keys()values()entries() 等方法是实例方法,返回的统一都是数组的遍历对象 Iterator
console.log([1, 2, 3].keys()); // Array Iterator {}
console.log([1, 2, 3].values()); // Array Iterator {}
console.log([1, 2, 3].entries()); // Array Iterator {}

# 7、Object.getOwnPropertyDescriptor

TIP

方法返回指定对象上一个自有属性对应的属性描述符,如果对象身上有这个属性,返回其属性描述符对象,否则返回undefined

语法

Object.getOwnPropertyDescriptor(obj, prop);
// obj 需要查找的目标对象
// prop 目标对象内属性名称
const obj = {
  name: "清心",
  get sex() {
    return this._sex;
  },
  set sex(value) {
    this._sex = value;
  },
};
Object.defineProperty(obj, "age", {
  value: 33,
  configurable: false,
  enumerable: true,
  writable: false,
});

let desc1 = Object.getOwnPropertyDescriptor(obj, "name");
console.log(desc1);
let desc2 = Object.getOwnPropertyDescriptor(obj, "sex");
console.log(desc2);
let desc3 = Object.getOwnPropertyDescriptor(obj, "age");
console.log(desc3);

image-20230210172810537

# 8、Object.getOwnPropertyDescriptors

TIP

获取一个对象的所有自身属性的描述符。如果没有属性,则返回空对象

语法

const obj = {
  name: "清心",
  get sex() {
    return this._sex;
  },
  set sex(value) {
    this._sex = value;
  },
};
Object.defineProperty(obj, "age", {
  value: 33,
  configurable: false,
  enumerable: true,
  writable: false,
});

let descAll = Object.getOwnPropertyDescriptors(obj);
console.log(descAll);

image-20230210171959839

浅拷贝一个对象

Object.assign()方法只能拷贝源对象的可枚举的自身属性,同时拷贝时无法拷贝属性的特性们,而且访问器属性会被转换成数据属性,也无法拷贝源对象的原型。

Object.getOwnPropertyDescriptors 方法配合 Object.create() 方法可以实现上面说的这些。

Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);
const obj = {
  name: "清心",
  get sex() {
    return this._sex;
  },
  set sex(value) {
    if (value < 33) {
      throw new Error("年龄不符");
    } else {
      this._sex = value;
    }
  },
};
Object.defineProperty(obj, "age", {
  value: 33,
  configurable: false,
  enumerable: true,
  writable: false,
});

const obj2 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
);
console.log(obj2);
obj2.sex = 22;

image-20230210172539691

# 9、Object.getOwnPropertyNames

TIP

返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。

方法 原型 自身 自身 Symbol 可枚举 不可枚举
Object.getOwnPropertyNames

语法

Object.getOwnPropertyNames(obj);
// 在 ES2015 中,非对象参数被强制转换为对象
const obj = {
  [Symbol()]: "Symbol",
  name: "清心",
};
Object.defineProperty(obj, "sex", {
  value: "女",
});

const desc = Object.getOwnPropertyNames(obj);
console.log(desc); // ['name', 'sex']

如果只想获取对象的自身可枚举属性,可以使用 Object.keys 方法。

  • 在 ES2015 中,非对象参数被强制转换为对象
Object.getOwnPropertyNames(1); // []
Object.getOwnPropertyNames("hello"); //  ['0', '1', '2', '3', '4', 'length']

# 10、Object.getOwnPropertySymbols

TIP

返回一个给定对象自身的所有 Symbol 属性的数组

方法 原型 自身 自身 Symbol 可枚举 不可枚举
Object.getOwnPropertySymbols

语法

Object.getOwnPropertySymbols(obj);
const obj = {
  [Symbol()]: "Symbol",
  name: "清心",
};
Object.defineProperty(obj, "sex", {
  value: "女",
});

const desc = Object.getOwnPropertySymbols(obj);
console.log(desc); // [Symbol()]

# 11、四种获取对象属性方法的对比

方法 原型 自身 自身 Symbol 可枚举 不可枚举
Object.keys()
for...in
Object.getOwnPropertyNames
Object.getOwnPropertySymbols

# 12、Object.freeze

  • Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改。
const obj = {
  a: 1,
  b: 2,
  arr: [1, 2, 3],
};
Object.freeze(obj); // 冻结对象
obj.a = 3; // 修改属性值无效
delete obj.b; // 不能删除属 性
obj.c = 4; // 不能添加属性
console.log(obj); // {a: 1, b: 2, arr: [1, 2, 3]}
  • Object.freeze() 冻结对象,属于浅冻结。如果对象的属性是一个引用类型,修改属性值指向的对象的属性是可以。
const obj = {
  a: 1,
  b: 2,
  arr: [1, 2, 3],
};
Object.freeze(obj); // 冻结对象
obj.arr.push("abc");
console.log(obj.arr); // [1, 2, 3, 'abc']

深度冻结

以下代码来自 MDN 官方文档 (opens new window)

// 深冻结函数。
function deepFreeze(obj) {
  // 取回定义在 obj 上的属性名
  var propNames = Object.getOwnPropertyNames(obj);

  // 在冻结自身之前冻结属性
  propNames.forEach(function (name) {
    var prop = obj[name];

    // 如果 prop 是个对象,冻结它
    if (typeof prop == "object" && prop !== null) deepFreeze(prop);
  });

  // 冻结自身 (no-op if already frozen)
  return Object.freeze(obj);
}

obj2 = {
  internal: {},
};

deepFreeze(obj2);
obj2.internal.a = "anotherValue";
obj2.internal.a; // undefined

# 13、Object.fromEntries

TIP

Object.fromEntries() 方法把键值对列表转换为一个对象。其返回值为一个新的对象。

语法

Object.fromEntries(iterable);
// iterable可以理解为一个可迭代对象或一个迭代器对象。可迭代对象或迭代器对象的返回值必须是一个双元素数组。如:二维数组,Map,Object.entries()的返回值等

基本用法

  • 二维数组转对象
const arr = [
  ["a", 1],
  ["b", 2],
  ["c", 3],
];
const obj = Object.fromEntries(arr);
console.log(obj); // {a: 1, b: 2, c: 3}
  • Map 转 Object
let map = new Map([
  ["a", 1],
  ["b", 2],
  ["c", 3],
]);
const obj = Object.fromEntries(map);
console.log(obj); // {a: 1, b: 2, c: 3}
  • 迭代器对象转对象
function* gen() {
  yield ["a", 1];
  yield ["b", 2];
  yield ["c", 3];
}
const obj = Object.fromEntries(gen());
console.log(obj);

总结:

Object.fromEntries()Object.entries 是互逆的操作

上次更新时间: 6/8/2023, 9:23:17 PM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X