Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 66 题:ES6 代码转成 ES5 代码的实现思路是什么

Open zeroone001 opened this issue 5 years ago • 11 comments

zeroone001 avatar May 05 '19 00:05 zeroone001

将ES6的代码转换为AST语法树,然后再将ES6 AST转为ES5 AST,再将AST转为代码

Gentlemancj avatar May 05 '19 01:05 Gentlemancj

题目说的是 ES6,所以不考虑 .jsx.ts 这类 js 拓展语言。 ES6 转 ES5 目前行业标配是用 Babel,转换的大致流程如下:

  1. 解析:解析代码字符串,生成 AST;
  2. 转换:按一定的规则转换、修改 AST;
  3. 生成:将修改后的 AST 转换成普通代码。

如果不用工具,纯人工的话,就是使用或自己写各种 polyfill 了。

wingmeng avatar May 05 '19 14:05 wingmeng

把 ES6 代码转成 ES5 代码的实现思路可以分成三步:

  • 打开冰箱
  • 把大象装进去
  • 关上冰箱

喔,不对,原谅我开了个玩笑,嗯,有点冷……

回到正题上来,说到 ES6 代码转成 ES5 代码,我们肯定会想到 Babel。所以,我们可以参考 Babel 的实现方式。

那么 Babel 是如何把 ES6 转成 ES5 呢,其大致分为三步:

  • 将代码字符串解析成抽象语法树,即所谓的 AST
  • 对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码
  • 根据处理后的 AST 再生成代码字符串

基于此,其实我们自己就可以实现一个简单的“编译器”,用于把 ES6 代码转成 ES5。

比如,可以使用 @babel/parserparse 方法,将代码字符串解析成 AST;使用 @babel/coretransformFromAstSync 方法,对 AST 进行处理,将其转成 ES5 并生成相应的代码字符串;过程中,可能还需要使用 @babel/traverse 来获取依赖文件等。对此感兴趣的可以看看这个

如果有误,希望可以帮忙指出来,多谢。

FishPlusOrange avatar May 06 '19 06:05 FishPlusOrange

说说我的理解哦 es6比es5多出来的部分分两类 一类是语法,如箭头函数,解构; 一类是新的类、新的类方法、新的实例方法,如:Promise, Array.from, Array.prototype.find

babel在做语法转换的时候,通过抽象语法树来实现代码层面的翻译。 比如将 const fn = () => { Array.isArray([1, 2, 3]); }; 转成 "use strict"; var fn = function fn() { Array.isArray([1, 2, 3]); };

对于新的类、类方法,实例方法,基本就是polyfill,或者polyfill加上代码转换。 拿Array.from来说,只需要使用es5的语法,自己实现一遍Array.from,就可以用不改动源代码而使用Array.from这个es6的api了,@babel/polyfill这个包就是做这事儿的。 复杂点的Promise在babel转译的时候,不仅会调整源代码,还需要引入@babel/polyfill。有时候会遇到regeneratorRuntime is not defined这个报错,原因是源代码中Promise部分被转译了,转译后用到的regeneratorRuntime在@babel/polyfill中,但@babel/polyfill没被引入。

georgehpj avatar May 14 '19 12:05 georgehpj

ES6 代码转换成 ES5 的思路大家都讲的差不多,我来做个补充。

Babel 将把 ES6 转成 ES5 第二步,从 Babel 6.0 开始,就不再对代码进行转换。现在 Babel 只负责parse 和 generate 流程,也就是专注于解析和生成阶段。转换代码的 transform 过程全都交给bable插件去做了。

我们的项目里的 .babelrc 文件就是用来配置babel处理的,常有plugins 与 presets配置项,当他们同时存在的时候,先执行plugins从上到下,在执行presets从左到右。

habc0807 avatar Jul 23 '19 14:07 habc0807

借用babel转换思想,将es6解析成AST,然后按照统一规则转换修改AST,再将AST解析成es5

superermiao avatar Jul 25 '19 09:07 superermiao

