Package Exports
- jsdc
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (jsdc) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Javascript Downcast
transform ecmascript6 to ecmascript5
INSTALL
npm install jsdc使用说明
- jsdc仅提供安全兼容的转换接口,并且不改变你的源代码行数一致性,这使得调试极为便利
- 智能识别es5语法,jsdc不会修改es5的部分
- CommonJS/AMD/CMD自适应
- as simple as possible
- 仅转换可实现的语言部分,扩展库(如
Set和Map)请使用es6-shim之类的库
已实现的部分
- 二进制和八进制的Number扩展
- block局部作用域
- let/const关键字
- 默认参数赋值
- rest扩展参数和spread扩展参数调用
- template模板
- for of循环
- class类实现
- extends类继承
- module模块
- ArrayComprehension数组推导
- ArrowFunction箭头函数
- Genarator生成器函数
API
Jsdc
- 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
静态属性
- parse(code:String):String 可以直接调用静态方法转换,以省略new一个对象的步骤
- define(d:Boolean):Boolean 读取/设置转换module为CommonJS时是否包裹define(即转为AMD/CMD),默认false
- ast():Object 返回解析后的语法树
- tokens():Array
Demo
- demo目录下是一个web端的实时转换例子,本地浏览需要
npm install安装依赖 - 依赖的语法解析器来自于
homunculus:https://github.com/army8735/homunculus - 在线地址:http://army8735.me/jsdc/demo/
License
[MIT License]
语法转换规则
- 以下按实现逻辑顺序排列(如有逻辑关联,如let和block作用域)
- 确保转换后代码执行一致,调试行数一致
Number数字扩展
0b或0B开头的二进制将使用parseInt转换:
var i = 0b010, j = 0B101var i = parseInt("0b010", 2), j = parseInt("0B101", 2)0o或0O开头的八进制也是如此(有人会用大写的字母O吗,和数字0根本看不出来区别):
var i = 0o456, j = 0O777var i = parseInt("0o456", 8), j = parseInt("0O777", 8)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的性质一样,除了迁移还会改写为var形式:
{function a(){}}var a;{a=function (){}}{}块级作用域
必要时将{}替换为function作用域:
{
let a = 1;
function b(){}
}var b;!function() {
let a = 1;
b=function (){}
}();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;
}();默认参数值
根据是否undefined赋值,它可以有多个:
function method(a, b = 1) {
}function method(a, b ) {if(typeof b == "undefined") 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(c))如果调用者是成员表达式,context将从this变为主表达式:
Math.max(...a)Math.max.apply(Math, [].concat(a))template模板
将模板转换为普通字符串,需要的情况下会包裹括号(确保运算符优先级正确性):
`template`"template"模板中的引号将被转义:
`"`"\""模板中的变量会被替换:
`${a}b`(a + "b")注意变量标识符$也可以被转义:
`\${a}b`"\${a}b"for of循环
将of改为in:
for(a of b){}for(a in b){}循环体内复制将键改为值:
for(a of b){}for(a in b){a = b[a];}var语句同样处理:
for(var a of b){}for(var a in b){a = b[a];}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会巧妙地设置到原型上:
class A{
get b(){}
set c(d){}
}function A(){}
A.prototype.b={get b(){}}["b"];
A.prototype.c={set c(d){}}["c"];
static静态属性会附加在function本身:
class A{
static a(){}
}function A(){}
A.a=function(){}
extends类继承
采用最广泛的寄生组合式继承:
class A extends B{
constructor(){}
}function A(){}!function(){var _=Object.create(B.prototype);_.constructor=A;A.prototype=_;}();
function A(){}
Object.keys(B).forEach(function(k){A[k]=B[k]});开头会附加上一段
prototype原型和constructor构造器,标准的寄生组合式继承方法。 结尾会继承父类的静态属性。
super关键字直接改写为父类引用:
class A extends B{
constructor(){super()}
}function A(){}!function(){var _=Object.create(B.prototype);_.constructor=A;A.prototype=_;}();
function A(){B.call(this)}
Object.keys(B).forEach(function(k){A[k]=B[k]});如果不是调用父类构造函数而是方法,则不会添加call:
class A extends B{
constructor(){super.a()}
}function A(){}!function(){var _=Object.create(B.prototype);_.constructor=A;A.prototype=_;}();
function A(){B.a()}
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):Booleanmodule转换为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 *会赋给module.exports:
export * from "a"module.exports=require("a");export一个var语句时会自动赋值同名变量:
export var a = 1var a;exports.a=a = 1export一个方法或类时也一样:
export function a(){}
export class A{}exports.a=a;function a(){}
exports.A=A;function A(){}export default会直接赋给module.exports:
export default amodule.exports=aArrayComprehension数组推导
可以代替Array.map方法:
var a = [for(k of o)k]var a = function(){var k;var __0__=[];for(k in o){k=o[k];__0__.push(k)}return __0__}();注意再次出现的临时变量
__0__和上面提到的一致,不会冲突。
if语句可以替代Array.filter方法:
var a = [for(k of o)if(k)k]var a = function(){var k;var __0__=[];for(k in o){k=o[k];if(k)__0__.push(k)}return __0__}();嵌套组合使用也是可以的:
var a = [for(a of b)for(c of a)if(c)c]var a = function(){var a;var c;var __0__=[];for(a in b){a=b[a];for(c in a){c=a[c];if(c)__0__.push(c)}}return __0__}();ArrowFunction箭头函数
转换为普通函数:
var a = v => vvar a = function(v) {return v}括号形式的参数:
var a = (b, c) => b + cvar 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语句本身尚未做处理,后面会提到。
Generator生成器函数
它的实现比较复杂,首先是改写为普通函数:
function *a(){
yield 1
yield 2
}function a(){
return 1
return 2
}然后包裹:
function *a(){
yield 1
yield 2
}var a=function(){return function (){return {next:a}};function a(){
return 1
return 2
}}();这样每次调用它便能得到像es6中一样的一个具有
next()方法的对象。
内部的a变量需要改写为一个唯一临时id(为什么后面会提到):
function *a(){
yield 1
yield 2
}var a=function(){return function (){return {next:__0__}};function __0__(){
return 1
return 2
}}();再次添加一个唯一临时id作为state标识,来为实现yield功能做准备:
function *a(){
yield 1
yield 2
}var a=function(){var __1__=0;return function (){return {next:__0__}};function __0__(){
return 1
return 2
}}();当出现yield语句时,添加switch语句来模拟顺序执行:
function *a(){
yield 1
yield 2
}var a=function(){var __1__=0;return function (){return {next:__0__}};function __0__(){
switch(__1__++){case 0:return 1
case 1:return 2}
}}();同时函数里面的var声明需要前置:
function *a(){
var a = 1;
yield a++;
yield a++;
}var a=function(){var __1__=0;return function (){return {next:__0__}};var a;function __0__(){
switch(__1__++){case 0:a = 1;
return a++;
case 1:return a++;}
}}();以免每次调用
next()方法时变量又重新声明一遍失去了原先的状态,函数则不需要前置。
注意函数内有个同名变量
a,这就是前面为什么要改函数名的原因。
待续……


