感觉这篇文章总结的js的规范写法不错,拿来收藏。转自:https://mp.weixin.qq.com/s/AtR94IL9BW9EXOTnKOilmA

1. 按强类型风格写代码

JS是弱类型的,但是写代码的时候不能太随意,写得太随意也体现了编码风格不好。下面分点说明:

(1)定义变量的时候要指明类型,告诉JS解释器这个变量是什么数据类型的,而不要让解释器去猜,例如不好的写法:
var num,
str,
obj;

声明了三个变量,但其实没什么用,因为解释器不知道它们是什么类型的,好的写法应该是这样的:

var num =0,
str ='',
obj =null;

定义变量的时候就给他一个默认值,这样不仅方便了解释器,也方便了阅读代码的人,他会在心里有数——知道这些变量可能会当作什么用。

(2)不要随意地改变变量的类型,例如下面代码:
var num =5;
num ="-"+ num;

第1行它是一个整型,第2行它变成了一个字符串。因为JS最终都会被解释成汇编的语言,汇编语言变量的类型肯定是要确定的,你把一个整型的改成了字符串,那解释器就得做一些额外的处理。并且这种编码风格是不提倡的,有一个变量第1行是一个整型,第10行变成了一个字符串,第20行又变成了一个object,这样就让阅读代码的人比较困惑,上面明明是一个整数,怎么突然又变成一个字符串了。好的写法应该是再定义一个字符串的变量:

var num =5;
var sign ="-"+ num;
(3)函数的返回类型应该是要确定的,例如下面不确定的写法:
function getPrice(count){
if(count <0)return"";
elsereturn count *100;
}

getPrice这个函数有可能返回一个整数,也有可能返回一个空的字符串。这样写也不太好,虽然它是符合JS语法的,但这种编码风格是不好的。使用你这个函数的人会有点无所适从,不敢直接进行加减乘除,因为如果返回字符串进行运算的话值就是NaN了。函数的返回类型应该是要确定的,如下面是返回整型:

function getPrice(count){
if(count <0)return-1;
elsereturn count *100;
}

然后告诉使用者,如果返回-1就表示不合法。如果类型确定,解释器也不用去做一些额外的工作,可以加快运行速度。

2. 减少作用域查找

(1)不要让代码暴露在全局作用域下

例如以下运行在全局作用域的代码:

    var map = document.querySelector("#my-map");
map.style.height ="600px";
</script>

有时候你需要在页面直接写一个script,要注意在一个script标签里面,代码的上下文都是全局作用域的,由于全局作用域比较复杂,查找比较慢。例如上面的map变量,第二行在使用的时候,需要在全局作用域查找一下这个变量,假设map是在一个循环里面使用,那可能就会有效率问题了。所以应该要把它搞成一个局部的作用域:

<script>
!function(){
var map = document.querySelector("#my-map");
map.style.height ="600px";
}()
</script>

上面用了一个function制造一个局部作用域,也可以用ES6的块级作用域。由于map这个变量直接在当前的局部作用域命中了,所以就不用再往上一级的作用域(这里是全局作用域)查找了,而局部作用域的查找是很快的。同时直接在全局作用域定义变量,会污染window对象。

(2)不要滥用闭包

闭包的作用在于可以让子级作用域使用它父级作用域的变量,同时这些变量在不同的闭包是不可见的。这样就导致了在查找某个变量的时候,如果当前作用域找不到,就得往它的父级作用域查找,一级一级地往上直到找到了,或者到了全局作用域还没找到。因此如果闭包嵌套得越深,那么变量查找的时间就越长。如下:

function getResult(count){
count++;
function process(){
var factor =2;
return count * factor -5;
}
return process();
}

上面的代码定义了一个process函数,在这个函数里面count变量的查找时间要高于局部的factor变量。其实这里不太适合用闭包,可以直接把count传给process:

function getResult(count){
count++;
function process(count){
var factor =2;
return count * factor -5;
}
return process(count);
}

这样count的查找时间就和factor一样,都是在当前作用域直接命中。这个就启示我们如果某个全局变量需要频繁地被使用的时候,可以用一个局部变量缓存一下,如下:

var url ="";
if(window.location.protocal ==="https:"){
url ="wss://xxx.com"+ window.location.pathname + window.location.search;
}

