如何让你的 JS 写得更漂亮?

更新日期: 2019-09-17阅读: 2k标签: 优化

网上有不少关于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 "";
   else return count * 100;
}

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

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

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

2. 减少作用域查找

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

<script>
   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 

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

可以改成:

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

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

function getPrice(e){return 0>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)自增 

chatService.sendMessage(localMsgId++, msgContent);


5. 减少魔数

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)使用箭头函数取代小函数 

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 

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

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

class Person{
   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)块级作用域变量 

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了。那怎么办呢?可以这样解决:

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]();
}

把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等,也是可以帮助写出简洁高效的代码。

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

作者:会编程的银猪
http://www.renfed.com/2017/04/29/effective-js-optimize/

链接: https://www.fly63.com/article/detial/5348

js中for循环优化总结_如何提高程序的执行效率

在程序开发中,经常会使用到for循环的,但是很多人写的for循环效率都是比较低的,下面就举例说明,并总结优化for循环的方法,来提高我们程序的执行效率。

网站打开速度优化_如何提高网页访问速度技巧方法总结

网站的加载速度不仅影响着用户体验,也会影响搜索引擎的排名,在百度推出“闪电算法”以来,将网站首屏打开速度被列入优化排名行列,作为前端开发的我们需要如果来优化网站的打开速度呢?下面就整理挖掘出很多细节上可以提升性能的东西分享给大家

JS性能优化之文档片段createDocumentFragment

DocumentFragments是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中

深入浅出代码优化﹣if/else

对于代码裡面的 if else,我们可以使用逻辑判断式,或更好的三元判断式来优化代码。除了可以降低维护项目的成本之外,还可以提升代码可读性。就让我们从最简单的 if else 例子开始吧。

微信小程序性能优化入门指南

小程序从发布到现在也已经有将近两年的时间,越来越来多的公司开始重视小程序生态带来的流量,今年也由于小程序平台对外能力的越来越多的开放以及小程序平台的自身优化,越来越多的开发者也自主的投入到小程序的开发当中

网络串流播放_HTML5如何优化视频文件以便在网络上更快地串流播放

无论你正在将 GIF 动图转换为 MP4 视频,还是手头已经有一大堆 MP4 视频,你都可以优化文件结构,以使得这些视频更快地加载和播放。通过重组 atoms 将 moov 放到文件开头,浏览器可以避免发送额外的 HTTP range request 请求来搜寻和定位 moovatom

​web项目优化_Web 服务器性能与站点访问性能优化

要优化 Web 服务器的性能,我们先来看看 Web 服务器在 web 页面处理上的步骤:Web 浏览器向一个特定的服务器发出 Web 页面请求; Web 服务器接收到 web 页面请求后,寻找所请求的 web 页面,并将所请求的 Web 页面传送给 Web 浏览器; 显示出来

前端性能优化之重排和重绘

浏览器下载完页面所有的资源后,就要开始构建DOM树,于此同时还会构建渲染树(Render Tree)。(其实在构建渲染树之前,和DOM树同期会构建Style Tree。DOM树与Style Tree合并为渲染树)

微信小程序代码优化总汇

写篇文章的目的,是以开放小程序代码的层面的优化。包括:条件判断将wx:if换成了hidden 、页面跳转请销毁之前使用的资源、列表的局部更新、小程序中多张图片懒加载方案、Input状态下隐藏input,应预留出键盘收起的时间

我是如何将页面加载时间从6S降到2S的?

生活在信息爆炸的今天,我们每天不得不面对和过滤海量的信息--无疑是焦躁和浮动的,这就意味着用户对你站点投入的时间可能是及其吝啬的(当然有一些刚需站点除外)。如何给用户提供迅速的响应就显得十分重要了

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!