看看这些被同事喷的JS代码风格你写过多少

殷荣桧 JavaScript 今天

现在写代码比以前好多了,代码的格式都有eslint,prettier,babel(写新版语法)这些来保证,然而,技术手段再高端都不能解决代码可读性(代码能否被未来的自己和同事看懂)的问题,因为这个问题只有人自己才能解决。我们写代码要写到下图中左边这样基本上就功德圆满了。

注:由于个人水平与眼界的原因,这篇文章中并没有完全覆盖到常见的写代码的不好的习惯,所以你如果觉的有需要补充的,都可以在文章下方评论,或者直接到我的Github的这篇文章中评论。对于有用的,都将补充到我的掘金和Github中去。同时,你如果觉的文章写得还可以,Please在我的Github中送上你宝贵的Star,你的Star是我继续写文章最大的动力。

一、变量相关

(1)变量数量的定义

NO:滥用变量:


 
  1. let kpi = 4;  // 定义好了之后再也没用过

  2. function example() {

  3.    var a = 1;

  4.    var b = 2;

  5.    var c = a+b;

  6.    var d = c+1;

  7.    var e = d+a;

  8.    return e;

  9. }

YES: 数据只使用一次或不使用就无需装到变量中


 
  1. let kpi = 4;  // 没用的就删除掉,不然过三个月自己都不敢删,怕是不是那用到了

  2. function example() {

  3.    var a = 1;

  4.    var b = 2;

  5.    return 2a+b+1;

  6. }

(2)变量的命名

NO:自我感觉良好的缩写


 
  1. let fName = 'jackie'; // 看起来命名挺规范,缩写,驼峰法都用上,ESlint各种检测规范的工具都通过,But,fName是啥?这时候,你是不是想说What are you 弄啥呢?

  2. let lName = 'willen'; // 这个问题和上面的一样

YES:无需对每个变量都写注释,从名字上就看懂


 
  1.  let firstName = 'jackie'; // 怎么样,是不是一目了然。少被喷了一次

  2.  let lastName = 'willen';

(3)特定的变量

NO:无说明的参数


 
  1. if (value.length < 8) { // 为什么要小于8,8表示啥?长度,还是位移,还是高度?Oh,my God!!

  2.    ....

  3. }

YES:添加变量


 
  1. const MAX_INPUT_LENGTH = 8;

  2. if (value.length < MAX_INPUT_LENGTH) { // 一目了然,不能超过最大输入长度

  3.    ....

  4. }

(4)变量的命名

NO:命名过于啰嗦


 
  1. let nameString;

  2. let theUsers;

YES: 做到简洁明了


 
  1. let name;

  2. let users;

(5)使用说明性的变量(即有意义的变量名)

NO:长代码不知道啥意思


 
  1. const address = 'One Infinite Loop, Cupertino 95014';

  2. const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;

  3. saveCityZipCode(

  4.  address.match(cityZipCodeRegex)[1], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码

  5.  address.match(cityZipCodeRegex)[2], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码

  6. );

YES:用变量名来解释长代码的含义


 
  1. const address = 'One Infinite Loop, Cupertino 95014';

  2. const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;

  3. const [, city, zipCode] = address.match(cityZipCodeRegex) || [];

  4. saveCityZipCode(city, zipCode);

(6)避免使用太多的全局变量

NO:在不同的文件不停的定义全局变量


 
  1. name.js

  2. window.name = 'a';

  3. hello.js

  4. window.name = 'b';

  5. time.js

  6. window.name = 'c';  //三个文件的先后加载顺序不同,都会使得window.name的值不同,同时,你对window.name的修改了都有可能不生效,因为你不知道你修改过之后别人是不是又在别的说明文件中对其的值重置了。所以随着文件的增多,会导致一团乱麻。

YES:少用或使用替代方案 你可以选择只用局部变量。通过(){}的方法。


 
  1. 如果你确实用很多的全局变量需要共享,你可以使用vuex,redux或者你自己参考flux模式写一个也行。

(7) 变量的赋值。

NO:对于求值获取的变量,没有兜底。


 
  1. const MIN_NAME_LENGTH = 8;

  2. let lastName = fullName[1];

  3. if(lastName.length > MIN_NAME_LENGTH) { // 这样你就给你的代码成功的埋了一个坑,你有考虑过如果fullName = ['jackie']这样的情况吗?这样程序一跑起来就爆炸。要不你试试。

  4.    ....

  5. }

