让同事赞不绝口的代码原来长这样啊

2023-10-14 07:59

本文主要是介绍让同事赞不绝口的代码原来长这样啊,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文/会编程的银猪

阅读本文需要 3分钟

网上有不少关于 JS 编写优化建议,这里我根据自己的经验提出一些比较有用的意见。

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 "";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 ‘==’.
if(a == b){

(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;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)自增
利用自增也可以简化代码。如下,每发出一条消息,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 里面经常会遇到只要执行一行代码就好了,写个 function 总感觉有点麻烦,用字符串的方式又不太好,所以这种情况用箭头函数也很方便:

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

箭头函数在 C++/Java 等其它语言里面叫做 Lambda 表达式,Ruby 比较早就有这种语法形式了,后来 C++/Java 也实现了这种语法。当然箭头函数或者 Lambda 表达式不仅适用于这种一行的,多行代码也可以,不过在一行的时候它的优点才比较明显。

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

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)块级作用域变量
块级作用域变量也是 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 了。那怎么办呢?可以这样解决:

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.chinasem.cn/article/209167

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时