如何写出漂亮的Js代码
我觉得写好代码和作文章差不多,无外乎:工整、优雅、拒绝重复、惜字如金。对代码有感情,每一行都应该尽心尽力,并且还要有把所有代码扔垃圾篓之后再重写两遍的冲动,一旦有了这种冲动之后,什么都挡不住你,连吃喝拉撒时,问题都会浮现到你脑子里,你就会不由自主地解决它们……
网上有不少关于 JS 编写优化建议,这里我根据自己的经验提出一些比较有用的建议。
1. 按强类型风格写代码
JS是弱类型的,但是写代码的时候不能太随意,写得太随意也体现了编码风格不好。下面分点说明:
var num, str, obj;var num =0, str ='', obj =null;定义变量的时候就给他一个默认值,这样不仅方便了解释器,也方便了阅读代码的人,他会在心里有数——知道这些变量可能会当作什么用。
(2)不要随意地改变变量的类型,例如下面代码:
var num =5;num ="-"+ num;var num =5;var sign ="-"+ num;function getPrice(count){ if(count <0)return""; elsereturn count *100;}function getPrice(count){ if(count <0)return-1; elsereturn count *100;}然后告诉使用者,如果返回-1就表示不合法。如果类型确定,解释器也不用去做一些额外的工作,可以加快运行速度。
2. 减少作用域查找
(1)不要让代码暴露在全局作用域下
例如以下运行在全局作用域的代码:
<script> var map = document.querySelector("#my-map"); map.style.height ="600px";</script><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();}function getResult(count){ count++; function process(count){ var factor =2; return count * factor -5; } return process(count);}var url ="";if(window.location.protocal ==="https:"){ url ="wss://xxx.com"+ window.location.pathname + window.location.search;}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){
}if(a == b){
}Expected ‘===’ and instead saw ‘==’.
null==undefined //true'' == '0' //false0 == '' //true0 == '0' //true'
' == 0 //truenew String("abc") == "abc" //truenew Boolean(true) == true //truetrue == 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;}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:
chatService.sendMessage(localMsgId++, msgContent);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(()=> 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;};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]();}var tasks =[];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]();}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等,也是可以帮助写出简洁高效的代码。
总结
以上列了我自己在实际写代码过程中遇到的一些问题和一些个人认为比较重要的方面,其它的还有变量命名、缩进、注释等,这里就不提及了。写代码的风格也体现了编程的素养,有些人的代码看起来非常地干净利落,而有些人的代码看起来让人比较痛苦。这种编程素质的提升需要有意识地去做一些改进,有些人虽然代码写得很烂,但是他自己并不觉得有什么问题。这就需要多去学下别人的代码,甚至学一下其它语言的书写,两者一比较就能发现差异,或者看下这方面的书,像什么代码大全之类的。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!