对编写JavaScript代码的理解

web development

前言

从14年7月开始从事前端开发至今已经有4年多的时间,从入行的很长时间里,觉得自己都还没入门。直到最近的某个时间回想,觉得现在的自己可能算是入门了。

期间参与过多家互联网(科技)公司的前端开发工作,经历过10w+ JavaScript 项目的洗礼,修过一年的BUG、写过富文本编辑器、WebAPP、Hybird APP、JS库等等,也和很多个前端开发者合作过,查阅学习过很多人的代码。其实目前得到的结论是,由于JavaScript的入门门槛过低(自己也是因为这个原因入行),导致JavaScript的代码质量普遍偏低。所以想用自己的入门经验来给那些从事JavaScript开发,但是尚未入门的人一些经验的分享。

大多为个人实践的思考和总结,可能也会存在一些理解偏差和差异,希望可以指正。

如何评判代码质量

可能一直以来代码质量都是一个虚的表述,没有可以遵循的标准来衡量一份代码的质量。但是在实践中我们会得到一些依据。

  • 是否可维护
  • 是否易扩展
  • 是否易重构
  • 是否边界异常情况处理的到位
  • 是否优化了性能、运行是否高效、内存是否有效管理和占用
  • 是否单元测试覆盖,及高覆盖率

编程原则

  • 复用:最大化的复用,而不是Copy相同的代码,保证极低的代码重复率
  • 模块(组件)化:问题分解,把大的问题,分成若干个小问题去处理,保证一个函数(类)只做一件事的原则,降低复杂度
  • 数据操作分离(松耦合):每个类(函数)都是一个独立的机器,注入数据得到实例或得出结果,不依赖任何外部状态
  • 注重运行效率:保证代码以较高的效率运行,并持续优化

编程方法

在目前的大多数实践中,是以类的继承的面向对象编程为基础加上函数式编程来构成整个项目。

继承

其实不管是ES6的Class概念出现之前还是之后,JavaScipt 大型工程的继承大多都是基于类的概念来实现,Class的出现,使我们可以更加显式的使用类,即面向对象的编程的思想来实现继承。

复用

类用处在于继承,而函数则是复用。当一个函数被考虑要复用的话,就需要把这个函数写成一个无状态的纯函数,自身只接收数据,然后返回新的数据。只是一个处理数据的机器

封装

即将常用的多个逻辑语句(或者函数)封装为一个公用函数或者一个公共的类,提供方便的调用方式,这样可以更好的复用,降低重复率,对提高可维护性有重要意义。(这里所说的封装仅仅是一种描述形式,有别于传统编程语言中的封装概念)

依赖注入

依赖注入的思想在AngularVscode 中被广泛使用,将运行时的依赖通过参数的形式注入进实例中,这种方式有点类似函数式编程,可以保持实例的松耦合,每个实例都是一个独立的机器,会使得逻辑清晰,依赖清晰,利于维护。

内存管理

虽然JavaScript的引擎都有内存回收机制,但是想要控制良好的内存占用,必须手动来管理内存(VSCode 就是个很好的例子),主动的来清理和消除不必要的内存暂用,可以对整个JavaScript的性能和运行效率有很好的提升。

比如一些异步方法、定时器、大的数据量变量等,我们必须根据使用逻辑来主动销毁,否则就会带来问题和不必要的内存占用。

执行效率

其实我们在写每一段代码时,都需要去思考和考量这段代码是否高效。考虑如何降低时间复杂度、如何通过最短路径求解、是否可应对极端情况、是否可以分场景提高运行效率

代码保障

其实有一些现实案例值得我们思考,为什么普遍IOS应用比Android应用质量(指的运行效率、稳定性、BUG量等)高,为什么React写出的代码容易维护、重构、拓展。其实我们从人的这一方面来保障代码的质量是不可靠的,必须上升到工具方面、底层框架设计方面、系统层面,才是靠谱有效的保障。

Typescript

目前JS还没有较好的底层框架、系统层面的保障。其中Typescript算是一种目前比较好的从语言层面的保障。“Javascript 不适合编写大型项目”,而Typescript 使得 JavaScript 可以编写大型项目。

JSLint、TSLint、ESLint

这些 Lint 一方面可以规范代码样式,另一方可以规范一些逻辑,对代码质量起到基础保障的作用,建议项目均开启基础的Lint规则,也可以参照业内成熟的规则范例。

单元测试 自动化测试