频繁地使用了window.location对象,所以可以先把它缓存一下:

var url ="";
var location = window.location;
if(location.protocal ==="https:"){
url ="wss://xxx.com"+ location.pathname + location.search;
}

搞成了一个局变变量,这样查找就会明显快于全局的查找,代码也可以写少一点。

3. 避免==的使用

这里你可能会有疑问了,有些人喜欢用,有些人喜欢用=,大家的风格不一样,你为什么要强制别人用=呢?习惯用的人,不能仅仅是因为比=少敲了一次键盘。为什么不提倡用==呢?

(1)如果你确定了变量的类型,那么就没必要使用==了,如下:
if(typeof num !="undefined"){

}
var num = parseInt(value);
if(num ==10){ }

上面的两个例子都是确定类型的,一个是字符串,一个是整数。就没必要使用了,直接用=就可以了。

(2)如果类型不确定,那么应该手动做一下类型转换,而不是让别人或者以后的你去猜这里面有类型转换,如下:
var totalPage ="5";
if(parseInt(totalPage)===1){ }
(3)使用==在JSLint检查的时候是不通过的:
if(a == b){

}

如下JSLint的输出:

Expected ‘===’ and instead saw ‘==’.
(4)并且使用==可能会出现一些奇怪的现象,这些奇怪的现象可能会给代码埋入隐患:
null==undefined          //true
'' == '0' //false
0 == '' //true
0 == '0' //true
'
' == 0 //true
new String("abc") == "abc" //true
new Boolean(true) == true //true
true == 1 //true

上面的比较在用===的时候都是false,这样才是比较合理的。例如第一点null居然会等于undefined,就特别地奇怪,因为null和undefined是两个毫无关系的值,null应该是作为初始化空值使用,而undefined是用于检验某个变量是否未定义。

这和第1点介绍强类型的思想是相通的。

4. 合并表达式

如果用1句代码就可以实现5句代码的功能,那往往1句代码的执行效率会比较高,并且可读性可能会更好

(1)用三目运算符取代简单的if-else

如上面的getPrice函数:

function getPrice(count){
if(count <0)return-1;
elsereturn count *100;
}

可以改成:

function getPrice(count){
return count <0?return-1: count *100;
}

这个比写一个if-else看起来清爽多了。当然,如果你写了if-else,压缩工具也会帮你把它改三目运算符的形式:

function getPrice(e){return0>e?-1:100*e}

(2)连等

连等是利用赋值运算表达式会返回所赋的值,并且执行顺序是从右到左的,如下:

overtime = favhouse = listingDetail ={...}

有时候你会看到有人这样写:

var age =0;
if((age =+form.age.value)>=18){
console.log("你是成年人");
}else{
consoe.log("小朋友,你还有"+(18- age)+"就成年了");
}

也是利用了赋值表达式会返回一个值,在if里面赋值的同时用它的返回值做判断,然后else里面就已经有值了。上面的+号把字符串转成了整数。

(3)自增

利用自增也可以简化代码。如下,每发出一条消息,localMsgId就自增1:

5. 减少魔数

例如,在某个文件的第800行,冒出来了一句:

dialogHandler.showQuestionNaire("seller","sell",5,true);

就会让人很困惑了,上面的四个常量分别代表什么呢,如果我不去查一个那个函数的变量说明就不能够很快地意会到这些常量分别有什么用。这些意义不明的常量就叫“魔数”。

所以最好还是给这些常量取一个名字,特别是在一些比较关键的地方。例如上面的代码可改成:

  var naireType ="seller",
dialogType ="sell",
questionsCount =5,
reloadWindow =true; naireHandler.showNaire(naireType, dialogType, questionsCount, reloadWindow);

这样意义就很明显了。

6. 使用ES6简化代码

ES6已经发展很多年了,兼容性也已经很好了。恰当地使用,可以让代码更加地简洁优雅。

(1)使用箭头函数取代小函数

有很多使用小函数的场景,如果写个function,代码起码得写3行,但是用箭头函数一行就搞定了,例如实现数组从大到小排序:

var nums =[4,8,1,9,0];
nums.sort(function(a, b){
return b - a;
});

//输出[9, 8, 4, 1, 0]