涉及到的主要是AST相关的内容,AST(Abstract Syntax Tree)中文叫抽象语法树,是用来表示源代码语法的一种树形结构,树上的每个节点都代表源代码的一种结构。AST在我们日常应用中非常广泛,我们的代码高亮,代码检查等都是依靠的AST。

那么ES6转ES5的思路,其实就是在处理AST的过程中进行操作。转化代码的流程一般分为三步

  1. 将代码通过解释器转化为AST,可以通过 astexplorer 来查看代码对应的AST结构
  2. 通过一定的规则,去修改AST的结构(常见的比如转jsx,ES6转ES5都是在这一步进行操作)
  3. 将修改后的AST转化为普通代码

现在一般使用的就是bable转ES6,具体的ES6转ES5在第二步中的逻辑,那就得看bable中转ES6的babel-preset-es2015 对AST进行操作的源码了

chaijinsong avatar Aug 15 '19 00:08 chaijinsong

这问的是问题么? 你说的是普通开发者的思路? 还是babel作者的思路啊??

zhishaofei3 avatar Sep 26 '19 07:09 zhishaofei3

ES6转ES5分为以下两种情况 1.语法转换 ES6语法通过babel等工具为ES5语法,本质是将ES6语法转AST(抽象语法数——对编程语言编写的程序的一种描述)再将AST转为ES5语法代码;例如:let,const转换为var,箭头函数转换为function函数声明等 2.API转换 采用babel-polyfill等工具对ES5中不存在的API(包括Set等ES6中新的数据结构)做修复,例如:Array.prototype.includes Set Map等在ES5中不存在,需要用相应的ES5代码实现这些API


补充说明

  • .vue文件通过webpack的vue-loader分析出script style template 再走上面的ES6转ES5流程
  • jsx通过babel插件转js语法再走ES6转ES5
  • ts通过tsc结合tsconfig.json直接转ES5

JohnieXu avatar Sep 29 '19 01:09 JohnieXu

ES6 代码转成 ES5 代码的实现思路是什么

  • 把代码字符串解析成AST(抽象语法树)
  • AST就是一个json,按照一定规则把这个json里ES6部分的东西转换成ES5的
  • 再把修改后的AST转换成代码

关于AST

AST的全称是abstract syntax tree,中文名叫抽象语法树,假设有一个函数,

function hello(a, b, c){ 
}

这段代码被解析成的ast树就是

type: Program
-
body
-
#1
type: FunctionDeclaration
-
id
type: Identifier
name: hello
-
params
-
#1
type: Identifier
name: a
-
#2
type: Identifier
name: b
-
#3
type: Identifier
name: c
-
body
type: BlockStatement
body
generator: false
expression: false
async: false
sourceType: script

这样看起来还不是很直观,其实上面的树就等于

{
  "type": "Program",
  "body": [
    {
      "type": "FunctionDeclaration",
      "id": {
        "type": "Identifier",
        "name": "hello"
      },
      "params": [
        {
          "type": "Identifier",
          "name": "a"
        },
        {
          "type": "Identifier",
          "name": "b"
        },
        {
          "type": "Identifier",
          "name": "c"
        }
      ],
      "body": {
        "type": "BlockStatement",
        "body": []
      },
      "generator": false,
      "expression": false,
      "async": false
    }
  ],
  "sourceType": "script"
}

有了这个json后,就可以对代码进行操作了。在没有树的情况下,如果要对文件里的某一个语句进行替换的话,一般就是全局 搜索然后replace,这样有可能影响到别的代码,但是有了树后,就可以对这个json进行操作,精确地去修改某个对象的属性,也就不会影响到别的代码了。所以babel转换ES6的核心,就是在ast中按照一定的规则取修改json里的属性和方法,然后再把tree转换成代码

soraly avatar Jun 29 '20 02:06 soraly

使用babel

  1. es6 代码字符串解析生成 AST
  2. 按照es5 的规则 转换 生成符合es5规范的 AST
  3. 将 AST 转换为代码字符串

Yangfan2016 avatar Aug 08 '22 03:08 Yangfan2016