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

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

想象一下这个场景:你正在写一个优雅的函数,准备用 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),因为它简单、快速、可靠。只有在需要更严格检查的特殊场景下,才考虑其他方法。

相关推荐
木心操作6 分钟前
nodejs动态创建sql server表
前端·javascript·sql
一个很帅的帅哥8 分钟前
Vue中的data为什么是函数?
前端·javascript·vue.js·data
南屿im34 分钟前
用 Node.js 开发命令行工具:打造你的高效 CLI
前端·javascript
ObjectX前端实验室2 小时前
【react18原理探究实践】render阶段【首次挂载】
前端·react.js
ObjectX前端实验室2 小时前
【react18原理探究实践】组件的 props 和 state 究竟是如何确定和存储的?
前端·react.js
fxshy3 小时前
解决 Web 应用加载地图资源时的 HTTP 与 HTTPS 混合内容问题
前端·网络协议·http
一个很帅的帅哥3 小时前
Vue keep-alive
前端·javascript·vue.js·keep-alive
lbh3 小时前
Chrome DevTools 详解(一):Elements 面板
前端·javascript·浏览器
明里人3 小时前
React 状态库:Zustand 和 Jotai 怎么选?
前端·javascript·react.js
sniper_fandc3 小时前
Vue3双向数据绑定v-model
前端·vue