添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • as simple as possible
  • 仅转换可实现的语言部分,扩展库(如 Set Map )请使用 es6-shim 之类的库
  • 特别注意某些实现依赖 Iterator ,请确保有此扩展
  • 已实现的部分

  • 二进制和八进制的Number扩展
  • Unicode的字符串增强
  • Object属性增强
  • block局部作用域
  • let/const关键字
  • 默认参数赋值
  • rest扩展参数和spread扩展参数调用
  • template模板
  • for of循环
  • class类实现
  • extends类继承
  • module模块
  • ArrayComprehension数组推导
  • ArrowFunction箭头函数
  • yield语句
  • Generator生成器函数
  • constructor(code:String = '') 传入需要转换的code
  • parse(code:String = null):String 转换code,可以为空,否则会覆盖构造函数里传入的code
  • define(d:Boolean):Boolean 读取/设置转换module为CommonJS时是否包裹define(即转为AMD/CMD),默认false
  • ast():Object 返回解析后的语法树
  • tokens():Array<Object> 返回解析后的词法单元序列
  • parse(code:String):String 可以直接调用静态方法转换,以省略new一个对象的步骤
  • define(d:Boolean):Boolean 读取/设置转换module为CommonJS时是否包裹define(即转为AMD/CMD),默认false
  • ast():Object 返回解析后的语法树
  • tokens():Array<Object> 返回解析后的词法单元序列
  • runtime(flag:Boolean):void 开启/关闭运行时支持,仅限NodeJs。开启后改写require机制,获取module前尝试预编译
  • demo目录下是一个web端的实时转换例子,本地浏览需要 npm install 安装依赖
  • 依赖的语法解析器来自于 homunculus :https://github.com/army8735/homunculus
  • 在线地址:http://army8735.me/jsdc/demo/
  • Tools

  • 命令行版工具:https://github.com/xudafeng/jsdc-cli
  • gulp插件:https://github.com/army8735/gulp-jsdc
  • License

    [MIT License]

    语法转换规则

  • 以下按实现逻辑顺序排列(如有逻辑关联,如let和block作用域)
  • 确保转换后代码执行一致,调试行数一致
  • Number数字扩展

    0b 0B 开头的二进制将使用 parseInt 转换:

    var i = 0b010, j = 0B101
    var i = parseInt("0b010", 2), j = parseInt("0B101", 2)

    0o 0O 开头的八进制也是如此(有人会用大写的字母O吗,和数字0根本看不出来区别):

    var i = 0o456, j = 0O777
    var i = parseInt("0o456", 8), j = parseInt("0O777", 8)

    Unicode的字符串增强

    Unicode 编号大于 0xFFFF 的字符将会转移成2个 utf-8 拼接:

    '\u{10000}'
    '\ud800\udc00'

    转义符也能正确识别:

    '\\u{10000}'
    '\\u{10000}'

    Object增强

    赋值对象时 Object 的同名属性可以简写:

    var a = {o}
    var a = {o:o}

    方法也是:

    var a = {o(){}}
    var a = {o:function(){}}

    甚至可以用 [] 表明它是一个表达式:

    var a = {
    ['a'+'b'] : 1
    }
    var a = function(){var _0={
    _1: 1
    };_0['a'+'b']=_0._1;delete _0._1;return _0}()

    实现方法是先用个临时唯一变量替换掉表达式,最后再将它还原回来。

    var和函数迁移

    var 申明迁移至最近的作用域起始处:

    function() {
      if(true) {
        var a = 1;
        let b = 2;
      }
    }
    function() {var a;
      if(true) {
        a = 1;
        let b = 2;
      }
    }

    仅当必要时才迁移,否则保持原样(比如下面没有 let ):

    function() {
      if(true) {
        var a = 1;
      }
    }

    示例中 let 和块级作用域尚未处理,后面会提到。

    函数和var的性质一样:

    {function a(){}}
    !function(){function a(){}}();

    {}块级作用域

    必要时将 {} 替换为 function 作用域:

    {
      let a = 1;
      function b(){}
    }
    !function() {
      let a = 1;
      function b(){}
    }();

    if 语句, iterator 语句和 try / catch / finally 等也是,注意和纯 {} 语句插入匿名函数位置的异同:

    if(true) {
      let a = 1;
    }
    if(true) {!function() {
      let a = 1;
    }();}

    示例中 let 尚未做处理,后面会提到。

    let/const关键字

    let const 替换为 var

    let a = 1;
    const b;
    var a = 1;
    var b;

    注意和块级作用域的交互:

    {
      var a = 1;
      let b;
      const c = 1;
    }
    var a;!function() {
      a = 1;
      var b;
      var c = 1;
    }();

    函数和 Generator 函数均默认块级作用域。

    默认参数值

    根据是否 undefined 赋值,它可以有多个:

    function method(a, b = 1) {
    }
    function method(a, b) {if(===void 0)b=1;
    }

    将扩展参数通过 arguments 转换为数组:

    function method(a, ...args) {
    }
    function method(a, args) {args = [].slice.call(arguments, 1);
    }

    方法执行则使用 apply 调用:

    fn(a, b, ...c)
    fn.apply(this, [a,b].concat(Array.from(c)))

    如果调用者是成员表达式,context将从 this 变为主表达式:

    Math.max(...a)
    Math.max.apply(Math, [].concat(Array.from(a)))

    在数组中则会自动展开,支持string预判断:

    var codeUnits = [..."this is a string"];
    var codeUnits = [...a];
    var codeUnits = [].concat("this is a string".split(""));
    var codeUnits = [].concat(Array.from(a));

    template模板

    将模板转换为普通字符串,需要的情况下会包裹括号(确保运算符优先级正确性):

    `template`
    "template"

    模板中的引号将被转义:

    `"`
    "\""

    模板中的变量会被替换:

    `${a}b`
    (+ "b")

    注意变量标识符$也可以被转义:

    `\${a}b`
    "\${a}b"

    for of循环

    存储被循环体到临时引用:

    for(of b){
    }
    var _0=b;for(of b){
    }

    of 改为 = 并添加 ; 补完循环:

    for(of b){
    }
    var _0=b;for(=_0;;){
    }

    将赋值添加 [Symbol.iterator]().next() 并添加 .done 结束判断:

    for(of b){
    }
    var _0=b;for(=_0[Symbol.iterator]().next();!a.done;a=_0.next()){
    }

    循环体内先赋值 .value

    for(of b){
    }
    var _0=b;for(=_0[Symbol.iterator]().next();!a.done;a=_0.next()){a=a.value;
    }

    var 语句同样处理:

    for(var a of b){
    }
    var _0=b;for(var a =_0[Symbol.iterator]().next();!a.done;a=_0.next()){a=a.value;
    }

    class类声明

    将类声明改为 function 声明:

    class A{}
    function A(){}

    constructor 构造函数可省略,也可以显示声明:

    class A{
      constructor(a){this.a = a}
    }
    //此行是空行,请忽略:由于github会忽略前面的空白,所以用注释代替
    function A(a){this.a = a}
     

    注意行对应关系,省略的话行位置是 class 声明行,否则是 constructor 声明行。

    方法会改写成 prototype 的原型方法:

    class A{
      method(){}
    }
    function A{}
    A.prototype.method=function(){}
     

    getter/setter会通过 Object.defineProperty 巧妙地设置到原型上:

    class A{
      get b(){}
      set c(d){}
    }
    function A(){}
      Object.defineProperty(A.prototype, "b", {get :function(){}});
      Object.defineProperty(A.prototype, "c", {set :function(d){}});
     

    static 静态方法会附加在 function 本身:

    class A{
    static F(){}
    }
    function A(){}
    A.F=function(){}
     

    extends类继承和super关键字

    采用最广泛的寄生组合式继承:

    class A extends B{
    constructor(){}
    }
    !function(){var _0=Object.create(B.prototype);_0.constructor=A;A.prototype=_0;}();
    function A(){}
    Object.keys(B).forEach(function(k){A[k]=B[k]});

    开头会附加上一段 prototype 原型和 constructor 构造器,标准的寄生组合式继承方法。

    结尾会继承父类的静态属性。

    super 关键字直接改写为父类引用:

    class A extends B{
    constructor(){super()}
    }
    !function(){var _0=Object.create(B.prototype);_0.constructor=A;A.prototype=_0;}();
    function A(){B.call(this)}
    Object.keys(B).forEach(function(k){A[k]=B[k]});

    如果不是调用父类构造函数而是方法,则会这样:

    class A extends B{
    constructor(){super.a()}
    }
    !function(){var _0=Object.create(B.prototype);_0.constructor=A;A.prototype=_0;}();
    function A(){B.prototype.a.call(this)}
    Object.keys(B).forEach(function(k){A[k]=B[k]});

    默认构造器函数则会自动调用 super()

    class A extends B{
    }
    function A(){B.call(this)}!function(){var _0=Object.create(B.prototype);_0.constructor=A;A.prototype=_0}();
    Object.keys(B).forEach(function(k){A[k]=B[k]});

    class表达式

    和函数表达式一样,class也可以有表达式:

    var o = class{
      method(){}
    }
    var o = function(){function _0(){}
      _0.prototype.method = function(){}
    return _0}()

    由于表达式没有名字(也可以有),所以需要封装成立即执行的匿名函数并返回一个 class 声明。

    有名字的话就用原有名字,否则依然临时唯一id。

    注意匿名函数的结尾没有分号,因为本身是个 assignmentexpr

    module

    只要出现了module/import/export语句,就认为文件是个模块,用 define 封装成AMD/CMD模块:

    module circle from "a"
    define(function(requrie,exports,module){module circle from "a"});

    注意语句本身尚未做处理,下面会说明。为阅读方便,下面所有都省略了 define 封装。

    也可以通过API设置来控制:

    jsdc.define(wrap:Boolean):Boolean

    module 转换为 require

    module circle from "a"
    var circle=require("a");

    import 也会转换为 require

    import "a"
    require("a");

    import 可以指定id:

    import a from "a"
    var a;!function(){var _0=require("a");a=_0.a}();

    类似 _0 变量是自动生成的,数字会自动累加,且不会和已有变量冲突。

    在冲突时会自动跳过:

    import _0 from "a"
    var _0;!function(){var _1=require("a");_0=_1.a}();

    import 还可以指定多个id:

    import a,b from "a"
    var a;var b;!function(){var _0=require("a");a=_0.a;b=_0.b;}();

    import 可以用 {} 来赋值,注意里面 as 声明变量名的方法:

    import {a,b as c} from "a"
    var a;var c;!function(){var _0=require("a");a=_0.a;c=_0.b;}();

    export * from "" 会将模块的导出赋给module.exports:

    export * from "a"
    !function(){var _0=require("a");Object.keys(_0).forEach(function(k){module.exports[k]=temp[k];});}();

    export 一个 var 语句时会自动赋值同名变量:

    export var a = 1
    var a;exports.a== 1

    export 一个方法或类时也一样:

    export function a(){}
    export class A{}
    exports.a=a;function a(){}
    exports.A=A;function A(){}

    export default 会赋给 exports.default ,这样在使用时会判断是否有 default 属性:

    export default a
    import b from "a"
    module.exports=a
    var b=function(){var b=function(){var _0=require("a");return _0.hasOwnProperty("b")?_0.b:_0.hasOwnProperty("default")?_0.default:_0}()}()

    注意单id会优先判断使用同名属性,退级使用 default ,最后模块本身

    ArrayComprehension数组推导

    可以代替 Array.map 方法:

    var a = [for(of o)k]
    var a = function(){var k;var _0=[];for(in o){k=o[k];_0.push(k)}return _0}()

    注意再次出现的临时变量 _0 和上面提到的一致,不会冲突。

    if 语句可以替代 Array.filter 方法:

    var a = [for(of o)if(k)k]
    var a = function(){var k;var _0=[];for(in o){k=o[k];if(k)_0.push(k)}return _0}()

    嵌套组合使用也是可以的:

    var a = [for(of b)for(of a)if(c)c]
    var a = function(){var a;var c;var _0=[];for(in b){a=b[a];for(in a){c=a[c];if(c)_0.push(c)}}return _0}()

    ArrowFunction箭头函数

    转换为普通函数:

    var a = v => v
    var a = function(v) {return v}

    括号形式的参数:

    var a = (b, c) => b + c
    var a = function(b, c) {return b + c}

    {} 的函数体:

    var a = (b, c) => {return b - c}
    var a = function(b, c) {return b - c}

    yield语句

    yield 作为关键字只能出现在 Generator 中,会被替换为 return

    function *a() {
      yield
    }
    function *a() {
      return
    }

    Generator 语句本身尚未做处理,后面会提到。

    赋值语句后会加上一个临时唯一id,模拟下次调用 next() 传入的一个参数:

    function *a() {
      var a = yield
    }
    function *a(_0) {
      var a;return;a=_0
    }

    yield 的返回值将变成一个对象的 value ,同时添加 done 属性标明是否结束:

    function *a() {
      var a = yield 1
    }
    function *a(_0) {
      var a;return {value:1,done:true};a=_0
    }

    Generator生成器函数

    它的实现比较复杂,首先是改写为普通函数:

    function *a(){
      yield 1
      yield 2
    }
    function a(){
      return{value:1,done:false}
      return{value:2,done:true}
    }

    然后包裹:

    function *a(){
      yield 1
      yield 2
    }
    var a=function(){return function(){return{next:a}};function a(){
      return{value:1,done:false}
      return{value:2,done:true}
    }}();

    这样每次调用它便能得到像es6中一样的一个具有 next() 方法的对象。

    内部的a变量需要改写为一个唯一临时id(为什么后面会提到):

    function *a(){
      yield 1
      yield 2
    }
    var a=function(){return function(){return{next:_0}};function _0(){
      return{value:1,done:false}
      return{value:2,done:true}
    }}();

    再次添加一个唯一临时id作为state标识,来为实现 yield 功能做准备:

    function *a(){
      yield 1
      yield 2
    }
    var a=function(){return function(){var _1=0;return{next:_0};function _0(){
      return{value:1,done:false}
      return{value:2,done:true}
    }}}();

    当出现 yield 语句时,添加 while switch 语句来模拟顺序执行:

    function *a(){
      yield 1
      yield 2
    }
    var a=function(){return function(){var _1=0;return{next:_0};function _0(){
      while(1){switch(_1){case 0:_1=1;return{value:1,done:false}
      case 1:_1=-1;return{value:1,done:true}}}
    }}}();

    注意状态在 switch 各分支语句之间的跳转

    同时函数里面的 var 声明需要前置,以免每次调用 next() 方法时又重新声明一遍失去了状态:

    function *a(){
      var a = 1;
      yield a++;
      yield a++;
    }
    var a=function(){return function(){var _1=0;return{next:_0};var a;function _0(){
      while(1){switch(_1){case 0:= 1;
      _1=1;return{value:a++,done:false};
      case 1:_1=-1;return{value:a++,done:true;}}}
    }}}();

    函数则不需要前置。

    注意函数内有个同名变量 a ,这就是前面为什么要改函数名的原因。

    添加 default 语句:

    function *a(){
      var a = 1;
      yield a++;
      yield a++;
    }
    var a=function(){return function(){var _0=0;return{next:_1};var a;function _1(_2){
      while(1){switch(_0){case 0:= 1;
      _0=1;return{value:a++,done:false};case 1:
      _0=-1;return{value:a++,done:true};default:return{done:true}}}
    }}}();

    yield 还支持返回一个 Generator ,这就是一个递归:

    function *a(){
      yield * b
    }
    var a=function(){return function(){var _0=0;return{next:_1};function _1(_2){
      while(1){switch(_0){case 0:_0=1;var _3=b();if(!_3.done)_0=0;return _3;default:return{done:true}}}
    }}}();

    表达式也一样,没有 yield 则不会添加 while switch 语句:

    ~function *(){
    }
    ~function(){return function(){var _0=0return{next:_1};function _1(){
    }}}()

    destructure解构

    var 声明变量时可以用数组:

    var [a] = [1]
    var a;(function(){var _0= [1];a=_0[0];}).call(this)

    变量名会被前置,同时包裹执行一个匿名函数,将变量名赋值对应到正确的索引。

    多个变量一样,注意逗号占位符:

    var [a,b,,c] = [1]
    var c;var b;var a;(function(){var _1= [1];a=_1[0];b=_1[1];c=_1[3]}).call(this)

    也可以是对象:

    var {a} = {"a":1}
    var a;(function(){var _0= {"a":1};a=_0["a"]}).call(this)

    注意变量名和键名要一致。

    对象可以用 : 号更改引用:

    var {a,b:c} = {"a":1,"b":2}
    var a;(function(){var _0= {"a":1,"b":2};a=_0["a"];c=_0["b"]}).call(this)

    它们甚至可以互相嵌套递归:

    var [a,{b},{c:[d]}] = [1,{"b":2},{"c":[3]}]
    var d;var b;var a;(function(){var _0= [1,{"b":2},{"c":[3]}];a=_0[0];var _1=_0[1];b=_1["b"];var _2=_0[2];var _3=_2["c"];d=_3[0]}).call(this)

    解构还允许在未定义的情况下默认赋值:

    var [a=1] = []
    var a;(function(){var _0= [];a=_0[0];if(_0.indexOf(a)!=0)a=1}).call(this)

    表达式赋值也可以:

    ({a=1} = {})
    ((function(){var _0= {};a=_0["a"];if(!_0.hasOwnProperty('a'))a=1;return _0}).call(this))

    数组解构最后允许 rest 运算符:

    var [a, ...b] = [1, 2, 3]