教你如何做一个优雅的Ecmascripter /转
看看这些被同事喷的JS代码风格你写过多少
殷荣桧 JavaScript 今天
现在写代码比以前好多了,代码的格式都有eslint,prettier,babel(写新版语法)这些来保证,然而,技术手段再高端都不能解决代码可读性(代码能否被未来的自己和同事看懂)的问题,因为这个问题只有人自己才能解决。我们写代码要写到下图中左边这样基本上就功德圆满了。
注:由于个人水平与眼界的原因,这篇文章中并没有完全覆盖到常见的写代码的不好的习惯,所以你如果觉的有需要补充的,都可以在文章下方评论,或者直接到我的Github的这篇文章中评论。对于有用的,都将补充到我的掘金和Github中去。同时,你如果觉的文章写得还可以,Please在我的Github中送上你宝贵的Star,你的Star是我继续写文章最大的动力。

一、变量相关
(1)变量数量的定义
NO:滥用变量:
let kpi = 4; // 定义好了之后再也没用过
function example() {
var a = 1;
var b = 2;
var c = a+b;
var d = c+1;
var e = d+a;
return e;
}
YES: 数据只使用一次或不使用就无需装到变量中
let kpi = 4; // 没用的就删除掉,不然过三个月自己都不敢删,怕是不是那用到了function example() {var a = 1;var b = 2;return 2a+b+1;}
(2)变量的命名
NO:自我感觉良好的缩写
let fName = 'jackie'; // 看起来命名挺规范,缩写,驼峰法都用上,ESlint各种检测规范的工具都通过,But,fName是啥?这时候,你是不是想说What are you 弄啥呢?let lName = 'willen'; // 这个问题和上面的一样
YES:无需对每个变量都写注释,从名字上就看懂
let firstName = 'jackie'; // 怎么样,是不是一目了然。少被喷了一次let lastName = 'willen';
(3)特定的变量
NO:无说明的参数
if (value.length < 8) { // 为什么要小于8,8表示啥?长度,还是位移,还是高度?Oh,my God!!....}
YES:添加变量
const MAX_INPUT_LENGTH = 8;if (value.length < MAX_INPUT_LENGTH) { // 一目了然,不能超过最大输入长度....}
(4)变量的命名
NO:命名过于啰嗦
let nameString;let theUsers;
YES: 做到简洁明了
let name;let users;
(5)使用说明性的变量(即有意义的变量名)
NO:长代码不知道啥意思
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
address.match(cityZipCodeRegex)[1], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码
address.match(cityZipCodeRegex)[2], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码
);
YES:用变量名来解释长代码的含义
const address = 'One Infinite Loop, Cupertino 95014';const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;const [, city, zipCode] = address.match(cityZipCodeRegex) || [];saveCityZipCode(city, zipCode);
(6)避免使用太多的全局变量
NO:在不同的文件不停的定义全局变量
name.jswindow.name = 'a';hello.jswindow.name = 'b';time.jswindow.name = 'c'; //三个文件的先后加载顺序不同,都会使得window.name的值不同,同时,你对window.name的修改了都有可能不生效,因为你不知道你修改过之后别人是不是又在别的说明文件中对其的值重置了。所以随着文件的增多,会导致一团乱麻。
YES:少用或使用替代方案 你可以选择只用局部变量。通过(){}的方法。
如果你确实用很多的全局变量需要共享,你可以使用vuex,redux或者你自己参考flux模式写一个也行。
(7) 变量的赋值。
NO:对于求值获取的变量,没有兜底。
const MIN_NAME_LENGTH = 8;let lastName = fullName[1];if(lastName.length > MIN_NAME_LENGTH) { // 这样你就给你的代码成功的埋了一个坑,你有考虑过如果fullName = ['jackie']这样的情况吗?这样程序一跑起来就爆炸。要不你试试。....}
YES:对于求值变量,做好兜底。
const MIN_NAME_LENGTH = 8;
let lastName = fullName[1] || ''; // 做好兜底,fullName[1]中取不到的时候,不至于赋值个undefined,至少还有个空字符,从根本上讲,lastName的变量类型还是String,String原型链上的特性都能使用,不会报错。不会变成undefined。
if(lastName.length > MIN_NAME_LENGTH) {
....
}
其实在项目中有很多求值变量,对于每个求值变量都需要做好兜底。
let propertyValue = Object.attr || 0; // 因为Object.attr有可能为空,所以需要兜底。
但是,赋值变量就不需要兜底了。
let a = 2; // 因为有底了,所以不要兜着。
let myName = 'Tiny'; // 因为有底了,所以不要兜着。
二、函数相关
(1)函数命名
NO:从命名无法知道返回值类型
function showFriendsList() {....} // 现在问,你知道这个返回的是一个数组,还是一个对象,还是true or false。你能答的上来否?你能答得上来我请你吃松鹤楼的满汉全席还请你不要当真。
Yes: 对于返回true or false的函数,最好以should/is/can/has开头
function shouldShowFriendsList() {...}function isEmpty() {...}function canCreateDocuments() {...}function hasLicense() {...}
(2) 功能函数最好为纯函数
NO: 不要让功能函数的输出变化无常。
function plusAbc(a, b, c) { // 这个函数的输出将变化无常,因为api返回的值一旦改变,同样输入函数的a,b,c的值,但函数返回的结果却不一定相同。var c = fetch('../api');return a+b+c;}
YES:功能函数使用纯函数,输入一致,输出结果永远唯一
function plusAbc(a, b, c) { // 同样输入函数的a,b,c的值,但函数返回的结果永远相同。return a+b+c;}
(3)函数传参
NO:传参无说明
page.getSVG(api, true, false); // true和false啥意思,一目不了然
YES: 传参有说明
page.getSVG({imageApi: api,includePageBackground: true, // 一目了然,知道这些true和false是啥意思compress: false,})
(4)动作函数要以动词开头
NO: 无法辨别函数意图
function emlU(user) {....}
YES:动词开头,函数意图就很明显
function sendEmailToUser(user) {....}
(5)一个函数完成一个独立的功能,不要一个函数混杂多个功能
这是软件工程中最重要的一条规则,当函数需要做更多的事情时,它们将会更难进行编写、测试、理解和组合。当你能将一个函数抽离出只完成一个动作,他们将能够很容易的进行重构并且你的代码将会更容易阅读。如果你严格遵守本条规则,你将会领先于许多开发者。
NO:函数功能混乱,一个函数包含多个功能。最后就像能以一当百的老师傅一样,被乱拳打死(乱拳(功能复杂函数)打死老师傅(老程序员))
function sendEmailToClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client)
if (clientRecord.isActive()) {
email(client)
}
})
}
YES: 功能拆解,
function sendEmailToActiveClients(clients) { //各个击破,易于维护和复用
clients.filter(isActiveClient).forEach(email)
}
function isActiveClient(client) {
const clientRecord = database.lookup(client)
return clientRecord.isActive()
}
(6)优先使用函数式编程
NO: 使用for循环编程
for(i = 1; i <= 10; i++) { // 一看到for循环让人顿生不想看的情愫a[i] = a[i] +1;}
YES:使用函数式编程
let b = a.map(item => ++item) // 怎么样,是不是很好理解,就是把a的值每项加一赋值给b就可以了。现在在javascript中几乎所有的for循环都可以被map,filter,find,some,any,forEach等函数式编程取代。
(7) 函数中过多的采用if else ..
No: if else过多
if (a === 1) {
...
} else if (a ===2) {
...
} else if (a === 3) {
...
} else {
...
}
YES: 可以使用switch替代或用数组替代
switch(a) {
case 1:
....
case 2:
....
case 3:
....
default:
....
}
Or
let handler = {
1: () => {....},
2: () => {....}.
3: () => {....},
default: () => {....}
}
handler[a]() || handler['default']()
三、尽量使用ES6,有可以能的话ES7中新语法(只罗列最常用的新语法,说实话,有些新语法不怎么常用)
(1)尽量使用箭头函数
NO:采用传统函数
function foo() {// code}
YES:使用箭头函数
let foo = () => {// code}
(2)连接字符串
NO:采用传统+号
var message = 'Hello ' + name + ', it\'s ' + time + ' now'
YES:采用模板字符
var message = `Hello ${name}, it's ${time} now`
(3) 使用解构赋值
NO:使用传统赋值:
var data = { name: 'dys', age: 1 };
var name = data.name;
var age = data.age;
var fullName = ['jackie', 'willen'];
var firstName = fullName[0];
var lastName = fullName[1];
YES:使用结构赋值:
const data = {name:'dys', age:1};
const {name, age} = data; // 怎么样,是不是简单明了
var fullName = ['jackie', 'willen'];
const [firstName, lastName] = fullName;
(4) 尽量使用类class
NO: 采用传统的函数原型链实现继承
典型的 ES5 的类(function)在继承、构造和方法定义方面可读性较差,当需要继承时,优先选用 class。代码太多,就省略了。
YES:采用ES6类实现继承
class Animal {
constructor(age) {
this.age = age
}
move() {
/* ... */
}
}
class Mammal extends Animal {
constructor(age, furColor) {
super(age)
this.furColor = furColor
}
liveBirth() {
/* ... */
}
}
class Human extends Mammal {
constructor(age, furColor, languageSpoken) {
super(age, furColor)
this.languageSpoken = languageSpoken
}
speak() {
/* ... */
}
}先写到这了,这是目前为止发现的问题,这篇文章中并没有完全覆盖到常见的写代码的不好的习惯,所以你如果觉的有需要补充的,都可以在文章下方评论,或者直接到我的Github的这篇文章中评论。对于有用的,都将补充到我的掘金和Github中去。同时,你如果觉的文章写得还可以,Please在我的Github中送上你宝贵的Star,你的Star是我继续写文章最大的动力。
注:除了上述这些人为习惯之外,就像前面提到的,对于机械性的,你可以使用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 /转的更多相关文章
- 教你动手做一个 iOS 越狱 app
前言 俗话说得好, 万事开头难. 仅仅是上图一个如此简单地不能再简单的小app, 其实都不算是app, 只是注入了一段代码进系统中, 等到特定的函数方法调用的时候就会被我们hook掉, 执行我们写的代 ...
- 论好的代码习惯的养成/做一个优雅的coder
1.先说一下以前被滴滴大佬教育的事情: 以前写代码的时候,因为只需要取特定的几个字段,所以经常这么写 //Request $request for example $parameters = $req ...
- 3分钟教你做一个iphone手机浏览器
3分钟教你做一个iphone手机浏览器 第一步:新建一个Single View工程: 第二步:新建好工程,关闭arc. 第三步:拖放一个Text Field 一个UIButton 和一个 UIWebV ...
- R数据分析:跟随top期刊手把手教你做一个临床预测模型
临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...
- 【酷Q插件制作】教大家做一个简单的签到插件
酷Q插件已经有很多了,社区分享一大堆,不过还是自己写才有乐趣,哈哈.不得不吐槽一下,酷Q竟然不更新了,出了个酷Q pro,还收费!!诶.不过这也影响不了咱写插件的心情,今天教大家写一个酷Q签到插件,虽 ...
- TTS-零基础入门-10分钟教你做一个语音功能
在本片博客正式開始之前,大家先跟我做一个简单的好玩的 小语音. 新建一个文本文档,然后再文档里输入这样 一句话 CreateObject("SAPI.SpVoice").Spea ...
- Vue+ElementUI: 手把手教你做一个audio组件
目的 本项目的目的是教你如何实现一个简单的音乐播放器(这并不难) 本项目并不是一个可以用于生产环境的element播放器,所以并没有考虑太多的兼容性问题 本项目不是ElementUI的一个音频插件,只 ...
- 用RecyclerView做一个小清新的Gallery效果 - Ryan Lee的博客
一.简介 RecyclerView现在已经是越来越强大,且不说已经被大家用到滚瓜烂熟的代替ListView的基础功能,现在RecyclerView还可以取代ViewPager实现Banner效果,当然 ...
- fir.im Weekly - 如何做一个出色的程序员
做一个出色的程序员,困难而高尚.本期 fir.im Weekly 精选了一些实用的 iOS,Android 开发工具和源码分享,还有一些关于程序员的成长 Tips 和有意思有质量的线下活动~ How ...
随机推荐
- 【LeetCode】124. Binary Tree Maximum Path Sum
Binary Tree Maximum Path Sum Given a binary tree, find the maximum path sum. The path may start and ...
- Hadoop命令手册
原文地址:http://hadoop.apache.org/docs/r1.0.4/cn/commands_manual.html 概述 常规选项 用户命令 archive distcp fs fsc ...
- pythonl练习笔记——PythonNet 套接字socket
1 套接字socket 1.1 套接字概述 套接字,一种网络通讯工具:用于进行网络间的通信,是一种特殊文件类型, 套接字,是一个通信链的句柄,用于描述IP地址和端口,实现向网络发出请求或应答网络请求. ...
- Jmeter -----计数器(counter)
计数器的定义 Allows the user to create a counter that can be referenced anywhere in the Thread Group. The ...
- Linux中断 - tasklet
一.前言 对于中断处理而言,linux将其分成了两个部分,一个叫做中断handler(top half),属于不那么紧急需要处理的事情被推迟执行,我们称之deferable task,或者叫做bott ...
- python标准库介绍——25 errno 模块详解
==errno 模块== ``errno`` 模块定义了许多的符号错误码, 比如 ``ENOENT`` ("没有该目录入口") 以及 ``EPERM`` ("权限被拒绝& ...
- ubuntu 16.04 apt-get 更新使用中科大镜像源
1 备份系统配置 sudo cp /etc/apt/sources.list /etc/apt/source.list.bak 2 编辑配置 sudo vi /etc/apt/sources.list ...
- 关于K8s集群器日志收集的总结
本文介绍了kubernetes官方提供的日志收集方法,并介绍了Fluentd日志收集器并与其他产品做了比较.最后介绍了好雨云帮如何对k8s进行改造并使用ZeroMQ以消息的形式将日志传输到统一的日志处 ...
- angular.js ng-repeat动态插入删除dom节点
既然上面提到 angular.js 下无需用户直接操作dom ,而是在编译间断 dom 与 控制层model 实现了双向绑定,一方做出改变,另一方就会立即改变,那问题来了,我想插入一个文本框和按钮,并 ...
- Apache2.2配置小结.
lamp 1.编译安装 2,日志轮循 3,优化 4,排错 4,1 vhost :NameServer 4,2 403:虚拟主机,给目录在主配置文件里添加配置 4.3 首页文件 初始化安装完成后,如果不 ...