Transcrypt是一个很有意思的工具: 它让你告别手写繁复的JavaScript代码,使用相对简明清晰的Python代替这一工作。 之后使用这个工具,可以把Python编写的代码转换成JavaScript。
1. 为什么不直接写JavsScript?
JavaScript本身不算是很难的编程语言,但还是有很多不便之处。这里只能举几个例子:
1.1 js的模块化问题。
想要实现一个很复杂的js程序,一般要考虑将不同的功能拆分成模块,然后各自完成各自的任务。
然而,js本身没有什么方式可以做到这一点:
要么在浏览器或者NodeJS中,使用require
这样的方案(”AMD”—-模块异步加载)(浏览器还需要额外加载require.js
),
要么使用各种打包工具(CommonJS—-规定了通用的模块定义方式),根据模块各部分代码相互关系,将所有的代码打包进一个巨大的文件。
Transcrypt支持Python的模块机制(import
语法),效果上最后还是生成一个打包的代码文件,但使用起来,比CommonJS要清晰一些。
1.2 缺乏对class
这样的关键字的支持
JavaScript虽然算是一种基于对象的语言—-JavaScript中包括数字、字符串等都是对象,
但又没有办法通过class
来自己声明一个对象。
这就导致不同的程序员,会采用不同的方案来构建对象。比如有使用Object的:
或者改写一个函数,增加各种attributes:
1.3 缺乏语法糖,代码复杂
Python简洁的语法,很多得益于丰富的语法糖: 很简洁的几句话就可以实现复杂的功能,而JavaScript则可能要从头开始写一系列代码。
举几个例子:
a) 使用给定的值生成一个字符串
这也是一个简单的例子,如何套用模板,将数据转换成供显示的文本。js的解决方案就复杂许多:
- 首先,js是不支持带有换行的字符串变量的。所以我们不能用Python
"""..."""
这种语法,指定一个带有换行的字符串。 - 其次,也不能在js的字符串中定义占位符,然后用数据填充,所以要自行断开字符串,将数据与字符串合并。
看起来结果就是:
即使看上去代码量差不多,这样做还是有一个缺点: 如果需要修改模板,使用Python,只要将带有占位符的字符串换掉就可以了(比如字符串在单独的模板文件中予以定义), 但使用js,就需要具体修改带有逻辑运算符的代码。这样就很不直观,容易出错。
b) 根据已有数据生成一个新的数组
假设我们有一个数组[-2, -1, 0, 1, 2, 3, 4]
,想列出这个数组中大于0的各个数字的平方。
使用js的思路,是新建一个数组,然后遍历原数组,检查各项是否大于0, 如果是,将结果记录到新的数组:
Python则简单很多。Python支持在构建list
的时候,指定条件,并使用表达式指定要放入list
的值:
1.4 复杂的异步编程
JavaScript的另一个令人诟病的问题,是在调用一系列异步调用时难以组织代码。
实际应用中,无论NodeJS还是浏览器,都要遇到各种异步调用的代码。
假设我们有3个函数:
readFile
用于读取一个文件;encrypt
用于加密数据;upload
用于将数据上传到服务器。
这三个函数都是异步调用的。
传统的方式,一般是在实际完成功能需要的参数之外,这些函数还接受一个callback
(回调函数)作为输入。
这样调用上述函数之后,他们会立刻返回,并不耽误时间。然后当函数对应的后台任务完成时,再呼叫callback
,传入结果。
比如这样看,可以假设readFile
的用法是readFile(filename, callback)
。
这个函数接受一个filename
作为要读取的文件名,然后在读取完毕时呼叫callback
函数,传入读取结果。
实际调用时,要写成:
假如我们想在读取文件后,将文件内容加密,就要在上述代码的读取成功处,
加上对encrypt=encrypt(data, callback)
函数的调用。于是代码就成了这个样子:
如果进一步,想在加密之后,将密文上传到服务器,整个代码就……
很多时候,要完成的工作都是上面这样一连串的。 js的回调机制,就很容易引入如上所示不断嵌套的代码,让结果变得十分难看,所谓的回调地狱。
为了解决这个问题,JavaScript引入了Promise
机制。
这样每个函数就都返回一个Promise
对象。
Promise
对象支持使用.then()
这样的方法,将流程串联起来:
这样简明了许多。
Transcrypt在Promise
机制的基础上,结合了Python 3引入的async
和await
语法,让上面的过程变得更加直观:
使用await
,这些异步的调用又可以写成一系列按顺序完成的代码,而不失对各步的控制,思路清晰很多。
2. 如何使用Transcrypt?
2.1 安装和调用
Transcrypt可以通过pip安装:
$ sudo pip install transcrypt
安装后,直接在命令行调用就可以了,例如,
$ transcrypt main.py
可以将main.py
转换成js文件。
类似Python 3在执行代码前会在__pycache__
目录中放置Python字节码那样,
transcrypt会将转换后的文件放到相应的__javascript__
目录。
如果要求转换的是main.py
,则在__javascript__/
中,一般会有如下文件:
main.mod.js
,这是仅仅包含main.py
各行代码相应js转译的文件。 这个文件中会用很多transcrypt的函数“包装”输入的Python代码,但基本上是一对一的对照, 因此可供用户检查代码是否有问题等。main.js
,这是将main.py
转译为js,并嵌入transcrypt本身需要的js代码,打包而成的文件。 这个文件可以独立嵌入到网页了。main.min.js
。如果见到这个文件,说明transcrypt还进行了代码压缩,这个文件应该也可以单独运行。
代码压缩(minify)需要很多运算,耗费时间,在开发程序时不方便。可以使用-n
参数,跳过这一步:
$ transcrypt -n main.py
这样就可以只生成main.js
。
2.2 一些坑
Python和Javascript毕竟是两种语言,虽然transcrypt可以在很大程度上将前者翻译为后者, 但有些两种语言内在的不一致,决定了编程时还需要注意许多坑。
在Transcrypt官网的文档中,详细列明了各种坑和其理由。一定要仔细阅读。
2.2.1 类的继承,方法的重载
在Python中,定义基类和子类,是很方便的事情,有时候这种编程方式会节约大量时间。
在transcrypt中,可以使用类的继承,但必须在转译时,于命令行使用-e6
选项。
这样生成的代码为ECMAScript 6代码,才可以启用这样的诸多功能。
重载Python的类,自定义诸如__str__
,__getattr__
或__setattr__
这样的方法,
可以编写出非常简明的程序。例如一个模板程序:
这个程序定义的Template
类,只需要像操作一般的类那样给它的属性赋值,
然后用str
函数令其生成文本即可。这种用法非常直观,但也必须在启用了ECMAScript 6转译后才能利用。
2.2.2 特殊的方法名称
如果要在python中调用一个js模块(比如jQuery的$.get
)的get
方法,直接调用会出现错误。
根据transcrypt文档的解释,这是因为在python中,get
本身具有特定的用途,
故须使用py_get
与js_get
区别调用python内部和原生js的get
方法。
同理,对于set
方法也有类似的规定。
2.2.3 __pragma__
: 很多特性的开关
transcrypt提供了一个__pragma__
函数,可以在程序中微调转译时的行为。
例如,要在python代码中调用jQuery,不能直接使用$
变量,因为$
并不是一个规范的Python变量名。
这时,就要用__pragma__
为$
定义一个别名:
之后,就可以在程序中用S
代替$
来调用jQuery了。
__pragma__
能进行的调节有许多。
很多情况下,Python本身允许的特性,transcrypt出于效率考虑默认不支持,便需要通过这一函数启用。请读者务必参考文档。