codereview之前端优化代码

4次阅读

共计 6759 个字符,预计需要花费 17 分钟才能阅读完成。

所谓无规矩不成方圆,前端工夫在团队 code-review 中发现,不同期间不同开发人员写的代码堪称形形色色。因而咱们提出了一些相干代码方面的标准,心愿日后能造成团队的编码标准。

本文次要针对一些 JavaScript 进行优化,使之更加强壮,可读性更强,更以保护。

gitthub 地址:github.com/Michael-lzg…

上一篇:code-review 之前端代码标准

if 判断的优化

JavaScript 条件语句在咱们平时的开发中是不可避免要用到的,然而很多时候咱们的代码写的并不好,一连串的 if-else 或者多重嵌套判断都会使得代码很臃肿,上面举例进行优化。

需要:当初有 4 个产品,别离是手机、电脑、电视机、游戏机,当然每个产品显示的价格不一样。

1、最简略的办法:if 判断

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

毛病:代码太长了, 保护和浏览都很不敌对

2、好一点的办法: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

3、更优的办法: 策略模式

策略模式利用组合、委托和多态等技术和思维,能够无效地防止多重条件抉择语句。它提供了对凋谢—关闭准则的完满反对,将算法封装在独立的 strategy 中,使得它们易于切换,易于了解,易于扩大。

const commodity = new Map([['phone', 1999],
  ['computer', 9999],
  ['television', 2999],
  ['gameBoy', 3999],
])

const price = (name) => {return commodity.get(name)
}
price('phone') // 1999

includes 的优化

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 '你的身份不非法'}
}

for 循环

在 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-offor-in 循环遍历数组更好。for-of 只有具备 Iterator 接口的数据结构,都能够应用它迭代成员。它间接读取的是键值。for-in 须要穷举对象的所有属性,包含自定义的增加的属性也能遍历到。且 for-inkeyString 类型,有转换过程,开销比拟大。

所以在开发过程中循环数组尽量避免应用 for-in

数组去重

数组去重是理论开发解决数据中常常遇到的,办法有很多,这里就不一一例举了。

1、最传统的办法:利用数组的 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]

2、优化:利用 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 的创立

创立多个 dom 元素时,先将元素 appendDocumentFragment 中,最初对立将 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)。当内存占用越来越高,轻则影响零碎性能,重则导致过程解体。

引起内存透露的起因

全局变量

1、未声明变量或者应用 this 创立的变量 (this 的指向是 window) 都会引起内存透露

function fn() {a = "Actually, I'm a global variable"}
fn()

function fn() {this.a = "Actually, I'm a global variable"}
fn()

解决办法:

  • 防止创立全局变量
  • 应用严格模式, 在 JavaScript 文件头部或者函数的顶部加上 use strict

2、在 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()}

防抖与节流

在前端开发的过程中,咱们常常会须要绑定一些继续触发的事件,如 resizescrollmousemove 等等,但有些时候咱们并不心愿在事件继续触发的过程中那么频繁地去执行函数。这时候就用到防抖与节流。

案例 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

默认状况下,浏览器是同步加载 js 脚本,解析 html 过程中,遇到 <script> 标签就会停下来,等脚本下载、解析、执行完后,再持续向下解析渲染。

如果 js 文件体积比拟大,下载工夫就会很长,容易造成浏览器梗塞,浏览器页面会呈现出“白屏”成果,用户会感觉浏览器“卡死了”,没有响应。此时,咱们能够让 js 脚本异步加载、执行。

<script src="path/to/home.js" defer></script>
<script src="path/to/home.js" async></script>

下面代码中,<script> 标签别离有 deferasync 属性,浏览器辨认到这 2 个属性时 js 就会异步加载。也就是说,浏览器不会期待这个脚本下载、执行结束后再向后执行,而是间接持续向后执行

defer 与 async 区别:

  • defer:DOM 构造齐全生成,以及其余脚本执行实现,才会执行(渲染完再执行)。有多个 defer 脚本时,会依照页面呈现的程序顺次加载、执行。
  • async:一旦下载实现,渲染引擎就会中断渲染,执行这个脚本当前,再持续渲染(下载完就执行)。有多个 async 脚本时,不能保障依照页面呈现程序加载、执行

举荐文章

总结 18 个 webpack 插件,总会有你想要的!
搭建一个 vue-cli4+webpack 挪动端框架(开箱即用)
从零构建到优化一个相似 vue-cli 的脚手架
封装一个 toast 和 dialog 组件并公布到 npm
从零开始构建一个 webpack 我的项目
总结几个 webpack 打包优化的办法
总结 vue 常识体系之高级利用篇
总结 vue 常识体系之实用技巧
总结 vue 常识体系之根底入门篇
总结挪动端 H5 开发罕用技巧(干货满满哦!)

正文完
 0