YES:对于求值变量,做好兜底。


 
  1. const MIN_NAME_LENGTH = 8;

  2. let lastName = fullName[1] || ''; // 做好兜底,fullName[1]中取不到的时候,不至于赋值个undefined,至少还有个空字符,从根本上讲,lastName的变量类型还是String,String原型链上的特性都能使用,不会报错。不会变成undefined。

  3. if(lastName.length > MIN_NAME_LENGTH) {

  4.    ....

  5. }

  6. 其实在项目中有很多求值变量,对于每个求值变量都需要做好兜底。

  7. let propertyValue = Object.attr || 0; // 因为Object.attr有可能为空,所以需要兜底。

  8. 但是,赋值变量就不需要兜底了。

  9. let a = 2; // 因为有底了,所以不要兜着。

  10. let myName = 'Tiny'; // 因为有底了,所以不要兜着。

二、函数相关

(1)函数命名

NO:从命名无法知道返回值类型


 
  1. function showFriendsList() {....} // 现在问,你知道这个返回的是一个数组,还是一个对象,还是true or false。你能答的上来否?你能答得上来我请你吃松鹤楼的满汉全席还请你不要当真。

Yes: 对于返回true or false的函数,最好以should/is/can/has开头


 
  1. function shouldShowFriendsList() {...}

  2. function isEmpty() {...}

  3. function canCreateDocuments() {...}

  4. function hasLicense() {...}

(2) 功能函数最好为纯函数

NO: 不要让功能函数的输出变化无常。


 
  1. function plusAbc(a, b, c) {  // 这个函数的输出将变化无常,因为api返回的值一旦改变,同样输入函数的a,b,c的值,但函数返回的结果却不一定相同。

  2.        var c = fetch('../api');

  3.        return a+b+c;

  4. }

YES:功能函数使用纯函数,输入一致,输出结果永远唯一


 
  1. function plusAbc(a, b, c) {  // 同样输入函数的a,b,c的值,但函数返回的结果永远相同。

  2.        return a+b+c;

  3. }

(3)函数传参

NO:传参无说明


 
  1. page.getSVG(api, true, false); // true和false啥意思,一目不了然

YES: 传参有说明


 
  1. page.getSVG({

  2.    imageApi: api,

  3.    includePageBackground: true, // 一目了然,知道这些true和false是啥意思

  4.    compress: false,

  5. })

(4)动作函数要以动词开头

NO: 无法辨别函数意图


 
  1. function emlU(user) {

  2.    ....

  3. }

YES:动词开头,函数意图就很明显


 
  1. function sendEmailToUser(user) {

  2.    ....

  3. }

(5)一个函数完成一个独立的功能,不要一个函数混杂多个功能

这是软件工程中最重要的一条规则,当函数需要做更多的事情时,它们将会更难进行编写、测试、理解和组合。当你能将一个函数抽离出只完成一个动作,他们将能够很容易的进行重构并且你的代码将会更容易阅读。如果你严格遵守本条规则,你将会领先于许多开发者。

NO:函数功能混乱,一个函数包含多个功能。最后就像能以一当百的老师傅一样,被乱拳打死(乱拳(功能复杂函数)打死老师傅(老程序员))


 
  1. function sendEmailToClients(clients) {

  2.  clients.forEach(client => {

  3.    const clientRecord = database.lookup(client)

  4.    if (clientRecord.isActive()) {

  5.      email(client)

  6.    }

  7.  })

  8. }

YES: 功能拆解,


 
  1. function sendEmailToActiveClients(clients) {  //各个击破,易于维护和复用

  2.  clients.filter(isActiveClient).forEach(email)

  3. }

  4. function isActiveClient(client) {

  5.  const clientRecord = database.lookup(client)

  6.  return clientRecord.isActive()

  7. }

(6)优先使用函数式编程

NO: 使用for循环编程


 
  1. for(i = 1; i <= 10; i++) { // 一看到for循环让人顿生不想看的情愫

  2.   a[i] = a[i] +1;

  3. }

YES:使用函数式编程


 
  1. let b = a.map(item => ++item) // 怎么样,是不是很好理解,就是把a的值每项加一赋值给b就可以了。现在在javascript中几乎所有的for循环都可以被map,filter,find,some,any,forEach等函数式编程取代。

