面试题:如何判断一个对象是否为可迭代对象?

引言:当你的代码遇到"不可迭代"的尴尬

想象一下这个场景:你正在写一个优雅的函数,准备用 for...of 循环来遍历传入的参数,结果浏览器无情地抛出了一个错误:

javascript 复制代码
Uncaught TypeError: obj is not iterable

😱 那一刻,你的内心是崩溃的。

什么是可迭代对象? 简单来说,就是那些可以被"遍历"的对象,比如数组、字符串、Map、Set 等。它们就像是"有顺序的菜",你可以一道一道地品尝。

为什么需要判断? 因为不是所有对象都这么"听话"。有些对象就像熊孩子一样,你让它排队它偏不排,这时候就需要先识别出对象是不是"熊孩子"。

可迭代对象

迭代器协议:JavaScript 的"循环规则"

在 JavaScript 的世界里,有一套严格的"循环规则",叫做迭代器协议(Iterator Protocol)

这个协议的核心就是 Symbol.iterator 方法。如果一个对象有这个"循环许可证",那它就是可迭代的。

javascript 复制代码
// 就像每个人都有身份证一样,可迭代对象都有这个"循环许可证"
console.log([1, 2, 3][Symbol.iterator]); // ƒ values() { [native code] }
console.log("hello"[Symbol.iterator]);   // ƒ [Symbol.iterator]() { [native code] }
console.log({a: 1}[Symbol.iterator]);    // undefined - 这个对象没有"循环许可证"!

常见的可迭代对象

让我们认识一下 JavaScript 世界里的可迭代对象们:

  • 数组(Array) :最守规矩的循环达人,永远按顺序来;
  • 字符串(String) :字符界的循环高手,一个字符一个字符地来;
  • MapSet:ES6 的新贵,也是循环的好手;
  • 类数组对象 :比如 argumentsNodeList,虽然不是数组,但也能循环;
  • 生成器函数:高级循环技巧,可以"暂停"和"继续"。

判断方法

方法一:最简单粗暴

javascript 复制代码
function isIterable(obj) {
  return obj != null && typeof obj[Symbol.iterator] === 'function';
}

console.log(isIterable([1, 2, 3]));     // true
console.log(isIterable("hello"));       // true
console.log(isIterable({a: 1}));        // false
console.log(isIterable(null));          // false
  • 优点:简单直接;
  • 缺点:有时候"可迭代对象"可能是假的(比如返回的不是真正的迭代器);

方法二:更严格

javascript 复制代码
function isIterable(obj) {
  try {
    const iterator = obj[Symbol.iterator]();
    return typeof iterator.next === 'function';
  } catch (e) {
    return false;
  }
}

console.log(isIterable([1, 2, 3]));     // true
console.log(isIterable("hello"));       // true
console.log(isIterable({a: 1}));        // false
  • 优点:更严格;
  • 缺点:稍微慢一点;

方法三:最魔幻

javascript 复制代码
function isIterable(obj) {
  try {
    [...obj];  // 展开运算符,就像魔法一样
    return true;
  } catch (e) {
    return false;
  }
}

console.log(isIterable([1, 2, 3]));     // true
console.log(isIterable("hello"));       // true
console.log(isIterable({a: 1}));        // false
  • 优点:代码最简洁;
  • 缺点:性能稍差,因为要实际执行迭代操作;

深入理解

迭代器是如何工作的?

迭代器就像一个"智能服务员",它知道:

  1. 现在轮到谁了(状态);
  2. 下一个是谁(next 方法);
  3. 还有没有下一个(done 属性)。
javascript 复制代码
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

就像服务员一个一个地端菜,端完了就说"没有了"。

自定义可迭代对象

javascript 复制代码
class MyIterable {
  constructor(data) {
    this.data = data;
  }
  
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        if (index < this.data.length) {
          return { value: this.data[index++], done: false };
        }
        return { done: true };
      }
    };
  }
}

const myIterable = new MyIterable(['apple', 'banana', 'orange']);
for (const item of myIterable) {
  console.log(item);
}

性能对比

让我们来做个性能测试,看看哪种方法最快:

javascript 复制代码
function isIterable1(obj) {
  return obj != null && typeof obj[Symbol.iterator] === 'function';
}

function isIterable2(obj) {
  try {
    const iterator = obj[Symbol.iterator]();
    return typeof iterator.next === 'function';
  } catch (e) {
    return false;
  }
}

function isIterable3(obj) {
  try {
    [...obj];  // 展开运算符,就像魔法一样
    return true;
  } catch (e) {
    return false;
  }
}

// 性能测试
const testData = Array.from({length: 10000}, (_, i) => i);

console.time('方法一');
for (let i = 0; i < 10000; i++) {
  isIterable1(testData);
}
console.timeEnd('方法一');

console.time('方法二');
for (let i = 0; i < 10000; i++) {
  isIterable2(testData);
}
console.timeEnd('方法二');

console.time('方法三');
for (let i = 0; i < 10000; i++) {
  isIterable3(testData);
}
console.timeEnd('方法三');

运行结果:

可以看出,方法一 方法二 > 方法三。

最后

在实际开发中,推荐使用方法一(检查 Symbol.iterator),因为它简单、快速、可靠。只有在需要更严格检查的特殊场景下,才考虑其他方法。

相关推荐
0思必得06 分钟前
[Web自动化] Selenium处理滚动条
前端·爬虫·python·selenium·自动化
Misnice9 分钟前
Webpack、Vite、Rsbuild区别
前端·webpack·node.js
青茶36010 分钟前
php怎么实现订单接口状态轮询(二)
前端·php·接口
大橙子额1 小时前
【解决报错】Cannot assign to read only property ‘exports‘ of object ‘#<Object>‘
前端·javascript·vue.js
WooaiJava2 小时前
AI 智能助手项目面试技术要点总结(前端部分)
javascript·大模型·html5
爱喝白开水a2 小时前
前端AI自动化测试:brower-use调研让大模型帮你做网页交互与测试
前端·人工智能·大模型·prompt·交互·agent·rag
Never_Satisfied2 小时前
在JavaScript / HTML中,关于querySelectorAll方法
开发语言·javascript·html
董世昌412 小时前
深度解析ES6 Set与Map:相同点、核心差异及实战选型
前端·javascript·es6
WeiXiao_Hyy3 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡3 小时前
高中数学-数列-导数证明
前端·数学·算法