1. 让代码成立
题目:
// 使之成立
var [a, b] = {a:1, b:2}
console.log(a, b) // 输出1 2
这题比较抽象,许多人看的一脸懵。。。
分析
我们先来看一下原本的对象解构方法:
var {a, b} = {a:1, b:2}
console.log(a, b) // 输出1 2
我们知道在js中,数组是具有内置迭代器的可迭代对象。通过使用 for...of
循环,可以遍历数组中的每个元素:
var arr = [1, 2, 3, 4, 5, 6];
for (let i of arr) {
console.log(i); // 输出 1 2 3 4 5 6
}
那我们能不能去遍历对象来操作呢?
可以看到我们对普通对象使用for...of
方法会报错,它说明普通对象没有迭代属性,而数组是具有迭代属性Symbol.iterator
,我们可以在window上看到。
数组的原型:
那思路不就有了吗?能不能给对象加上迭代属性使得可以像数组一样迭代遍历,以数组的形式返回对象的值,并利用解构赋值语法将数组中的元素赋值给变量。
// 过程如下:
// {a:1, b:2} => [1, 2]
const [a, b] = [1 , 2]
console.log(a, b) // 输出1 2
迭代属性:Symbol.iterator
for...in
循环用于迭代对象的可枚举属性。它遍历对象及其原型链上所有可枚举的属性,包括继承的属性。通常用于遍历对象属性键值对。for...of
循环用于迭代可迭代对象(如数组、字符串、Set、Map 等)。它遍历对象的可迭代属性值,而不是属性键。它只能用于迭代实现了迭代器接口的对象。
关于迭代器的详细内容可以去es6官网查看:es6 中的 Iterator 和 for...of 循环
对象增加迭代器:
我们可以给对象的原型上添加迭代器[Symbol.iterator]
来实现对象的迭代遍历
Object.prototype[Symbol.iterator] = function() {
return Object.values(this)[Symbol.iterator]();
}
var [a, b] = {a: 1, b: 2};
console.log(a, b); // 输出 1 2
-
Object.values(this)
:调用了 Object.values 方法,它会返回一个给定对象自身的所有可枚举属性值的数组,它会返回 this 指向的对象中所有的值组成的数组。 -
[Symbol.iterator]
:使用了 Symbol.iterator 符号,它是在迭代器协议中使用的特殊符号。当在一个对象上调用[Symbol.iterator]
时,它会返回一个迭代器对象,使得该对象可以被迭代(比如在 for...of 循环中使用)。
所以,我们返回 Object.values(this)[Symbol.iterator]()
可以将对象的值转换为一个数组,然后获取该数组的迭代器对象。这个迭代器对象可以用于遍历数组中的值。
真就汗流浃背了。。。。
2. 数组去重
let arr = [1, 1, '2', 3, 1, 2,
{ name: '张三', id: { n: 1 }, a: undefined },
{ name: '张三', id: { n: 1 }, a: undefined },
{ name: '张三', id: { n: 2 } },
]
喂,醒一醒,这可是字节的题目,没你想的那么简单用set去重就可以了。。。
let newArr = [...new Set(arr)]
console.log(newArr);
可以看到我们去重并不完整,因为Set
只能去重原始类型的数据,对于引用类型没用。
方法一:子元素全部转化为字符串去重再转化为对象
// 转化为字符串
let arr2 = arr.map((item) => {
return JSON.stringify(item);
})
// 字符串去重
let newArr = new Set(arr2);
// 转化为对象
newArr = Array.from(newArr).map((item) => {
return JSON.parse(item)
})
console.log(newArr);
通过结果来看,这种方法是有缺陷的**,对于 undefined 它无法转化为字符串!
方法二:通过原型遍历
整体思路为: 从一个数组中移除重复的元素,并返回一个只包含唯一值的新数组。同时,它利用辅助函数equal
来判断两个值是否相等。
let arr = [1, 1, '2', 3, 1, 2,
{ name: '张三', id: { n: 1 }, a: undefined },
{ name: '张三', id: { n: 1 }, a: undefined },
{ name: '张三', id: { n: 2 } },
]
function uniqueArr(arr) {
let res = []
for (let item of arr) {
let isFind = false
// 对于每个元素,通过equal函数检查它是否与res数组中的任何元素相等:
for (let resItem of res) {
// 如果相等,则将isFind设置为 true,表示找到了重复项,跳出内层循环。
if (equal(item, resItem)) {
isFind = true
break;
}
}
// 如果不相等,则将该元素添加到 `res` 数组中。
if (!isFind) res.push(item);
}
// 返回去重结果
return res
}
function equal(v1, v2) {
// 判断两个值是否都是对象类型
if ((typeof v1 === 'object' && v1 !== null) && (typeof v2 === 'object' && v2 !== null)) { // 都是引用类型
// 属性数量不同,直接返回 false
if (Object.keys(v1).length !== Object.keys(v2).length)
return false
// 遍历第一个对象的属性,递归比较对应属性的值,如果有不相等的情况则返回 false。
for (let key in v1) {
if (v2.hasOwnProperty(key)) {
// 递归比较它们的属性。
if (!equal(v1[key], v2[key])) {
return false
}
}
else {
return false
}
}
// 如果两个对象完全相等(包括属性和属性值),则返回 true。
return true
}
else {
// 如果两个值不都是对象类型,则直接比较它们是否相等。
return v1 === v2
}
}
console.log(uniqueArr(arr))
结果: