likes
comments
collection
share

深入剖析大厂面试题:解构与迭代器

作者站长头像
站长
· 阅读数 39

面试题

面试官:你试试让以下代码成功完成解构。

var [myname,age]={myname:"张三",age:18}

我:大致看一眼,心想这不是相当的简单吗?这题是包拿下的。

我:怎么越看越不对劲了,好家伙,有点慌了。怎么和我见过的不一样。

var [a,b]=[1,2]//数组的解构
var { name, age } = { name: '张三', age: 18 }//对象的解构
var [myname,age]={myname:"张三",age:18}//这是什么东西

执行面试官给的代码会出现{(intermediate value)(intermediate value)} is not iterable错误,立马联想到迭代器。开始回忆。

迭代器

迭代器的介绍

迭代器是某些数据结构的属性,并不是方法。可以被遍历的数据结构就会有迭代器属性,例如数组、Map和Set等,但是对象没有自带的迭代器属性。

迭代器就是一个对象,这个对象有一个next方法。每次调用next方法时会返回一个包含valuedone键值对的对象,其中value的值为当前迭代到的元素值、done的值为布尔值,当done的值为false表示迭代没有完成,当done的值为true表示迭代完成。

手搓一个迭代器。

function createIterator(items) {
    var i = 0;
    return {
        next: function () {
            var done = i >= items.length
            var value = !done ? items[i++] : undefined
            return {
                done: done,
                value: value
            }
        }
    }
}

var iterator = createIterator([1, 2, 3])

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

结果为:

深入剖析大厂面试题:解构与迭代器

在手搓的迭代器中运用了闭包的原理。在createIterator方法运行完成后就应该会被垃圾回收机制给回收掉,但是iterator.next方法执行时会访问createIterator方法的执行上下文内的变量i,所以在createIterator方法的执行上下文被回收后仍然会在执行上下文栈上留下一个内存空间存储变量i。每调用一次next方法就会让变量i自增,实现往后遍历。在没有遍历完时调用next方法就会返回{ done: false, value: 当前迭代到的元素值 },在遍历完后再调用next方法就会返回{ done: true, value: undefined }

for...of

for...of是ES6新增加的循环结构。for...of循环的引入可以让遍历操作变得更加直接和易读,减少了样板代码,提高了代码质量。

for...of语法

iterable是可迭代的对象;variable是一个变量,在每次迭代中该变量都会被赋值为当前iterable中所迭代元素的值。

for (variable of iterable) {
    // 循环体内的代码,variable为当前迭代到的值
}

for...of原理

for...of执行时会对循环的数据结构进行判断,判断该数据结构是否具有可迭代性。

在JavaScript中存在一个特殊的方法——[Symbol.iterator]方法,可以通过调用[Symbol.iterator]方法获取迭代器。因此for...of执行时会判断循环的数据结构是否拥有[Symbol.iterator]方法,进而判断该数据结构是否具有可迭代性。

用一个例子理解[Symbol.iterator]方法:

function createIterator(items) {
    var i = 0;
    return {
        next: function () {
            var done = i >= items.length
            var value = !done ? items[i++] : undefined

            return {
                done: done,
                value: value
            }
        }
    }
}

const obj = {
    value: 1
}

obj[Symbol.iterator] = function () {
    return createIterator([1, 2, 3])
}

通过手搓一个迭代器和[Symbol.iterator]方法,让obj对象具有可迭代性。从例子中可以看出调用[Symbol.iterator]方法返回的值就是一个迭代器(一个对象)。

接下来看看通过给一个对象手搓一个迭代器,并且让该对象具有[Symbol.iterator]方法后是否可以执行for...of循环。

for (let value of obj) {
    console.log(value);
}

结果为:

深入剖析大厂面试题:解构与迭代器

手搓一个函数(不太严谨)模拟for...of的逻辑。

function forOf(obj, cb) {
    if (obj[Symbol.iterator]) {
        throw new TypeError(obj + "is not iterable")
    }
    let iterator = obj[Symbol.iterator]()
    let res = iterator.next()
    while (!res.done) {
        cb(res.value)
        res = iterator.next()
    }
}

var colors = ['red', 'black', 'blue']

forOf(arr, function (value) {
    console.log(value);
})
  1. 首先判断需要循环的对象是否具有可迭代性。
  2. 然后获取该对象的迭代器,并且用变量iterator进行存储。
  3. 通过变量res存储调用迭代器的next方法所返回的具有valuedone的对象。
  4. 再通过while循环判断是否迭代完成,如果没有迭代完成则执行for...of循环体内的代码并且再次调用next方法往后迭代。
  5. 直到迭代完该数据结构后结束。

面试题的解

回忆了一些关于迭代器的知识。但是这道面试题还涉及到了解构的知识。

简单回忆一下解构的核心原理。

//解构赋值的过程中也涉及到了迭代器
const newArr = ['red', 'black']
const [a, b] = newArr
//解构赋值的逻辑
var iterator = newArr[Symbol.iterator]()
a = iterator.next().value
b = iterator.next().value

回忆结束,开始解题。

var [myname,age]={myname:"张三",age:18}

要使该解构赋值可以成功,那么就需要给对象手搓一个迭代器。

那就给该对象添加一个[Symbol.iterator]方法,但是不能直接在该对象里添加,而是在该对象的原型上添加。

Object.prototype[Symbol.iterator] = function () {
    return {}
}
var [myname,age]={myname:"张三",age:18}
console.log(myname, age);

那返回的迭代器要怎么办呢?有迭代器的数据结构有数组,可以将对象和数组进行关联,然后再通过调用数组的[Symbol.iterator]方法的返回值作为该对象的[Symbol.iterator]方法的返回值。思想到位,开整。

可以通过Object.values(this)返回一个由对象的键值构成的数组,再通过Object.values(this)[Symbol.iterator]()作为返回值。

Object.prototype[Symbol.iterator] = function () {
    return Object.values(this)[Symbol.iterator]()
}
var [myname,age]={myname:"张三",age:18}
console.log(myname, age);

结果为:

深入剖析大厂面试题:解构与迭代器

这道面试题终于拿下了。

转载自:https://juejin.cn/post/7377647067575762996
评论
请登录