(7) 函数中过多的采用if else ..

No: if else过多


 
  1. if (a === 1) {

  2.    ...

  3. } else if (a ===2) {

  4.    ...

  5. } else if (a === 3) {

  6.    ...

  7. } else {

  8.   ...

  9. }

YES: 可以使用switch替代或用数组替代


 
  1. switch(a) {

  2.   case 1:

  3.           ....

  4.   case 2:

  5.           ....

  6.   case 3:

  7.           ....

  8.  default:

  9.       ....

  10. }

  11. Or

  12. let handler = {

  13.    1: () => {....},

  14.    2: () => {....}.

  15.    3: () => {....},

  16.    default: () => {....}

  17. }

  18. handler[a]() || handler['default']()

三、尽量使用ES6,有可以能的话ES7中新语法(只罗列最常用的新语法,说实话,有些新语法不怎么常用)

(1)尽量使用箭头函数

NO:采用传统函数


 
  1. function foo() {

  2.  // code

  3. }

YES:使用箭头函数


 
  1. let foo = () => {

  2.  // code

  3. }

(2)连接字符串

NO:采用传统+号


 
  1. var message = 'Hello ' + name + ', it\'s ' + time + ' now'

YES:采用模板字符


 
  1. var message = `Hello ${name}, it's ${time} now`

(3) 使用解构赋值

NO:使用传统赋值:


 
  1. var data = { name: 'dys', age: 1 };

  2. var name = data.name;

  3. var age = data.age;

  4. var fullName = ['jackie', 'willen'];

  5. var firstName = fullName[0];

  6. var lastName = fullName[1];

YES:使用结构赋值:


 
  1. const data = {name:'dys', age:1};

  2. const {name, age} = data;   // 怎么样,是不是简单明了

  3. var fullName = ['jackie', 'willen'];

  4. const [firstName, lastName] = fullName;

(4) 尽量使用类class

NO: 采用传统的函数原型链实现继承


 
  1. 典型的 ES5 的类(function)在继承、构造和方法定义方面可读性较差,当需要继承时,优先选用 class。代码太多,就省略了。

YES:采用ES6类实现继承


 
  1. class Animal {

  2.  constructor(age) {

  3.    this.age = age

  4.  }

  5.  move() {

  6.    /* ... */

  7.  }

  8. }

  9. class Mammal extends Animal {

  10.  constructor(age, furColor) {

  11.    super(age)

  12.    this.furColor = furColor

  13.  }

  14.  liveBirth() {

  15.    /* ... */

  16.  }

  17. }

  18. class Human extends Mammal {

  19.  constructor(age, furColor, languageSpoken) {

  20.    super(age, furColor)

  21.    this.languageSpoken = languageSpoken

  22.  }

  23.  speak() {

  24.    /* ... */

  25.  }

  26. }

先写到这了,这是目前为止发现的问题,这篇文章中并没有完全覆盖到常见的写代码的不好的习惯,所以你如果觉的有需要补充的,都可以在文章下方评论,或者直接到我的Github的这篇文章中评论。对于有用的,都将补充到我的掘金和Github中去。同时,你如果觉的文章写得还可以,Please在我的Github中送上你宝贵的Star,你的Star是我继续写文章最大的动力。


 
  1. 注:除了上述这些人为习惯之外,就像前面提到的,对于机械性的,你可以使用Babel、Eslint、Prettier这些工具来保证代码的格式一致。

参考资料

https://blog.risingstack.com/javascript-clean-coding-best-practices-node-js-at-scale/(JavaScript Clean Coding Best Practices)

https://www.zhihu.com/question/20635785 (如何写出优美的 JavaScript 代码?)

END

作者:殷荣桧

https://juejin.im/post/5becf928f265da61380ec986