当项目足够庞大复杂时,单元测试是必不可少的,否则在维护迭代中,很难保证代码修改后整个项目的稳定性。自动化测试有时候和项目的运行模式、依赖等有关不容易实现,但是自动化测试如果实现了,将会是更好的一层保障,基本可以保证可用性和稳定性交付。

保持头脑清醒

一般低级错误和BUG都是在自己头脑不清醒的时候产生的。比如一直加班到12点,头脑浑噩的准备最后提交一次代码,赶紧回家睡觉的时候;比如上线后的紧急修复线上BUG的时候。在紧张或者困乏头脑不清醒的时候写出来的代码是最容易携带问题进去的。所以我们要时刻保持清醒和镇定,如果无法镇定下来,可以暂时停止,找个自己可以调整的方式,短暂的调整之后再重新进入编码。

Code review

刚开始对Code review 是拒绝的,自己的代码被别人说三道四终归还是不爽,经常和Review者“干”起来,好多GitHub上的Pull request 也会经常看到两个人“干”起来。一味地“蛮干”是完全没有意义而且浪费时间的,拿证据、摆事实、讲道理说服对方或者接受对方的意见才是正确的做法。

当你遇到一个负责任的Review者的时候,会帮你规避很多BUG,比如逻辑错误、不严谨、误用等等被自己忽略的问题,可能也会学到一些编码技巧、编码规范, 这个过程其实也是开发者间交流成长的一个契机。一旦尝到甜头,之后你会需要甚至依赖Code Review。

一些想法

沟通

实际开发中沟通可能会占据大量的时间,个人觉得沟通需要遵循这样的原则,优先在线非实时沟通(gitlab issue commit 等)其次邮件通信工具沟通,最后才是面对面沟通。

面对面沟通其实很容易造成低效,而且会粗暴打断对方节奏,不是必须应尽量避免当面沟通。线上沟通更容易让自己深入思考,理清思路,突出需要沟通问题,往往有些时候在你梳理问题的时候,自己就把问题解决了,而无需去沟通。

非当面沟通的一个好处就是不易打断对方,所以在充分分析自己的问题之后,言简意赅的将自己问题发给对方,并给对方一定的处理时间,不要一味的催促,才是比较好的方式。

前端开发者有个比较常见的沟通场景,和后端联调接口。这个场景比较科学的实施应该是这样的:

  • 前后端其中一方书写接口文档,另一方Review确认
  • 前端后端分别开发,Mock数据或Mock请求,双方只对文档负责,相互无任何其他依赖
  • 前后端均开发完成,将Mock接口切回测试环境接口,做回归
  • 对回归测试出的少量的异常,前后端做简单沟通即可

其实在这个场景中前端需要沟通的东西是不多的,但是在实践中会发现好多开发者的大量时间浪费这上面。

沟通的态度也是很重要的,多用敬语、多谦让和理解、对人友善才会得到友善对待。

注释

曾经看到过这样一句话,大意是“好的命名,可以减少注释”。还是比较认同这句话,首先我们注重变量、函数、类的命名,减少不必要注释。只在难以理解的逻辑和特殊处理的逻辑处写少量的注释。只写必要注释,注释太多影响整个代码的可读性,而且如果注释没得到有效维护,在逻辑变更后反而引起了误解。

文档

文档应该是代码最好的伙伴,文档可以极大的降低沟通成本、维护成本并且可以帮助梳理思路等等。

不要深陷其中

实际编码中会经常遇到各种各样难以解决的问题,有时候甚至会觉得这个问题是无法解决的,但是到最后都发现大部分都可以解决。

在遇到难以解决的问题

编程理解

  • 当发现一个自己参与的项目,可以轻松的定位问题,轻松的重构、轻松的拓展,这个时候就可以享受编程的过程,把更多的精力花费在算法优化、工程优化上。

  • 代码写到最后,会发现自己只是在写声明配置文件而已。将函数或者类定义,然后定义参数,然后串联起来,然后实现函数或类的细节,仅此而已。

  • 代码质量是受个人性格、当时状态影响的。道理都懂,就是时间赶任务重,而做不到。所以我们要在平时养成良好的习惯,这样即便在高压状态下也会基本按照好的习惯来编码。好的编码习惯绝对是节省时间而不是浪费时间。
  • 编码期间,不要试图通过不断的试错来解决自己不了解的问题,所有的不了解最终都会回归到交付期的BUG。自己挖的坑,还是自己进。所以一定要对编码的周边API、环境、用法等做的十分的熟悉,不留盲点。