所谓无规矩不成方圆,前端时间在团队 code-review 中发现,不一样时期不一样开发人员写的代码可谓五花八门。所以咱们提出了一些相关代码方面的规范,但愿往后能造成团队的编码规范。html
本文主要针对一些 JavaScript 进行优化,使之更加健壮,可读性更强,更以维护。前端
gitthub地址:github.com/Michael-lzg…vue
上一篇:code-review以前端代码规范node
JavaScript 条件语句在咱们平时的开发中是不可避免要用到的,可是不少时候咱们的代码写的并很差,一连串的 if-else
或者多重嵌套判断都会使得代码很臃肿,下面举例进行优化。webpack
需求:如今有 4 个产品,分别是手机、电脑、电视机、游戏机,固然每一个产品显示的价格不同。git
一、最简单的方法:if 判断github
let commodity = { phone: '手机', computer: '电脑', television: '电视', gameBoy: '游戏机', } function price(name) { if (name === commodity.phone) { console.log(1999) } else if (name === commodity.computer) { console.log(9999) } else if (name === commodity.television) { console.log(2999) } else if (name === commodity.gameBoy) { console.log(3999) } } price('手机') // 9999
缺点:代码太长了,维护和阅读都很不友好web
二、好一点的方法:Switch
算法
let commodity = { phone: '手机', computer: '电脑', television: '电视', gameBoy: '游戏机', } const price = (name) => { switch (name) { case commodity.phone: console.log(1999) break case commodity.computer: console.log(9999) break case commodity.television: console.log(2999) break case commodity.gameBoy: console.log(3999) break } } price('手机') // 9999
三、更优的方法: 策略模式vue-cli
策略模式利用组合、委托和多态等技术和思想,能够有效地避免多重条件选择语句。它提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。
const commodity = new Map([ ['phone', 1999], ['computer', 9999], ['television', 2999], ['gameBoy', 3999], ]) const price = (name) => { return commodity.get(name) } price('phone') // 1999
includes
是 ES7 新增的 API,与 indexOf
不一样的是 includes
直接返回的是 Boolean
值,indexOf
则 返回的索引值, 数组和字符串都有 includes
方法。
需求:咱们来实现一个身份认证方法,经过传入身份 Id 返回对应的验证结果
传统方法
function verifyIdentity(identityId) { if (identityId == 1 || identityId == 2 || identityId == 3 || identityId == 4) { return '你的身份合法,请通行!' } else { return '你的身份不合法' } }
includes
优化
function verifyIdentity(identityId) { if ([1, 2, 3, 4].includes(identityId)) { return '你的身份合法,请通行!' } else { return '你的身份不合法' } }
在 JavaScript 中,咱们可使用 for()
, while()
, for(in)
,for(in)
几种循环,事实上,这三种循环中 for(in)
的效率极差,由于他须要查询散列键,因此应该尽可能少用。
for 循环是最传统的语句,它以变量 i 做为索引,以跟踪访问的位置,对数组进行操做。
var arr = ['a', 'b', 'c'] for (var i = 0; i < arr.length; i++) { console.log(arr[i]) //结果依次a,b,c }
以上的方法有一个问题:就是当数组的长度到达百万级时,arr.length
就要计算一百万次,这是至关耗性能的。因此能够采用如下方法就行改良。
var arr = ['a', 'b', 'c'] for (var i = 0, length = arr.length; i < length; i++) { console.log(arr[i]) //结果依次a,b,c }
此时 arr.length
只须要计算一次,优化了性能。
for-in
通常用来来遍历对象的属性的,不过属性须要 enumerable
(可枚举)才能被读取到。同时 for-in
也能够遍历数组,遍历数组的时候遍历的是数组的下标值。
var obj = { 0: 'a', 1: 'b', 2: 'c' } for (var key in obj) { console.log(key) //结果为依次为0,1,2 } var arr = ['a', 'b', 'c'] for (var key in a) { console.log(key) //结果为依次为0,1,2 }
for-of
语句看着有点像 for-in
语句,可是和 for-of
语句不一样的是它不能够循环对象,只能循环数组。
var arr = ['a', 'b', 'c'] for (var value of arr) { console.log(value) // 结果依次为a,b,c }
for-of
比 for-in
循环遍历数组更好。for-of
只要具备 Iterator
接口的数据结构,均可以使用它迭代成员。它直接读取的是键值。for-in
须要穷举对象的全部属性,包括自定义的添加的属性也能遍历到。且 for-in
的 key
是 String
类型,有转换过程,开销比较大。
因此在开发过程当中循环数组尽可能避免使用 for-in
。
数组去重是实际开发处理数据中常常遇到的,方法有不少,这里就不一一例举了。
一、最传统的方法:利用数组的 indexOf
下标属性来查询。
function unique4(arr) { var newArr = [] for (var i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i]) === -1) { newArr.push(arr[i]) } } return newArr } console.log(unique4([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])) // [1, 2, 3, 5, 6, 7, 4]
二、优化:利用 ES6 的 Set
方法。
Set
自己是一个构造函数,用来生成 Set
数据结构。Set
函数能够接受一个数组(或者具备 iterable 接口的其余数据结构)做为参数,用来初始化。Set
对象容许你存储任何类型的值,不管是原始值或者是对象引用。它相似于数组,可是成员的值都是惟一的,没有重复的值。
function unique4(arr) { return Array.from(new Set(arr)) // 利用Array.from将Set结构转换成数组 } console.log(unique4([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])) // [1, 2, 3, 5, 6, 7, 4]
箭头函数表达式的语法比函数表达式更简洁。因此在开发中更推荐使用箭头函数。特别是在 vue
项目中,使用箭头函数不须要在更 this
从新赋一个变量。
// 使用functions var arr = [5, 3, 2, 9, 1] var arrFunc = arr.map(function (x) { return x * x }) console.log(arrFunc) // 使用箭头函数 var arr = [5, 3, 2, 9, 1] var arrFunc = arr.map((x) => x * x)
要注意的是,箭头函数不绑定 arguments
,取而代之用 rest
参数…解决。
// 不能使用 arguments let fun1 = (b) => { console.log(arguments) } fun1(2, 92, 32, 32) // Uncaught ReferenceError: arguments is not defined // 使用rest 参数 let fun2 = (...c) => { console.log(c) } fun2(3, 82, 32, 11323) // [3, 82, 32, 11323]
建立多个 dom 元素时,先将元素 append
到 DocumentFragment
中,最后统一将 DocumentFragment
添加到页面。
常规方法;
for (var i = 0; i < 1000; i++) { var el = document.createElement('p') el.innerHTML = i document.body.appendChild(el) }
使用 DocumentFragment
优化屡次 append
var frag = document.createDocumentFragment() for (var i = 0; i < 1000; i++) { var el = document.createElement('p') el.innerHTML = i frag.appendChild(el) } document.body.appendChild(frag)
更优的方法:使用一次 innerHTML
赋值代替构建 dom 元素
var html = [] for (var i = 0; i < 1000; i++) { html.push('<p>' + i + '</p>') } document.body.innerHTML = html.join('')
系统进程再也不用到的内存,没有及时释放,就叫作内存泄漏(memory leak)。当内存占用愈来愈高,轻则影响系统性能,重则致使进程崩溃。
引发内存泄漏的缘由
一、未声明变量或者使用 this
建立的变量(this
的指向是 window
)都会引发内存泄漏
function fn() { a = "Actually, I'm a global variable" } fn() function fn() { this.a = "Actually, I'm a global variable" } fn()
解决方法:
use strict
。二、在 vue 单页面应用,声明的全局变量在切换页面的时候没有清空
<template> <div id="home"> 这里是首页 </div> </template> <script> export default { mounted() { window.test = { // 此处在全局window对象中引用了本页面的dom对象 name: 'home', node: document.getElementById('home') } } } </script>
解决方案: 在页面卸载的时候顺便处理掉该引用。
destroyed () { window.test = null // 页面卸载的时候解除引用 }
闭包引发的内存泄漏缘由:闭包能够维持函数内局部变量,使其得不到释放。
function fn() { var a = "I'm a" return function () { console.log(a) } }
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对 dom 的引用。
因为项目中有些页面不免会碰到须要定时器或者事件监听。可是在离开当前页面的时候,定时器若是不及时合理地清除,会形成业务逻辑混乱甚至应用卡死的状况,这个时就须要清除定时器事件监听,即在页面卸载(关闭)的生命周期函数里,清除定时器。
methods:{ resizeFun () { this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128 }, setTimer() { this.timer = setInterval(() => { }) }, clearTimer() {//清除定时器 clearInterval(this.timer) this.timer = null } }, mounted() { this.setTimer() window.addEventListener('resize', this.resizeFun) }, beforeDestroy() { window.removeEventListener('resize', this.resizeFun) this.clearTimer() }
在前端开发的过程当中,咱们常常会须要绑定一些持续触发的事件,如 resize
、scroll
、mousemove
等等,但有些时候咱们并不但愿在事件持续触发的过程当中那么频繁地去执行函数。这时候就用到防抖与节流。
案例 1:远程搜索时须要经过接口动态的获取数据,如果每次用户输入都接口请求,是浪费带宽和性能的。
<Select :remote-method="remoteMethod"> <Option v-for="item in temoteList" :value="item.value" :key="item.id">{{item.label}}</Option> </Select> <script> function debounce(fn, wait) { let timeout = null return function () { if (timeout !== null) clearTimeout(timeout) timeout = setTimeout(fn, wait) } } export default { methods:{ remoteMethod:debounce(function (query) { // to do ... }, 200), } } <script>
案例 2:持续触发 scroll
事件时,并不当即执行 handle
函数,当 1000 毫秒内没有触发 scroll
事件时,才会延时触发一次 handle
函数。
function debounce(fn, wait) { let timeout = null return function () { if (timeout !== null) clearTimeout(timeout) timeout = setTimeout(fn, wait) } } function handle() { console.log(Math.random()) } window.addEventListener('scroll', debounce(handle, 1000))
默认状况下,浏览器是同步加载 js 脚本,解析 html 过程当中,遇到 <script>
标签就会停下来,等脚本下载、解析、执行完后,再继续向下解析渲染。
若是 js 文件体积比较大,下载时间就会很长,容易形成浏览器堵塞,浏览器页面会呈现出“白屏”效果,用户会感受浏览器“卡死了”,没有响应。此时,咱们可让 js 脚本异步加载、执行。
<script src="path/to/home.js" defer></script> <script src="path/to/home.js" async></script>
上面代码中,<script>
标签分别有 defer
和 async
属性,浏览器识别到这 2 个属性时 js 就会异步加载。也就是说,浏览器不会等待这个脚本下载、执行完毕后再向后执行,而是直接继续向后执行
defer 与 async 区别:
defer
脚本时,会按照页面出现的顺序依次加载、执行。async
脚本时,不能保证按照页面出现顺序加载、执行总结18个webpack插件,总会有你想要的!
搭建一个 vue-cli4+webpack 移动端框架(开箱即用)
从零构建到优化一个相似vue-cli的脚手架
封装一个toast和dialog组件并发布到npm
从零开始构建一个webpack项目
总结几个webpack打包优化的方法
总结vue知识体系之高级应用篇
总结vue知识体系之实用技巧
总结vue知识体系之基础入门篇
总结移动端H5开发经常使用技巧(干货满满哦!)