前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >由 for...of 深入看 Babel 转码的局限

由 for...of 深入看 Babel 转码的局限

作者头像
逆葵
发布2019-04-25 11:14:24
8520
发布2019-04-25 11:14:24
举报
文章被收录于专栏:FECodingFECoding

ES6 借鉴了其他编程语言的特性,为 JavaScript 带来了 for…of 循环语法,用于遍历数组等数据结构。当然,由于是 ES6 的特性,我们使用 for…of 的时候,依然要借助 Babel 进行转码。我们来看看 Babel 是如何处理 for…of 代码的。

ES6 原生代码如下。为避免干扰,这里不使用 ES6 其他特性。

代码语言:javascript
复制
var names = ['paul', 'jordan', 'griffin'];
for (var name of names) {
  console.log(name);
}

Babel 转码后结果如下:

代码语言:javascript
复制
'use strict';

var names = ['paul', 'jordan', 'griffin'];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
  for (var _iterator = names[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var name = _step.value;

    console.log(name);
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator.return) {
      _iterator.return();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}

我们观察到 Babel 转换后的代码里第 9 行仍然出现了 ES6 的特性——Symbol.iterator,这是为什么呢?我们先来探究一下 for…of 的实现原理。

for…of 在对数据结构进行循环时,背后实际上是调用了该数据结构的 Iterator 接口。一种数据结构只要具有 Iterator 接口,我们就可以认为该数据结构是“可遍历的”(iterable)。原生数据结构中具有“可遍历”属性的包括数组、Set、Map、以及字符串之类的类数组对象等。具体到 Iterator 接口上,ES6 规定,默认的 Iterator 接口部署在该数据结构的 Symbol.iterator 属性上(Symbol 是 ES6 新增的原始数据类型,表示独一无二的值,具体参见 ES6 文档),该属性本身是一个函数,执行该函数会返回一个指针对象。该指针对象称为遍历器,其必须包含一个 next 方法,不断调用 next 方法可以使指针从数据结构的第一个成员一直指向最后一个成员,即调用 next 方法会返回数据结构当前成员的信息,该信息为一个对象,包含 value 和 done 两个属性。value 是当前成员的值,done 是一个布尔值,表示遍历是否结束。以上的理论有点抽象,我们来模拟一个“可遍历”的数据结构:

代码语言:javascript
复制
const iterableData = {
  data: ['paul','jordan','griffin','redick','rivers'],
  dataIndex: 0,
  //Symbol 类型的值作为对象属性时必须使用方括号结构
  [Symbol.iterator]: function () {
    var self = this;
    return {
      next: function () {
	    return {
		 value: self.data[self.dataIndex++],
          done: self.dataIndex < self.data.length? false: true	
	    };
      }
    };
  }
};


for (let item of iterableData) {
  console.log(item);
}
// paul, jordan, griffin, redick, rivers

可以看到,只要一个数据结构具有符合要求的 Symbol.iterator 属性,就可以通过 for…of 遍历(事实上,解构赋值、扩展运算符、yield* 等 ES6 特性也是调用该属性接口)。

现在,我们回过头来看 Babel 转换 for…of 循环的代码,其本质上还是通过调用 Iterator 接口(注意第 9 行),将 for…of 转换为传统的 for 循环,并在每次循环中调用遍历器的 next 方法来吐出数组中的值。如果在循环调用过程中出现错误,遍历器中如含有预定义的 return 函数(参见 ES6 文档中遍历器对象的规范 ),则调用之,否则直接抛出错误。

所以,问题就出现了,即使调用 Babel 对 for…of 循环进行转码,我们实际上还是无法完全摆脱 ES6 的特性——在不支持 Symbol 的环境下,代码仍然会报错。因为 Babel 默认只转换新的 JavaScript 句法(syntax),而不转换 Proxy、Set、Promise、Symbol 等新的 API。所以,在对兼容性要求较高时,确实要慎重使用 for…of 语法,即使我们有 Babel 这件神兵利器。

实际上,要想完全抹平 ES6 特性带来的新 API 也是可行的,只要在项目中引入 babel-polyfill 并配置好即可,但是这样带来的另一个问题就是因为 babel-polyfill 本身的体积,我们的代码也会变庞大不少。所以此举有利有弊,需要根据实际情况进行权衡。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档