總結(jié)分享10個JavaScript代碼優(yōu)化小tips
寫在前面
想要做到JavaScript的代碼優(yōu)化,首先需要做的是準確的測試JavaScript的代碼執(zhí)行時間。其實需要做的就是采集大量的執(zhí)行樣本進行數(shù)學統(tǒng)計和分析,這里我們使用的是benchmark.js來檢測代碼的執(zhí)行情況。
首先我們需要在項目中安裝依賴,代碼如下:
yarn add benchmark --save # 或者 npm i benchmark --save
然后我們寫一個測試代碼,如下所示:
const Benchmark = require('benchmark')
const suite = new Benchmark.Suite()
// 添加測試
suite
/**
* add() 方法接受兩個參數(shù),其中第一個表示測試的名稱,第二個表示測試的內(nèi)容,他是一個函數(shù)*
/
.add('join1000', () => {
new Array(1000).join(' ')
})
.add('join10000', () => {
new Array(10000).join(' ')
})
// 添加時間監(jiān)聽
.on('cycle', event => {
// 打印執(zhí)行時間
console.log(String(event.target))
})
// 完成后執(zhí)行觸發(fā)的事件
.on('complete', () => {
console.log('最快的是:' + suite.filter('fastest').map('name'))
})
// 執(zhí)行測試
.run({ async: true })
復制代碼代碼執(zhí)行結(jié)果如下:
// join1000 x 146,854 ops/sec ±1.86% (88 runs sampled)
// join10000 x 16,083 ops/sec ±1.06% (92 runs sampled)
// 最快的是:join1000
在結(jié)果中,ops/sec表示的是每秒執(zhí)行的次數(shù),當然是越大越好,緊接著是每秒執(zhí)行次數(shù)上下相差的百分比,最后括號中的內(nèi)容表示共取樣多少次。
或者也可以使用JSBench.me工具進行替換,網(wǎng)站測試截圖如下:

我們可以看到,都是join1000的性能更好一些(我感覺我在說廢話)。
慎用全局變量
這里所說的慎用全局變量,為什么要慎用呢?主要有以下幾點:
- 全局變量定義在全局執(zhí)行上下文,是所有作用域鏈的頂端。每次查找的時候都從局部找到最頂端,在時間上會有所消耗。
- 全局執(zhí)行上下文一直存在于上下文的執(zhí)行棧,直到程序退出,才會被銷毀,內(nèi)存空間浪費 。
- 如果某個局部作用域出現(xiàn)了同名的變量則會遮蓋或者說污染全局變量 。
下面我們就來寫一段代碼,看一下全局變量與布局變量在執(zhí)行效率方面的差異,代碼如下:
...
suite
.add('全局變量', () => {
// 該函數(shù)內(nèi)模擬全局作用域
let i,
str = ''
for (i = 0; i < 1000; i++) {
str += i
}
})
.add('局部變量', () => {
for (let i = 0, str = ''; i < 1000; i++) {
str += i
}
})
...代碼運行結(jié)果如下:
全局變量 x 158,697 ops/sec ±1.05% (87 runs sampled)
局部變量 x 160,697 ops/sec ±1.03% (90 runs sampled)
最快的是:局部變量
雖然說差異不大,但是我們可以感知全局變量比局部的性能更差一些。
通過原型新增方法
為構(gòu)造函數(shù)增加實例對象需要的方法時,盡量使用原型的方式添加,而不是構(gòu)造函數(shù)內(nèi)部進行添加,我們可以看如下測試代碼:
...
suite
.add('構(gòu)造函數(shù)內(nèi)部添加', () => {
function Person() {
this.sayMe = function () {
return '一碗周'
}
}
let p = new Person()
})
.add('原型方式內(nèi)部添加', () => {
function Person() {}
Person.prototype.sayMe = function () {
return '一碗周'
}
let p = new Person()
})
...代碼運行結(jié)果如下:
構(gòu)造函數(shù)內(nèi)部添加 x 573,786 ops/sec ±1.97% (89 runs sampled)
原型方式內(nèi)部添加 x 581,693 ops/sec ±3.46% (80 runs sampled)
最快的是:構(gòu)造函數(shù)內(nèi)部添加
避免閉包中的內(nèi)存泄露
由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,嚴重可能導致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除 (即將局部變量重新賦值為null)。
避免使用屬性訪問方法
在JavaScript中的對象中,避免使用一些屬性訪問方法,這是因為JavaScript中的所有屬性都是外部可見的。
示例代碼如下:
...
suite
.add('使用屬性訪問方法', () => {
function Person() {
this.name = '一碗周'
this.getName = function () {
return '一碗周'
}
}
let p = new Person()
let n = p.getName()
})
.add('不使用屬性訪問方法', () => {
function Person() {
this.name = '一碗周'
}
let p = new Person()
let n = p.name
})
...代碼運行結(jié)果如下:
使用屬性訪問方法 x 406,682 ops/sec ±2.33% (82 runs sampled)
不使用屬性訪問方法 x 554,169 ops/sec ±2.03% (85 runs sampled)
最快的是:不使用屬性訪問方法
for循環(huán)優(yōu)化
我們在使用for循環(huán)時,可以將有些必要的數(shù)據(jù)進行緩存,就比如arr.length這種屬性,不需要每次判斷都獲取一下,從而優(yōu)化我們的代碼。
示例代碼如下:
...
suite
.add('正序', () => {
let arr = new Array(100)
let str = ''
for (let i = 0; i < arr.length; i++) {
str += i
}
})
.add('緩存', () => {
let arr = new Array(100)
let str = ''
for (let i = arr.length; i; i--) {
str += i
}
})
.add('緩存的另一種寫法', () => {
let arr = new Array(100)
let str = ''
for (let i = 0, l = arr.length; i < l; i++) {
str += i
}
})
...代碼運行結(jié)果如下:
正序 x 1,322,889 ops/sec ±1.36% (86 runs sampled)
緩存 x 1,356,696 ops/sec ±0.70% (92 runs sampled)
緩存的另一種寫法 x 1,383,091 ops/sec ±0.70% (93 runs sampled)
最快的是:緩存的另一種寫法
選擇最優(yōu)的循環(huán)方式
我們現(xiàn)在常用的循環(huán)有forEach、for和for...in循環(huán),這幾種那個是性能最優(yōu)的呢,測試代碼如下:
...
suite
.add('forEach', () => {
let arr = new Array(100)
let str = ''
arr.forEach(i => {
str += i
})
})
.add('for...in', () => {
let arr = new Array(100)
let str = ''
for (i in arr) {
str += i
}
})
.add('for', () => {
let arr = new Array(100)
let str = ''
for (let i = 0, l = arr.length; i < l; i++) {
str += i
}
})
...代碼運行結(jié)果如下:
forEach x 4,248,577 ops/sec ±0.89% (86 runs sampled)
for...in x 4,583,375 ops/sec ±1.15% (91 runs sampled)
for x 1,343,871 ops/sec ±1.91% (88 runs sampled)
最快的是:for...in
由運行結(jié)果可以看出我們可以盡量使用for...in或者forEach循環(huán),減少使用for循環(huán)。
減少判斷層級
減少判斷層級就是減少一些if語句的嵌套,如果是一些必要的條件我們可以通過單層if結(jié)合return直接跳出函數(shù)的執(zhí)行,關于優(yōu)化前與優(yōu)化后的代碼執(zhí)行比對如下所示:
...
/***
接收兩類文件,zip 和 rar*
壓縮包的大小限制為 10 兆*
/
suite
.add('嵌套寫法', () => {
function uploadFile(suffix, size) {
// 允許上傳的后綴名
const suffixList = ['.zip', '.rar']
const M = 1024* 1024
if (suffixList.includes(suffix)) {
if (size <= 10* M) {
return '下載成功'
}
}
}
uploadFile('.zip', 1* 1024* 1024)
})
.add('減少判斷寫法', () => {
function uploadFile(suffix, size) {
// 允許上傳的后綴名
const suffixList = ['.zip', '.rar']
const M = 1024* 1024
if (!suffixList.includes(suffix)) return
if (size > 10* M) return
return '下載成功'
}
uploadFile('.zip', 1* 1024* 1024)
})
...代碼運行結(jié)果如下:
嵌套寫法 x 888,445,014 ops/sec ±2.48% (88 runs sampled)
減少判斷寫法 x 905,763,884 ops/sec ±1.35% (92 runs sampled)
最快的是:減少判斷寫法,嵌套寫法
雖然說差距并不是很大,但是不適用嵌套的代碼比普通代碼更優(yōu)一些。
減少作用域鏈查找層級
減少代碼中作用域鏈的查找也是代碼優(yōu)化的一種方法,如下代碼展示了兩者的區(qū)別:
...
suite
.add('before', () => {
var name = '一碗粥'
function sayMe() {
name = '一碗周'
function print() {
var age = 18
return name + age
}
print()
}
sayMe()
})
.add('after', () => {
var name = '一碗粥'
function sayMe() {
var name = '一碗周' // 形成局部作用域
function print() {
var age = 18
return name + age
}
print()
}
sayMe()
})
...代碼運行結(jié)果如下:
before x 15,509,793 ops/sec ±7.78% (76 runs sampled)
after x 17,930,066 ops/sec ±2.89% (83 runs sampled)
最快的是:after
上面代碼只是為了展示區(qū)別,并沒有實際意義。
減少數(shù)據(jù)讀取次數(shù)
如果對象中的某個數(shù)據(jù)在一個代碼塊中使用兩遍以上,這樣的話將其進行緩存從而減少數(shù)據(jù)的讀取次數(shù)來達到更優(yōu)的一個性能,
測試代碼如下:
...
var userList = {
one: {
name: '一碗周',
age: 18,
},
two: {
name: '一碗粥',
age: 18,
},
}
suite
.add('before', () => {
function returnOneInfo() {
userList.one.info = userList.one.name + userList.one.age
}
returnOneInfo()
})
.add('after', () => {
function returnOneInfo() {
let one = userList.one
one.info = one.name + one.age
}
returnOneInfo()
})
...代碼運行結(jié)果如下:
before x 222,553,199 ops/sec ±16.63% (26 runs sampled)
after x 177,894,903 ops/sec ±1.85% (88 runs sampled)
最快的是:before
字面量與構(gòu)造式
凡是可以使用字面量方式聲明的內(nèi)容,絕對是不可以使用構(gòu)造函數(shù)的方式聲明的,兩者在性能方面相差甚遠,代碼如下:
...
suite
.add('before', () => {
var str = new String('string')
})
.add('after', () => {
var str = 'string'
})
...代碼運行結(jié)果如下:
before x 38,601,223 ops/sec ±1.16% (89 runs sampled)
after x 897,491,903 ops/sec ±0.92% (92 runs sampled)
最快的是:after
到此這篇關于總結(jié)分享10個JavaScript代碼優(yōu)化小tips的文章就介紹到這了,更多相關JavaScript優(yōu)化tips內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript實現(xiàn)多態(tài)和繼承的封裝操作示例
這篇文章主要介紹了JavaScript實現(xiàn)多態(tài)和繼承的封裝操作,結(jié)合實例形式分析了javascript中多態(tài)與繼承的實現(xiàn)及封裝相關操作技巧,需要的朋友可以參考下2018-08-08
利用Javascript裁剪圖片并存儲的簡單實現(xiàn)
裁剪圖片對我們來說是再熟悉不過的了,最近工作中就又遇到了這個需求,所以想著干脆整理下來,方法大家和自己在需要的時候參考學習,所以這篇文章主要介紹了利用Javascript裁剪圖片并存儲的簡單實現(xiàn),后端PHP處理我用的是THINKPHP框架,需要的朋友可以參考下。2017-03-03
頁面下沉抖動效果-網(wǎng)站HTTP連接沒有效果-PC上有效果
頁面下沉抖動效果實現(xiàn)代碼,代碼少,功能還可以2008-05-05

