前端猎奇系列之探索Python来反补JavaScript——上篇

4,869 阅读12分钟

写在最前

人生苦短,我用 JavaScript。 然鹅,其他圈子里还流行这样一句话:人生苦短,我用 Python。 当然还有什么完美秀发编程,这就不提了。当你在前端学到一定地步的时候,你会有种想出去看一看的冲动,翻过这座山,去山的外面,看看外面的风景。虽然外面的风景也就那样,但是还是忍不住去看一看(%E8%B4%B1%E5%91%97%0A)。

多说一句

作为一个不安分的 FEE,我表示就算学了忘,我还是会学的。学了忘,和从来没有学过,是完全不同的概念。我记得有一个成语是这样的,叫 触类旁通

通常我们会这样理解,我举个例子,比如我学习 JavaScript,我通过学习 JavaScript 来慢慢通晓其他类似的语言。但是我觉得我们还可以换种角度理解,比如我 JavaScript 学习的很好了,我想去学习 Python ,通过学习 Python 来反补我的 JavaScript ,通过 Python 的学习,使我对 JavaScript 的思考变得更深了。比如,import等关键词在 Python 中早就有了。其实我想说的是,当你学习的内容越广泛的时候,涉猎的知识越多的时候,看待事情的格局越高的时候,你会发现,很多问题,都可以用 万变不离其宗 来解决。嗯....这哪是多说一句,明明就是一段。。。。

恩恩,,开始探索了啦!!

出自一家门

我喜欢把其他所学的知识和前端的知识进行比较。在整个较为系统(看慕课视频和阅读好书)的学习了 Python 后。其实收获还是挺多的。

其实 PYJS 有很多相同的地方,一个最重要的相同点就是: JSPY 都是解释型语言。解释型语言俗称脚本语言,翻开编程语言的历史,会发现 Perl 语言的诞生是脚本语言走向成熟的标志,有兴趣可以自行查阅资料去了解,如果想深入了解编程语言原理,可以看《编程语言原理》第10版,作者是赛巴斯塔。写的非常棒!别问我为什么,因为我以前看过了,嘻嘻。

其实解释型语言和 Java 这种编译型语言本质的区别就是如下一段话:

解释型语言是不需要开发者进行编译的,是在运行程序时才被翻译成机器语言。而Java这种语言是需要开发者去手动编译的,究竟哪个好呢,其实没法去比较,要是说性能,肯定是Java的性能更好,因为一次编译后,后面多次运行就可以直接运行字节码文件,不需要再次编译了。而JavaScriptPython这种语言,虽然性能差了点,每次运行都需要进行编译,但是开发效率高啊,可移植性也非常好。给它一个rumtime,它送你一个快乐的人生。 而且,我是脚本语言,我慢咋了?没听过计算机科学领域有一句非常 NB 的名言么:

计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。

根据这个理论,完全可以找到一个解决解释型语言运行慢的方法,那就是JIT compilation,翻译一下就是即时编译,用一个中间层,把翻译的机器码保存下来,等下次再次调用的时候,就直接从缓存中执行之前就已经编译好的机器码。

JS一直在吸收PY的优点

作为脚本行业的翘楚,PY一直扮演着高贵的角色,PY的设计哲学是优雅、明确、简单。而回头望一眼当初用了10天就发明出来的JS,我发现JS就好像一个白手起家的屌丝(人穷志不穷)。通过农村包围城市的方法,一步一个脚印,最终杀出一片属于自己的天空。

为什么要这样说呢,是因为我个人理解的,JS在被创造出来的时候,并没有给与太大期望,这也导致了很多东西在创造出来的时候没有考虑到,比如JS是基于面对对象进行设计的,但是却没有类、继承等面对对象的语言所必须拥有的特性。而PY从被设计之初就已经是一门面对对象的语言了。我举个栗子,JS中的this,有多种指向,对于刚入门前端的同学,是一个很难理解的知识点。纵观整个OOP(面对对象编程)语言,this的指向都很明确,指向由类创建出来的对象。而在JS中,正是因为设计之初的定位模棱两可,说是OOP,缺没有实现类等特性,但又是基于面对对象进行设计的。

既然没有在语言设计层面给出相应的关键字,例如class关键字,如果没有类,那JS还有什么,那就都是函数了丫😂,OOP的不明显,那剩下的只能OPP(面向过程)了呀😂。那这样一来,this的指向就会变得有多种情况,具体情况就不说了,自行查阅资料。但是呢,是时候拿出名言了:

计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。

你说没有class关键字 ? 无法实现继承? 无法实现多态性?不存在的,我就算饿死,我也会把这个实现了。嗯....真香~~ 然后就出现了一开始的使用原型链来实现继承,同时为了减少内存消耗,可以优化成组合继承。其实这一切的一切,都是为了填当初设计时没有规划好的坑,前同事挖坑,我来填。再后面,等ES6出来的时候,终于可以使用class来写OOP了。但是这个class也只是语法糖,说白了同事留下的坑,已经深入地心了!全世界都在用,我能怎么办?我内心谎的一笔,不能直接从根本上进行修改,那只能采取靠上面那句名言了,我造一个间接的中间层来实现class功能,其他的底层我都不动,向下兼容,毕竟用户是全世界。所以日子久了,你造一个中间层,我造一个中间层,然后JS变成的越来越优雅、明确、简洁。日子就越来越好了。

在全世界的FEE的努力下,JS一直向优雅、明确、简洁的方向上努力前进。

JS是如何吸收PY的

吸星大法也是分等级的,吸的不好,可能会炸。

第一个关于分号这个事情:

编写PY代码时不加分号是一个标准,使用换行符作为行代码结束标志。目前JS主流的框架都提倡不加分号。目的很简单:简单高效。JS日后的标准。