如果用箭头函数,排序只要一行就搞定了:

var nums =[4,8,1,9,0];
nums.sort(a, b => b - a);

代码看起来简洁多了,还有setTimeout里面经常会遇到只要执行一行代码就好了,写个function总感觉有点麻烦,用字符串的方式又不太好,所以这种情况用箭头函数也很方便:

setTimeout(()=> console.log("hi"),3000)

箭头函数在C++/Java等其它语言里面叫做Lambda表达式,Ruby比较早就有这种语法形式了,后来C++/Java也实现了这种语法。

当然箭头函数或者Lambda表达式不仅适用于这种一行的,多行代码也可以,不过在一行的时候它的优点才比较明显。

(2)使用ES6的class

虽然ES6的class和使用function的prototype本质上是一样的,都是用的原型。但是用class可以减少代码量,同时让代码看起来更加地高大上,使用function要写这么多:

functionPerson(name, age){
this.name = name;
this.age = age;
} Person.prototype.addAge =function(){
this.age++;
}; Person.prototype.setName =function(name){
this.name = name;
};

使用class代码看加地简洁易懂:

classPerson{
constructor(name, age){
this.name = name;
this.age = age;
}
addAge(){
this.age++;
}
setName(name){
this.name = name;
}
}

并且class还可以很方便地实现继承、静态的成员函数,就不需要自己再去通过一些技巧去实现了。

(3)字符串拼接

