探索如何赋予对象迭代魔法,轻松实现非传统解构赋值的艺术

前言

今天下午在网上冲浪过程中看到这样一个问题

面试题:如何让 var [a, b] = {a: 1, b: 2} 解构赋值成功?

据说是某大厂面试题,于是我学习了一下这个问题,写下这篇文章记录一下。

学习过程

要想解决这个问题首先要知道什么是解构赋值。

解构赋值是ES6中一种将数组或对象的属性/元素值赋给变量的语法。

运行一下题目的代码得到下面的情况

原因是对象的解构赋值需要使用对应的属性名来匹配,而不是数组的形式。

比如var {a, b} = {a: 1, b: 2} 就可以成功实现结构赋值。

但是这么高贵的问题,你10秒就回答完了,回去等通知吧。

两个普通想法

  1. 将对象属性解构到数组变量中
    首先使用Object.keys获取对象的所有键,然后使用map方法根据这些键从对象中提取对应的值,最后将这些值解构到一个数组变量中。

    复制代码
         var arr = [a, b];
         var obj = { a: 1, b: 2 }
         // 获取对象的键数组
         var keys = Object.keys(obj)
         // 根据键获取对应的值
         var values = keys.map(key => obj[key])
         //数组解构赋值
         var [a, b] = values;
         console.log([a, b])
  1. 将数组元素解构到对象变量中
    创建了一个空对象obj,然后使用数组解构赋值直接将数组的元素赋值给对象的属性。

    复制代码
         var arr = [1, 2];
         var obj = {};
         [obj.a, obj.b] = arr; // 数组解构赋值到对象属性
         console.log(obj); // 输出: {a: 1, b: 2}

高级解法

在JavaScript中数据是具有迭代器属性的一种数据结构,而对象是不具有迭代器的一种数据结构。

最直白淳朴的想法,解构赋值成功只需要把右边也变成可以迭代的对象就可以了。

在 JavaScript 中,可迭代对象是指具有 Symbol.iterator 方法的对象。这个方法返回一个迭代器(Iterator)对象,它通过 next() 方法提供对可迭代对象中的每个元素的访问。

JavaScript中数组,Set,Map,字符串都是可以迭代的对象,任何有iterator接口的对象都是可迭代的,我们可以自定义一个有iterator接口的对象。

网友做法

复制代码
Object.prototype[Symbol.iterator] = function(){
    // 使用 Object.values(this) 方法获取对象的所有值,并返回这些值的迭代器对象
    return Object.values(this)[Symbol.iterator]()
}

这段代码是将 Object.prototype 上的 [Symbol.iterator] 方法重新定义为一个新的函数。新的函数通过调用 Object.values(this) 方法获取对象的所有值,并返回这些值的迭代器对象。

复制代码
        Object.prototype[Symbol.iterator] = function () {
            return Object.values(this)[Symbol.iterator]()
        }
        var [a, b] = { a: 1, b: 2 }
        console.log([a, b])

给Object对象原型添加迭代器属性之后成功实现var [a, b] = { a: 1, b: 2 }解构赋值

题目拓展

上面通过给Object对象原型添加迭代器属性返回对象的值实现var [a, b] = { a: 1, b: 2 }解构赋值,但是若题目改为var [a, b] = { b: 1, z: 4 },会出现key不对应导致的问题。

运行下面的代码

复制代码
        Object.prototype[Symbol.iterator] = function () {
            return Object.values(this)[Symbol.iterator]()
        }
        var [a, b] = { b: 1, z: 4 };
        console.log([a, b])

运行结果如图

输出结果a应该是undefined,b:1,但是实际结果仍旧是a:1,b:4。

只是将{ b: 1, z: 4 }转成数组之后直接赋值,但是没有考虑到key的对应关系

最终优化结果

生成器函数function* () {}定义了一个迭代器生成器。当该迭代器被调用时,它会返回一个可迭代对象,并且通过yield*语句将对象的值作为迭代器的值逐个产生出来。为了在解构赋值时考虑到对象键的对应关系,我们需要修改迭代器函数,使其按照对象的键顺序来生成值。我们可以使用Object.entries方法来获取对象的键值对数组,然后按照这个顺序来生成值。

复制代码
        Object.prototype[Symbol.iterator] = function* () {
            let entries = Object.entries(this); // 获取对象的键值对数组
            let arr = ['a', 'b']; // 假设这是我们关心的键的数组

            for (let entry of entries) {
                let key = entry[0]; // 获取键
                if (arr.includes(key)) { // 检查键是否在数组中
                    yield entry[1]; // 如果键在数组中,则生成对应的值
                }
            }
        };

        // 使用数组解构赋值
        var [a, b] = { a: 1, z: 2, c: 3 }; // 这里我们只关心键 'a'

        console.log([a, b]);

首先给Object.prototype添加了一个自定义的迭代器,它使用Object.entries来获取对象的键值对数组,并按照这个数组的顺序来生成值。然后使用数组解构赋值来尝试解构一个对象,但是由于对象的键顺序是'b', 'z',而我们只关心值'1',所以变量a被赋值为'1',而变量b因为没有对应的值而被赋值为undefined。

总结一下代码的步骤就是

  1. 添加迭代器: 通过给Object.prototype添加一个名为Symbol.iterator的方法,所有的对象都可以被迭代。
  2. 定义关心的键: 在迭代器内部定义一个数组arr其中包含迭代过程中关心的键名。
  3. 迭代对象属性: 使用Object.entries(this)获取对象的键值对数组,这里的this指的是调用迭代器的对象。
  4. 过滤属性: 在遍历键值对数组时,我们检查每个键是否在我们关心的键数组arr中。如果键存在,我们才生成对应的值。
  5. 解构赋值: 使用数组解构赋值来提取我们关心的键对应的值。由于迭代器已经过滤掉了不需要的属性,解构赋值只会得到我们想要的值。

额外的小问题:为什么上面的代码中是 let arr = ['a', 'b'];不是let arr = [a, b]?

let arr = [a, b]是错误的,在声明arr数组的时候,变量a,b没有定义,没有具体的值不能用来初始化数组。

错误的运行结果是a is not defined

总结

本文解决如何让 var [a, b] = {a: 1, b: 2} 解构赋值成功的问题的最佳实践大概是通过扩展Object.prototype来为所有对象添加一个自定义的迭代器,该迭代器允许我们以一种特定的方式遍历对象的属性。具体来说,这个迭代器会过滤掉我们不关心的属性,只返回我们指定键名对应的属性值。

相关推荐
范文杰2 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪3 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪3 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy3 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom4 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom4 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom4 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom4 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom4 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试
LaoZhangAI5 小时前
2025最全GPT-4o图像生成API指南:官方接口配置+15个实用提示词【保姆级教程】
前端