第二个关于关键字

PYJS都使用import作为模块导入关键词

第三个关于函数

都有闭包、匿名函数,都可以使用lamada表达式。

这里说一下,其实有些其他语言也有,我学习PY的目的是为了反补JS,让我换个角度去看我的小可爱JS。其实在前端,很多人搞不清楚闭包。知其然,不能知其所以然。为什么会出现这种情况呢?

我个人认为,最主要的原因是因为闭包在JS语言中扮演着很重要的角色,说开点就是用到闭包的地方和场景太多了,很多场景必须使用闭包才能完成,然后呢闭包在很多场景的作用也不一样。很多刚入贵圈没多久的宝宝,还只停留在很浅的理解上。比如防止全局变量污染、模块化等。并不能深入的理解到闭包在JS中的重要作用。想透彻掌握闭包,那编译语言原理是肯定要掌握或者了解的。

闭包在JS中有多重要,我个人认为:闭包在很多语言中都存在,但JS对闭包的依赖,超过其他任何语言对闭包的依赖。为什么这么说呢?我们把格局放的大一点:从编程语言原理的角度来看来闭包,闭包其实就是:

一个子程序和定义它的引用环境。也就是如果子程序可以从程序的任意位置调用,就需要引用环境。

中断一下,先不看闭包,你会发现有个词很陌生,叫引用环境。其实这里的引用环境也叫作用域。不同叫法而已,那么问题来了,引用环境(作用域)是什么东东?

引用环境(作用域)是指这条语句中所有可见变量的集合。

怎么理解这句话,其实这句话对你理解前端经常提的作用域非常非常重要,请看一个非常简单的代码:

const g = 'haha'
const a = 'i am godkun'
function fun() {
  let b = 'hello world'
  console.log(b + a)
}
fun()

OK,看上面代码,fun()语句的作用域是什么?按照静态作用域语言的特性,你要去fun函数的代码执行处去看,通过fun函数可以知道,fun函数的作用域(引用环境)就是fun函数体内的局部作用域中声明的的变量b和全局作用域下的变量a的集合,这里强调一点,就算全局变量g没有被fun函数使用。那也是算在fun函数的引用环境里的。

总结一下就是: 在静态作用域语言中,语句的引用环境是在它的局部作用域中声明的变量,和在它的祖先作用域中声明的所有可见变量的集合。

好了,继续开始闭包吧,也就是如果作用域为静态的编程语言不允许嵌套子程序,那闭包就没有什么用。如果允许嵌套子程序,那就支持闭包。这种允许嵌套子程序的语言中,子程序引用环境中的所有变量(其本地变量和全局变量)都是可访问的,无论子程序在程序的什么地方调用。 这句话不好理解,我们可以以JS为例子进行通俗阐述:

JS是一个静态作用域语言,同时允许嵌套子程序,如果JS不允许嵌套子程序,那一首凉凉送给小可爱JS啊。什么是静态作用域语言,官方解答就不说了, 我通俗点说,就是你的JS程序在声明时,就已经确定好作用域了。学习过编程语言原理的应该知道 静态作用域又叫做词法作用域,使用词法作用域的变量叫词法(lexical)变量。

function say() {
  let str = 'hello world'
  console.log(str)
} 

从上面可以知道,变量str就是词法变量。那么最核心的本质要出来了。

词法变量都有一个确定的作用域和不确定的生存期。

词法变量的作用域可以是一个函数或block,使得其在这段代码区域内都有效。自从JS支持了块级作用域(let声明的也不能算真正意义上的块级作用域,不说这个了),这个block也就成为了现实。不过为什么说词法变量的生存期不确定呢,是因为词法变量的生存期取决于该变量需要引用多久。而引用多久,这是我们可以人为控制的。而人为控制总是会不靠谱的。所以就诞生了 GC 这种神器。

你会发现,从上面的静态作用域可以知道,应该还存在动态作用域。动态作用域是什么呢,不说了!,毕竟和JS无关。我只说我反补的!说到这,我是不是应该写个闭包文章,算了吧,以后再说吧。

发现说不完了

一本来想一篇搞定的,发现写着写着收不住了,一想到我今天9点(写到这时间已1542994654097)还要参加VueConf,明天还要参加上海的谷歌开发者大会。然后刺激的是今天我要5点起床做高铁,嗯,默默的在文章标题最后加了 ——上篇, 我还是收手睡觉吧。

其实我学习PY的目的是为了反补JS,通过PY来看清楚JS。其实学习服务端语言,对前端有一个很重要的帮助,就是可以深入理解Web编程中的关于网络方面的很多知识,TCP/IP 、 Socket 、HTTP等等,以及在web安全方面,服务端是如何做的。等等吧,下篇再说吧,当然还有我学习PY后的一次小实战,也算是学有所用吧。虽然用途不大,但是有趣就好。

可能有人会说,Node.js不也是服务端语言么,为什么不学习Node.js,这个我要说一下,Node.js我也学丫。比如Node.js源码中就大量用到了闭包,异步IO操作就用到了闭包。但是Node.js也是用JS写的😂,我是猎奇系列,所以Node.js 不考虑在内。

PY有装饰器,JS没有,但是伟大的转译器Babel可以解决这个问题,我觉得可以脑补一下,Babel这种神器以后甚至可以统一脚本语言。按照Babel规定的语法规范写,然后Babel通过对现有的代码进行分析,解析成AST,然后转换,生成新的AST,然后再让解释器去解释新的代码结构。

然后你懂的,根据命令来编译出你想要的脚本语言。PYJS结合为一体,最终变成了:

人生苦短,我爱 PS。 卒

兄得,如果你也爱 PS ,就点个赞吧,嘻嘻。欢迎关注,后续系列将更加精彩😊!