以前要用+号拼接:
var tpl =
'<div>'+
' <span>1</span>'+
'</div>';
现在只要用两个反引号“`”就可以了:
var tpl =
` <div>
<span>1</span>
</div>
`;
另外反引号还支持占位替换,原本你需要:
var page =5,
type = encodeURIComponet("#js");
var url ="/list?page="+ page +"&type="+ type;
现在只需要:
var url =`/list?page=${page}&type=${type}`;
就不用使用+号把字符串拆散了。

(4)块级作用域变量

块级作用域变量也是ES6的一个特色,下面的代码是一个任务队列的模型抽象:

var tasks =[];
for(var i =0; i <4; i++){
tasks.push(function(){
console.log("i is "+ i);
});
}
for(var j =0; j < tasks.length; j++){
tasks[j]();
}

但是上面代码的执行输出是4,4,4,4,并且不是想要输出:0,1,2,3,所以每个task就不能取到它的index了,这是因为闭包都是用的同一个i变量,i已经变成4了,所以执行闭包的时候就都是4了。那怎么办呢?可以这样解决:

for(var i =0; i <4; i++){
!function(k){
tasks.push(function(){
console.log("i is "+ k);
});
}(i);
}
for(var j =0; j < tasks.length; j++){
tasks[j]();
}

把i赋值给了k,由于k它是一个function的一个参数,每次执行函数的时候,肯定会实例化新的k,所以每次的k都是不同的变量,这样就输出就正常了。

但是代码看起来有点别扭,如果用ES6,只要把var改成let就可以了:

var tasks =[];
for(let i =0; i <=4; i++){
tasks.push(function(){
console.log("i is "+ i);
});
}
for(var j =0; j < tasks.length; j++){
tasks[j]();
}

只改动了3个字符就达到了目的。因为for循环里面有个大括号,大括号就是一个独立的作用域,let定义的变量在独立的作用域里面它的值也是独立的。当然即使没写大括号for循环执行也是独立的。

除了以上几点,ES6还有其它一些比较好用的功能,如Object的assign,Promise等,也是可以帮助写出简洁高效的代码。

以上列了我自己在实际写代码过程中遇到的一些问题和一些个人认为比较重要的方面,其它的还有变量命名、缩进、注释等,这里就不提及了。写代码的风格也体现了编程的素养,有些人的代码看起来非常地干净利落,而有些人的代码看起来让人比较痛苦。这种编程素质的提升需要有意识地去做一些改进,有些人虽然代码写得很烂,但是他自己并不觉得有什么问题。这就需要多去学下别人的代码,甚至学一下其它语言的书写,两者一比较就能发现差异,或者看下这方面的书,像什么代码大全之类的。

如何让你的JS代码写的更漂亮的更多相关文章

  1. 引擎渲染速度测试--我js代码写得少你不要骗我

    上一张图,很多人都看过的 地址:http://aui.github.io/artTemplate/test/test-speed.html 这个地址是在看artTemplate的时候看到的,很早都看过 ...

  2. 用AOP来让你的JS代码变得更有可维护性吧

    此文已由作者吴佳祥授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 好吧我承认这是篇任务. 最近看到个消息,ES2017已经定稿了,心想,我去,还完全没了解ES2016呢,ES ...

  3. 为什么要把js代码写到<!--//-->中

    是为了兼容,不支持js的浏览器会把其中的内容当做html注释.

  4. 用最少的JS代码写出贪吃蛇游戏---迷你版

    游戏进行页面展示 GAME  OVER 页面展示  代码如下: <!doctype html> <html>   <body>   <canvas id=&q ...

  5. Console命令详解,让调试js代码变得更简单

    Firebug是网页开发的利器,能够极大地提升工作效率. 但是,它不太容易上手.我曾经翻译过一篇<Firebug入门指南>,介绍了一些基本用法.今天,继续介绍它的高级用法. ======= ...

  6. [转] Console命令详解,让调试js代码变得更简单

    http://www.cnblogs.com/see7di/archive/2011/11/21/2257442.html Firebug是网页开发的利器,能够极大地提升工作效率. 但是,它不太容易上 ...

  7. Firebug控制台详解,让调试js代码变得更简单

    http://www.open-open.com/lib/view/open1373120100347.html Firebug是网页开发的利器,能够极大地提升工作效率. Firebug控制台详解 控 ...

  8. 原始的js代码和jquery对比

    Even a task as simple as this can be complicated without jQuery at our disposal. In plain JavaScript ...

  9. 轮播图--JS手写

    轮播图基本每个网站都会有,也有很多的JQuery插件可以用,这里是用JS代码写的. @{ Layout = null; } <!DOCTYPE html> <html> < ...

随机推荐

  1. 使用elasticsearch7.3版本在一台主机上部署多个实例组建集群

    系统:centos 7.4 x64 主机ip:192.168.0.160 软件包:elasticsearch-7.3.0-linux-x86_64.tar.gz 配置步骤 vim /etc/secur ...

  2. js 判断图片是否存在

    有的时候  虽然图片的路径是正确的 但是有可能由于某些原因  导致图裂了 或者网络加载失败   那这样的应该怎么判断呢? 如下: function isHasImg(pathImg){ var Img ...

  3. 在docker容器下利用数据卷实现在删除了mysql容器或者镜像的情况下恢复数据

    当把mysql容器销毁,在新建一个容器,进行之前的数据恢复. 因为之前建立了数据卷,那么现在就可以利用这个数据卷进行数据恢复. 使用docker volume create volume_name命令 ...

  4. JVM内存结构思维导图

  5. 剑指offer-4:变态条楼梯

    ##四.变态条楼梯 ###题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法. ###分析 也是斐波那契数列问题,根据上述的思路,可 ...

  6. 四、Vue CLI-异步请求(axios)

    一.模块的安装 npm install axios --save #--save可以不用写 如图: 二.配置main.js import axios from 'axios' Vue.prototyp ...

  7. 四、Ubuntu16.04下TestLink的部署【测试管理必备工具】

    TestLink部署和使用方法 TestLink是一个基于Web的开源测试和需求管理工具.该应用程序提供测试规范.测试计划和执行,报告,需求规范以及与知名的bug跟踪器协作. 特征 l 需求管理 - ...

  8. 如何代替set get方法

    博主刚刚看其他人的博客的时候,发现好多人还在用 生成set get方法  虽然是自动生成的 但是看起来很复杂,影响代码的可读性 那么有什么办法能代替set  get方法吗? 当然有啦!!! 只需要导入 ...

  9. 【洛谷P2647】最大收益

    题目大意 现在你面前有n个物品,编号分别为1,2,3,--,n.你可以在这当中任意选择任意多个物品.其中第i个物品有两个属性Wi和Ri,当你选择了第i个物品后,你就可以获得Wi的收益:但是,你选择该物 ...

  10. 机器学习降维--PCA

    1.原理和概念 PCA(Principal Component Analysis),即主成分分析方法,是一种使用最广泛的数据降维算法. PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特征 ...