只要功夫深,铁杵磨成针。在钻了好几个晚上和周末后,我没曾放弃过的布朗运动终于动起来了。怀着喜悦的心情,
给大家伙讲讲我看到的,学到的关于优先队列这个数据结构,ES6编程,和Canvas动画的事。
演示
原理
这个系统模拟的是小球在一个有限的空间里完全弹性碰撞的情况。上面的演示中大小不一的小球一共500个。实现这个系统有三个核心模块:
- 基础模块:两个小球之间的碰撞预测和碰撞后的响应解析
- 统筹模块:管理所有小球之间的碰撞顺序,以及任意两个小球碰撞后对系统的更新
- 动画模块:动态的描绘系统的状态
其中基础模块用到的是物理力学和矢量的知识,统筹模块靠的是优先级队列的数据结构,HTML Canvas帮助做动画。
动机
做这个程序,主要有两个企图。
- 想切身感受一下优先队列这个数据结构的美妙。这个想法是在看完了Coursera上的算法课之后有的,毕竟一下高效的模拟这么多小球的相互碰撞不是一件容易的事。
- 学习一下ES6这个编程语言。之前一直抗拒尝试ES6,是担心用到写不出优美的代码。后来看到最新的ES6,支持了模块,类,箭头函数等让大家期待已久的特性,手有点痒痒,决定试一下。
做完之后,别的先不提,单是把它真真切切,活生生的跑起来,就让我高兴了好几天,这便是程序员的最大的好处,创造东西有成就感。除此之外,收获还是蛮多的,下面稍微详细的侃侃从不同角度对ES6,和它周遭的生态系统的粗浅看法。
好的
到处都是Javascript
Javascript吸引我的最大的一个原因,就是我可以把它给部署到任何一台有浏览器的电脑,分享给成千上万的人。这就是Web的魅力。这个程序完全可以用Java,Scala或者其他语言写,
但想分享给其他人,其他人必须得先安装JDK,然后再下载我这个程序,再运行,才能看见点什么。
Web的话,点一个网址即可。并且我软件的升级对网友来说是完全透明的,他看到的永远是最新的版本。
模块化
在我看来,模块化的支持将把Javascript的有效打击范围再推向一个更广阔的阶段。因为模块化是构建复杂系统最重要的工具。从我这四五年做软件开发的经验来看,模块化有很重要的意义。
大事化小,小事化了
大事化小,是毫无疑问的,小事化了,就是在谈理想了。模块化的这个好处,是从我们人类认识世界的角度来出发的。一个人在认识复杂事物的时候,不可能一下就抓住它的全部,而是先从一些容易理解的比较基础的地方开始,然后一点一点积累出来,最后看到全貌。
举个简单地例子,让你算个
12*5+(24/6 + 16)/2.5 - 2
等于多少,你不可能一下就知道结果,而是先把这个复杂的算式,分解分解,弄成最基本的加减乘除,然后一步一步得到最后的结果。
做软件是同样地道理。把复杂的系统,分割成稍微简单地子模块,依次往下走,子模块做好了,大的系统才可能见得天日。这一点和任何开发语言都没有关系。甚至和编程本身都没有关系,因为它是我们认识,发现世界的基本方法。
你变你的,别影响到我就行
这是模块化带来的第二个非常关键的好处,隔离变化所造成的影响。在软件这个领域里,我们最喜欢的是变化,最害怕的也是变化。为什么爱得也是它,恨的也是它?
爱它使因为它带来的是效益,有变化说明客户有新的需求,需求越多,变化越多。恨它,是因为变化不好控制,一不小心,就造出了Bug,并且有时候都不知道,为什么改了东墙,却倒了西墙。
模块化所扮演的角色,就是隔离,一个模块内部的变化,一定要内部消化,不管你怎么消化,别影响到别的模块就好。就像我上面分的模块,动画模块和基础模块是隔离的,小球怎么撞那是基础模块的事,不管怎么撞,动画模块画球的方式都不会受到影响。
拿来用用
吃别人嚼过的馍馍没味,但用别人已经做好的模块就很爽,因为这能给你节省时间和精力,让你专注于别人没有而你独有的东西。这是我认识到的模块化,激活的第三个非常关键的特点。
我的这个模块里,直接用到的别人做好的模块是优先级队列这个数据结构,在es-collesions里。我用得没有预期中的那么顺利,因为它由Bug,就像所有的拿来主义一样,要持有敢怀疑的态度。我是最后才怀疑的它,最后证明Bug就在他那里,我给他修了。
生态系统
这里我所谓的生态系统,是指的在Javascript这个语言周围的一些辅助开发的工具。
Atom
Atom是Github的一款文档编辑器,用Javascript/Coffeescript写的,但它早已超越了文档编辑器的范畴。琳琅满目的插件,多种多样的功能
完全可以使它成为一个开发的整合环境,你所需要的代码补齐,代码高亮,搜索替换,git,vim等等很多工具都可以在里边找到相应地插件。唯一不足的是,
缺少调试功能。
Webpack
我查得第一个工具就是构建工具,稍微一查,真是有点被吓到。构建工具太多了,虽说百家齐鸣是好事,但多了也麻烦,这就牵扯到标准方面的事情。大概看了一下,最老牌的是Grunt,相当于Java里地Maven,推崇的是Convention over Configuration,我很喜欢这种方式
,大家都用同样地东西,学了一样工具,就到处都可以用。比较新的有Gulp,我说它比较像Java里的Gradle和SBT,Code as Configuration,配置文件本身也是代码,它省了不少配置的复杂度,也很有吸引力。
可最终我选择了,配置最简单,支持ES6最快捷,注重模块化的Webpack。几行配置文件就搞定了ES6的支持,并且自动生成一个Bundle整合文件,HTML只需要导入这个文件即可。很快就可以上手。它最有吸引力的是它关于Code Splitting的关注,可我后来意识到这里所说的
Code Splitting更像是Java 9里谈到的自动加载独立模块的概念,在我的项目里没用到。稍微细说一点,就是你有很多模块,当一个用户用你的应用的时候,未必一下子需要把这些模块都加载进去,提取给他有用的那些部分模块就好,简单地说是为了减少代码的下载量。
测试驱动
测试驱动,在使用像Javascript这样的松类型的开发语言的时候,所扮演的角色就更重要了。松类型的一个弱点就是不能在编译阶段找错,所以我们犯错误的概率就更大一点,所以早测试就尤为的重要了。
Webpack里有测试的Plugin,但是都是基于浏览器的。由于我的基础模块,和统筹模块都不操作DOM,完全可以脱离浏览器独立的测,所以我就选用了Mocha的单元测试,和Babel编译器相结合,单元测试非常快。试了一下Karma,是和浏览器结合的,太慢了。
Animation with Canvas
最后,说一说动画模块,一个简单地函数window.requestAnimationFrame
,就天衣无缝的把网友计算机的屏幕刷新机制和代码联系了起来,
这真的让人叹服。我觉得这是Web的另一大魅力,几乎所有我们能想象到的应用都可以用Web的技术来实现,WebSQL, WebSocket, WebComponent…
可改进的
松类型(loose typing)
松类型是有好处,因为它给了你更多地灵活性。我在基础模块了就用到了这一点。可以和小球碰撞的物体有两种,一个是小球和小球撞,另外一个就是墙和小球撞。我在球和墙的代码里,都写了一个撞小球的方法,它们不用继承同一个父类,我可以直接把他们放到同一个集合里,
然后调用它们共同的方法和小球撞。这在Java,Scala或者Haskell这些强类型的语言里是不可能实现的。
但它也有它的缺点,首当其冲的是,松类型不利于在编译阶段发现错误,这个弱点可以用单元测试来弥补。但最让我头疼的是,一个函数所传达的信息是不完整地,它只告诉了你它接受什么参数,而不告诉你要返回什么。这对代码的可读性是一个非常大得影响。我想一个可能的
改善的方法,是把函数的名字,起的更详细完整一些,在函数的名字里,告诉别人它要返回的是什么。
下一步
Webstorm IDE
Intellij在Java中的体现是有目共睹的,相信WebStorm也可以做的很好。Webstorm相对于Atom,对我的吸引力体现在它对调试的支持上,
也许我应该写更多的单元测试,彻底地省去调试的需求。
JSHint
我看了两个Douglas Crockford的演讲,一个是比较老一点的JavaScript: The Good Parts,
另一个是新一点的The Better Parts,他都提到了如何用JSHint帮助我
们避免Javascript里地一些陷阱,只用它好的部分。
尾部递归
另外一个我没有提到的ES6的新功能便是对尾部递归的优化,这么一来,我就完完全全没有必要在代码里使用循环了。