目录

把你的JS代码翻译成中文书写的代码

不少人应该知道JS里可以用部分unicode字符来命名变量和方法。想想挺好玩的,不但可以用中文来编程,还可以用颜文字和emoji来编程,想想也是醉了。

不过还是不建议在真实项目中这样做,但是这并不代表不可以用嘛,不过本文纯搞笑,大家别太较真,看看就行。

今天,跟大家介绍一下我写的一个《英文代码翻译器》可以用一句命令,把你的js代码翻译成中文变量命名并且还可以运行的js代码!

虽然这个项目只是开个玩笑,单其中涉及到的几个技术要点,还是值得一说的。本文主要分以下几节:

介绍 | 代码编译 | 翻译 | 全局变量处理 | 重复翻译结果处理 | 小结

介绍

github地址: GitHub - xinyu198736/hancode: 把你的JS代码翻译成中文书写的代码,并且可执行

使用方式:

GitHub - xinyu198736/hancode: 把你的JS代码翻译成中文书写的代码,并且可执行

sudo npm install hancode -g;

定位到某个目录

hancode -s ./code.js

会在当前目录生成一个 code.汉.js

code.汉.js 的代码其实就是本库的中文版,测试可以跑通,并且结果一样。

一个编译后的在线地址: http://f2e.souche.com/assets/js/souche.%E6%B1%89.js

本库主代码编译后的结果:

http://f2e.souche.com/assets/js/souche.%E6%B1%89.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
var 丑化JS = require(uglify-js);

var _ = require(underscore);

var 翻译工具 = require(./translate_util);

Var 翻译对象 = {};

module.exports = function(源代码, 回调) {
    var AST的代码 = 丑化JS.parse(源代码);
    Var 无功节点 = [];
    Var 参考节点 = [];
    Var 跨语言 = [];
    AST的代码.walk(new 丑化JS.TreeWalker(function() {
        if ( instanceof 丑化JS.AST_SymbolVar) {
            无功节点.push();
            跨语言.push(.name);
        }
    }));
    AST的代码.walk(new 丑化JS.TreeWalker(function() {
        if ( instanceof 丑化JS.AST_SymbolRef) {
            if (跨语言.indexOf(.name) != -1) {
                参考节点.push();
                跨语言.push(.name);
            }
        }
    }));
    跨语言 = _.uniq(跨语言);
    console.log(翻译中);
    翻译工具(跨语言, function(E, 结果) {
        翻译对象 = 结果;
        使uniq对象(翻译对象);
        无功节点.forEach(function() {
            .name = 翻译对象[.name] || .name;
        });
        参考节点.forEach(function() {
            .name = 翻译对象[.name] || .name;
        });
        回调(null, AST的代码.print_to_string({
            beautify: true
        }));
    });
};

var 使uniq对象 = function(obj) {
    Var 扫描值 = [];
    for (var  in obj) {
        var 价值 = obj[];
        if (扫描值.indexOf(价值) != -1) {
            Var 重复计数 = 0;
            扫描值.forEach(function(v) {
                if (v == 价值) {
                    重复计数++;
                }
            });
            obj[] = 价值 + _ + 重复计数;
        }
        扫描值.push(价值);
    }
};

代码编译

AST这个名词相信很多人都接触过,在做代码压缩的时候,都是先把js转换成AST语法树,然后操作ast语法树,最后再把ast语法树组装回代码,这样做的安全性比较高,比用单纯的正则匹配精确很多,之前我做过一个拆分代码拼接图案的库,用状态机自己写的,中间处理了很多兼容和条件,而且还不能百分百确保正确,而用AST的话其实就简单很多了。

关于编译理论的知识,说起来实在太长,这里就不深入了,主要讲讲我如何利用其替换js中的变量的。

现在比较流行的JS解析器主要是:( https://github.com/mishoo/UglifyJS2)[UglifyJS2]%5BUglifyJS2%5D)

UglifyJS2的主要功能其实是用来压缩js代码,不过他的压缩过程大概分了三步:parse,transform,generate code;平常我们可能并不需要关心这三步,一个方法就能搞定,不过稍微研究下就会发现,这三步拆分开很有意思,特别是transform这一步,我们可以替换成自己的逻辑,然后做很多改编代码行为的事情。

看我们的实现代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var UglifyJS = require(uglify-js);
//解析ast
var ast_code = UglifyJS.parse(source_code); 

//遍历ast,找出所有定义的变量
//为何分成两次,为了排除没有定义而直接使用的全局变量
ast_code.walk(new UglifyJS.TreeWalker(function(node){
    //如果是变量定义的节点
    if(node instanceof UglifyJS.AST_SymbolVar){
        varNodes.push(node);
        for_trans_words.push(node.name);
    }
}));
ast_code.walk(new UglifyJS.TreeWalker(function(node){
    if(node instanceof UglifyJS.AST_SymbolRef){
        if(for_trans_words.indexOf(node.name)!=-1){
            refNodes.push(node);
            for_trans_words.push(node.name);
        }
    }
}));
 处理替换ast节点中的name

//生成最终代码
ast_code.print_to_string({
    beautify:true
})   

解析和生成代码,UglifyJS都已经帮我们做好了,我们需要做的就是利用 UglifyJS.TreeWalker 来遍历所有的AST节点,判断其类型,然后替换或者转换。

我们这次的需求其实很简单,就是找到某些节点,把他们的name翻译成中文。

具体的实现见github。

翻译

hancode使用百度翻译来翻译英文到中文。事实上百度翻译支持很多语言的互译,使用也很简单。

百度翻译有专门的云服务平台,地址如下: 百度翻译开放平台

前端乱炖的url seo也是用的这个api。

百度翻译开放平台

全局变量处理

这个库里面有一些小细节,例如有些变量是全局变量,但是AST里并没有标记出来,需要自己判断,如果判断有Ref但是没有定义的变量,即为全局变量,例如 require/console 之类的。

这就是上面的代码片段中为何要遍历两次ast的原因。

重复翻译结果处理

这也是一个细节,如果两个变量的意思很接近,可能会被翻译成同一个结果,如果直接使用会造成代码错乱。

其实一个简单的方法就可以处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
 * 把一个object里的value都变成唯一的,如果不是唯一的,给他加_1,如果出现两次则_2
 * @param obj
 */
var makeUniqObject = function(obj){
    var scaned_values = []
    for(var I in obj){
        var value = obj[I];
        if(scaned_values.indexOf(value)!=-1){
            var repeat_count = 0;
            scaned_values.forEach(function(v){
                if(v==value){
                    repeat_count++;
                }
            })
            obj[I] = value+_+repeat_count
        }
        scaned_values.push(value);
    }
}

结语

这个项目其实没什么高深的,但是的确挺好玩,可以拿去唬人。