教你如何做一个优雅的Ecmascripter /转的更多相关文章

  1. 教你动手做一个 iOS 越狱 app

    前言 俗话说得好, 万事开头难. 仅仅是上图一个如此简单地不能再简单的小app, 其实都不算是app, 只是注入了一段代码进系统中, 等到特定的函数方法调用的时候就会被我们hook掉, 执行我们写的代 ...

  2. 论好的代码习惯的养成/做一个优雅的coder

    1.先说一下以前被滴滴大佬教育的事情: 以前写代码的时候,因为只需要取特定的几个字段,所以经常这么写 //Request $request for example $parameters = $req ...

  3. 3分钟教你做一个iphone手机浏览器

    3分钟教你做一个iphone手机浏览器 第一步:新建一个Single View工程: 第二步:新建好工程,关闭arc. 第三步:拖放一个Text Field 一个UIButton 和一个 UIWebV ...

  4. R数据分析:跟随top期刊手把手教你做一个临床预测模型

    临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...

  5. 【酷Q插件制作】教大家做一个简单的签到插件

    酷Q插件已经有很多了,社区分享一大堆,不过还是自己写才有乐趣,哈哈.不得不吐槽一下,酷Q竟然不更新了,出了个酷Q pro,还收费!!诶.不过这也影响不了咱写插件的心情,今天教大家写一个酷Q签到插件,虽 ...

  6. TTS-零基础入门-10分钟教你做一个语音功能

    在本片博客正式開始之前,大家先跟我做一个简单的好玩的 小语音. 新建一个文本文档,然后再文档里输入这样 一句话  CreateObject("SAPI.SpVoice").Spea ...

  7. Vue+ElementUI: 手把手教你做一个audio组件

    目的 本项目的目的是教你如何实现一个简单的音乐播放器(这并不难) 本项目并不是一个可以用于生产环境的element播放器,所以并没有考虑太多的兼容性问题 本项目不是ElementUI的一个音频插件,只 ...

  8. 用RecyclerView做一个小清新的Gallery效果 - Ryan Lee的博客

    一.简介 RecyclerView现在已经是越来越强大,且不说已经被大家用到滚瓜烂熟的代替ListView的基础功能,现在RecyclerView还可以取代ViewPager实现Banner效果,当然 ...

  9. fir.im Weekly - 如何做一个出色的程序员

    做一个出色的程序员,困难而高尚.本期 fir.im Weekly 精选了一些实用的 iOS,Android 开发工具和源码分享,还有一些关于程序员的成长 Tips 和有意思有质量的线下活动~ How ...

随机推荐

  1. 【C++】int与string互转

    int转string(注:itoa不是标准函数,OJ平台可能不接受) ; ]; string str; sprintf(temp, "%d", n); str = temp; or ...

  2. Android开发学习之SQLite数据存取浅析

    一.SQLite的介绍 1.SQLite简介 SQLite是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入 式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低 ...

  3. Fusion Tables 图层用于呈现 Google Fusion Tables 中包含的数据

    Google Maps API 允许您使用 FusionTablesLayer 对象将 Google Fusion Tables 中包含的数据呈现为地图上的图层.Google Fusion Table ...

  4. pylot 学习笔记

    安装步骤 1.下载pylot 版本是1.26,文件名是:pylot_1.26.zip 2.下载python 版本是2.5,文件名是:python-2.5.msi 3.下载numpy 版本是1.4.1, ...

  5. Linux常见的进程调度算法

    进程调度:在操作系统中调度是指一种资源分配. 调度算法是指: 根据系统的资源分配策略所规定的资源分配算法. 操作系统管理了系统的有限资源,当有多个进程(或多个进程发出的请求)要使用这些资源时,因为资源 ...

  6. winform dataGridView DataGridViewComboBoxColumn 下拉框事件

    有一个dataGridView ,有一列是DataGridViewComboBoxColumn .用动态绑定,在绑定数据的时候.我们也给这一列绑定数据 在dataGridView的RowsAdded事 ...

  7. spring 多线程

    http://blog.csdn.net/chszs/article/details/8219189 一.ThreadPoolTaskExecutor ThreadPoolTaskExecutor的配 ...

  8. Python 爬虫 数据提取

    一下子运行 七八十个  select 将会是什么样的体验呢? 业务部门提供了一个需要,要求从爬虫数据中提取出88家的数据, 并且也提供了一个excel表格,如下图: 这个时候我们可以通过拍卖行,拍卖时 ...

  9. Java WebService 简单实例(转

    一.准备工作(以下为本实例使用工具) 1.MyEclipse10.7.1 2.JDK 1.6.0_22 二.创建服务端 1.创建[Web Service Project],命名为[TheService ...

  10. 在ASP.NET MVC5中使用特性路由

    首先在RegisterRoutes时开启特性路由功能: routes.MapMvcAttributeRoutes(); 然后,就可以使用了. [Route("{productId:int}/ ...