#### JavaScript性能優(yōu)化 **如何編寫高性能的JavaScript**
- 性能優(yōu)化是不可避免的
- 哪些內(nèi)容可以看作是性能優(yōu)化
- 無所不在的前端性能優(yōu)化
#### 本階段的核心是JavaScript語言的優(yōu)化
- 內(nèi)存管理
- 垃圾回收與常見的GC算法
- V8引擎的垃圾回收
- Performance工具(垃圾回收的監(jiān)控)
- 代碼優(yōu)化實(shí)例
#### JavaScript內(nèi)存管理(Memory Management)
- 內(nèi)存: 由可讀寫的單元組成, 表示一片可操作空間
- 管理: 認(rèn)為的去操作一片空間的申請击敌、使用和釋放
- 內(nèi)存管理: 開發(fā)者主動申請空間而晒、使用空間粮彤、釋放空間
- 管理流程: 申請-使用-釋放
```javascript
// 申請空間
let obj = {}
// 使用空間
obj.name = 'liuchao'
// 釋放空間
obj = null
```
#### JavaScript中的垃圾回收
- JavaScript中內(nèi)存管理是自動的
- 對象不再被引用時是垃圾
- 對象不能從根上訪問到時是垃圾
#### 可達(dá)對象
- 可達(dá)對象: 可以訪問到的對象(引用、作用域鏈)
- 可達(dá)的標(biāo)準(zhǔn)就是從根出發(fā)時候能夠被找到
- JavaScript中的跟可以理解為是全局變量對象
```javascript
let obj = {name: 'liuchao'}
let ali = obj
obj = null//ali變量還是有值的
```
#### JavaScript中的引用與可達(dá)
```javascript
function objGroup (obj1, obj2){
? ? obj1.next = obj2
? ? obj2.prev = obj1
? ? return {
? ? ? ? o1: obj1,
? ? ? ? o2: obj2
? ? }
}
let obj = objGroup({name: 'obj1'}, {name: 'obj2'})
console.log(obj)
const user1 = {age: 1}
const user2 = {age: 2}
const user3 = {age: 3}
const nameList = [user1.name, user2.name, user3.name, ]
// function fn(){
//? ? num1 = 1
//? ? num2 = 2
// }
function fn(){
? ? const num1 = 1
? ? const num2 = 2
}
fn()
```
#### GC算法介紹
- GC就是垃圾回收機(jī)制的簡寫
- GC可以找到內(nèi)存中的垃圾坟乾、并釋放和回收空間?
? ? - 程序中不再需要使用的對象
? ? - 程序中不能在訪問到的對象
- 算法就是工作時查找和回收所遵循的規(guī)則
- 常見GC算法
? ? - 引用計數(shù)
? ? - 標(biāo)記清除
? ? - 標(biāo)記整理
? ? - 分代回收
#### 引用計數(shù)算法實(shí)現(xiàn)原理
- 核心思想: 設(shè)置引用數(shù), 判斷當(dāng)前引用數(shù)是否為0
- 引用計數(shù)器(被棄用的原因, 性能不好)
- 引用關(guān)系改變時修改引用數(shù)字
- 引用數(shù)字為0時立即回收
- 引用計數(shù)的優(yōu)點(diǎn)
? ? - 發(fā)現(xiàn)垃圾時立即回收
? ? - 減少程序卡頓時間
? ? - 最大限度減少程序暫停(內(nèi)存即將占滿,立即清除)
- 引用計數(shù)的缺點(diǎn)
? ? - 無法回收循環(huán)引用的對象
? ? - 時間(資源)開銷大
```javascript
// circular reference 對象循環(huán)引用
function fn(){
? ? const obj1 = {}
? ? const obj2 = {}
? ? obj1.name = obj2
? ? obj2.name = obj1
? ? return 'A circular reference.'
}
fn()
```
#### 標(biāo)記清除算法的實(shí)現(xiàn)原理
- 核心思想: 分為標(biāo)記和清除兩個階段
- 遍歷所有對象找標(biāo)記活動對象(對象、子對象遞歸標(biāo)記)
- 遍歷所有對象清除沒有標(biāo)記對象(也會清除未清除對象的標(biāo)記)
- 回收相應(yīng)的空間(回收的空間放到空閑鏈表上方便后續(xù)使用)
- 標(biāo)記清除算法優(yōu)點(diǎn)
? ? - 解決對象循環(huán)引用
- 標(biāo)記清除算法缺點(diǎn)
? ? - 不會立即回收垃圾對象
? ? - 空間碎片化: 回收空間地址不連續(xù)(分散在各個角落)
#### 標(biāo)記整理算的實(shí)現(xiàn)原理
- 標(biāo)記整理可以看作是標(biāo)記清除的增強(qiáng)
- 標(biāo)記階段的操作和標(biāo)記清除一致
- 清除階段會執(zhí)行整理, 移動對象位置(為了地址上連續(xù))
- 不會立即回收垃圾對象
#### 常見GC算法總結(jié)
- 引用計數(shù)的優(yōu)點(diǎn)
? ? - 發(fā)現(xiàn)垃圾時立即回收
? ? - 減少程序卡頓時間
? ? - 最大限度減少程序暫停(內(nèi)存即將占滿,立即清除)
- 引用計數(shù)的缺點(diǎn)
? ? - 無法回收循環(huán)引用的對象
? ? - 時間(資源)開銷大
- 標(biāo)記清除算法優(yōu)點(diǎn)
? ? - 解決對象循環(huán)引用
- 標(biāo)記清除算法缺點(diǎn)
? ? - 不會立即回收垃圾對象
? ? - 空間碎片化: 回收空間地址不連續(xù)(分散在各個角落)
- 標(biāo)記整理的優(yōu)點(diǎn)
? ? - 減少碎片化空間
- 標(biāo)記整理的缺點(diǎn)點(diǎn)
? ? - 不會立即回收垃圾對象
#### 認(rèn)識V8
- V8是一款主流的JavaScript執(zhí)行引擎
- V8采用即時編譯
? ? - V8快: 1. 優(yōu)秀內(nèi)存管理機(jī)制 2. V8采用即時編譯(原先源碼轉(zhuǎn)換成字節(jié)碼, 然后字節(jié)碼轉(zhuǎn)換成機(jī)器碼執(zhí)行; 即時編譯: 源碼轉(zhuǎn)換成機(jī)器碼直接執(zhí)行)
- V8內(nèi)存設(shè)限(64X: 1.5G; 32X: 800M)
? ? - V8本身為了瀏覽器而制造, 現(xiàn)有大小對網(wǎng)頁應(yīng)用來說足夠使用了
? ? - V8內(nèi)部的垃圾回收機(jī)制決定了再用此設(shè)置是合理的
? ? ? ? - 垃圾內(nèi)存達(dá)到1.5G的時候使用增量標(biāo)記進(jìn)行垃圾回收是50ms,如果使用非增量標(biāo)記進(jìn)行垃圾回收需要1s
#### V8垃圾回收策略
- 采用分代回收的思想
- 內(nèi)存分為新生代悠汽、老生代
- 針對不同對象采用不同GC算法
? ? - 分代回收
? ? - 空間復(fù)制
? ? - 標(biāo)記清除
? ? - 標(biāo)記整理
? ? - 標(biāo)記增量
#### V8如何回收新生代對象
- V8內(nèi)存分配
? ? - V8內(nèi)存空間一分為二
? ? - 小空間用于存儲新生代對象(32M(64X)|16M(32X))
? ? - 新生代對象指存活時間比較短的對象(eq: 局部變量)
- 新生代對象回收實(shí)現(xiàn)
? ? - 回收過程采用復(fù)制算法 + 標(biāo)記整理
? ? - 新生代內(nèi)存去分為二個等大小空間
? ? - 使用空間為From, 空閑空間為To
? ? - 活動對象存儲于From空間
? ? - 標(biāo)記整理后將活動對象拷貝至To
? ? - From與To交換空間, 完成釋放
- 新生代對象回收實(shí)現(xiàn)---說明
? ? - 拷貝過程中可能出現(xiàn)晉升
? ? - 晉升就是將新生代對象移動至老生代
? ? ? ? - 一輪GC還存活的新生代需要晉升
? ? ? ? - To空間的使用率超過25%(整體移動至老生代: From和To會交換)
#### V8如何回收老生代對象
- 老生代對象存放在右側(cè)老生代區(qū)域
- 內(nèi)存限制(64位操作系統(tǒng)1.4G, 32位操作系統(tǒng)700M)
- 老生代對象就是指存活時間較長的對象(閉包等)
- 老生代對象回收實(shí)現(xiàn)
? ? - 主要采用標(biāo)記清除舞蔽、標(biāo)記整理、增量標(biāo)記算法
? ? - 首先使用標(biāo)記清除完成垃圾空間的回收
? ? - 采用標(biāo)記整理進(jìn)行空間優(yōu)化(晉升的時候并且老生代存儲空間不足以存放新生代移動過來的對象)
? ? - 采用增量標(biāo)記進(jìn)行效率優(yōu)化
#### 新生代與老生代回收的細(xì)節(jié)對比
- 新生代區(qū)域垃圾回收使用空間換時間(復(fù)制算法)
- 老生代區(qū)域籃機(jī)回收不適合復(fù)制算法(空間大浪費(fèi)奢侈栋操、對象數(shù)據(jù)比較多消耗時間多)
- 標(biāo)記增量如何優(yōu)化垃圾回收
? ? - 垃圾回收會終止程序執(zhí)行
? ? - 標(biāo)記增量: 整個垃圾回收過程拆分成多個小步,替代整個垃圾回收, 實(shí)現(xiàn)程序執(zhí)行和垃圾回收交替執(zhí)行
#### V8垃圾回收總結(jié)
- V8是一款主流的JavaScript執(zhí)行引擎
- V8內(nèi)存設(shè)限(64X: 1.5G; 32X: 800M)
- 采用分代回收的思想
- 內(nèi)存分為新生代闸餐、老生代
- 針對不同對象采用不同GC算法
#### Performance工具介紹
### 為什么使用Performance
- GC的目的是為了實(shí)現(xiàn)內(nèi)存空間的良性循環(huán)
- 良性循環(huán)的基石是合理使用
- 時刻關(guān)注才能確定是否合理
- Performance提供多種監(jiān)控方式
- **通過Performance時刻監(jiān)控內(nèi)存**
#### Performance使用步驟
- 打開瀏覽器輸入牧鞭網(wǎng)址
- 進(jìn)入開發(fā)人員工具面板, 選擇性能
- 開啟錄制功能, 訪問具體頁面
- 執(zhí)行用戶行為, 一段時間后停止錄制
- 分析界面中記錄的內(nèi)存信息
#### 內(nèi)存問題的外在體現(xiàn)
- 頻繁垃圾回收: 頁面出現(xiàn)延遲加載或經(jīng)常性暫停(網(wǎng)絡(luò)正常下)
- 內(nèi)存膨脹: 頁面持續(xù)性出現(xiàn)糟糕的性能(網(wǎng)絡(luò)正常下)
- 內(nèi)存泄漏: 頁面性能隨時間延長越來越差(網(wǎng)絡(luò)正常下)
#### 監(jiān)控內(nèi)存的幾種方式
- 內(nèi)存泄漏: 內(nèi)存使用持續(xù)升高
- 內(nèi)存膨脹: 在多數(shù)設(shè)備上都存在性能問題(主流設(shè)備上測試)
- 頻繁垃圾回收: 通過內(nèi)存變化圖進(jìn)行分析
- 方式
? ? - 瀏覽器任務(wù)管理器
? ? - Timeline時序圖記錄
? ? - 堆快照查找分離DOM
? ? - 判斷是否存在頻繁的垃圾回收
#### 瀏覽器任務(wù)管理器監(jiān)控內(nèi)存
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>瀏覽器任務(wù)管理器監(jiān)控內(nèi)存變化</title>
</head>
<body>
? ? <button id='btn'>Add</button>
? ? <script>
? ? ? ? const oBtn = document.getElementById('btn')
? ? ? ? oBtn.onclick = function (){
? ? ? ? ? ? let arrList = new Array(1000000)
? ? ? ? }
? ? </script>
</body>
</html>
```
#### Timeline記錄內(nèi)存
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>Timeline記錄內(nèi)存</title>
</head>
<body>
? ? <button id='btn'>Add</button>
? ? <script>
? ? ? ? // 1. 創(chuàng)建大量DOM節(jié)點(diǎn)模擬內(nèi)存消耗
? ? ? ? // 2. DOM節(jié)點(diǎn)不夠,數(shù)組配合其他方法, 形成非常長的字符串,模擬內(nèi)存消耗
? ? ? ? const arrList = []
? ? ? ? function test(){
? ? ? ? ? ? for(let i = 0; i < 100000;i++){
? ? ? ? ? ? ? ? document.body.appendChild(document.createElement('p'))
? ? ? ? ? ? }
? ? ? ? ? ? arrList.push(new Array(1000000).join('x'))
? ? ? ? }
? ? ? ? document.getElementById('btn').addEventListener('click', test)
? ? </script>
</body>
</html>
```
#### 堆快照查找分離DOM
- 界面元素存活在DOM樹上
- 垃圾對象時的DOM節(jié)點(diǎn)(從DOM樹上脫離, 并且程序中無引用)
- 分離狀態(tài)的DOM節(jié)點(diǎn)(分離節(jié)點(diǎn): 從DOM樹上脫離, 但是程序中有引用; 頁面上看不見, 但是占據(jù)內(nèi)存, 導(dǎo)致內(nèi)存泄漏)
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>堆快照查找分離DOM</title>
</head>
<body>
? ? <button id='btn'>Add</button>
? ? <script>
? ? ? ? // 1. 創(chuàng)建大量DOM節(jié)點(diǎn)模擬內(nèi)存消耗
? ? ? ? var tmpELe
? ? ? ? function? fn() {
? ? ? ? ? ? var ul = document.createElement('ul')
? ? ? ? ? ? for(var i = 0;i < 10;i++){
? ? ? ? ? ? ? ? var li = document.createElement("li")
? ? ? ? ? ? ? ? ul.appendChild(li)
? ? ? ? ? ? }
? ? ? ? ? ? tmpELe = ul
? ? ? ? ? ? tmpELe = null
? ? ? ? }
? ? ? ? document.getElementById('btn').addEventListener('click', fn)
? ? </script>
</body>
</html>
```
#### 判斷是否存在頻繁的垃圾回收
- 原因
? ? - GC工作時應(yīng)用程序時停止的
? ? - 頻繁且過長的GC會導(dǎo)致應(yīng)用假死
? ? - 用戶使用中感知應(yīng)用卡頓
- 方法
? ? - Timeline中頻繁的上升和下降
? ? - 任務(wù)管理器中數(shù)據(jù)頻繁的增大減小
#### Performance使用總結(jié)
- Performance使用步驟
- 內(nèi)存問題的相關(guān)分析
- Performance時序圖監(jiān)控內(nèi)存變化
- 瀏覽器任務(wù)管理器監(jiān)控內(nèi)存
- 堆快照查找分離DOM
#### 代碼優(yōu)化介紹
- 如何精準(zhǔn)測試JavaScript性能
? ? - 本質(zhì)上就是采集大量的執(zhí)行樣本進(jìn)行數(shù)學(xué)統(tǒng)計和分析
? ? - 使用基于Benchmark.js的https://jsperf.com/ 完成
#### Jsperf使用流程
- 使用GitHub賬號登陸
- 填寫個人信息(非必須)
- 填寫詳細(xì)的測試用例信息(title、slug)
- 填寫準(zhǔn)備代碼(DOM操作室經(jīng)常使用)
- 填寫必要的setup和teardown代碼
- 填寫測試代碼片段
#### 慎用全局變量
- 全局變量定義在全局執(zhí)行上下文, 是所有作用域鏈的頂端(時間長)
- 全局執(zhí)行上下文一直存在于上下文執(zhí)行棧, 知道程序退出(GC工作不力, 降低內(nèi)存使用)
- 如果某個局部作用域出現(xiàn)了同名變量則護(hù)額遮蔽或者污染全局
```javacsript
? ? // 全局變量
? ? var i, str = ''
? ? for(i = 0; i < 100; i++ ){
? ? ? ? str += i
? ? }
? ? for(let i = 0; i < 100; i++ ){
? ? ? ? let str = ''
? ? ? ? str += i
? ? }
? ? // 局部變量有很大性能提升
? ? // Jsperf中查看結(jié)果
```
#### 緩存全局變量
- 將使用中無法避免的全局變量緩存到局部
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>緩存全局變量</title>
</head>
<body>
? ? <input type="button" value='btn' id='btn1'>
? ? <input type="button" value='btn' id='btn2'>
? ? <input type="button" value='btn' id='btn3'>
? ? <p>111</p>
? ? <input type="button" value='btn' id='btn4'>
? ? <input type="button" value='btn' id='btn5'>
? ? <p>222</p>
? ? <input type="button" value='btn' id='btn6'>
? ? <input type="button" value='btn' id='btn7'>
? ? <input type="button" value='btn' id='btn8'>
? ? <p>33333</p>
? ? <input type="button" value='btn' id='btn9'>
? ? <input type="button" value='btn' id='btn10'>
? ? <script>
? ? ? ? function getBtn() {
? ? ? ? ? ? let oBtn1 = document.getElementById('btn1')
? ? ? ? ? ? let oBtn3 = document.getElementById('btn3')
? ? ? ? ? ? let oBtn5 = document.getElementById('btn5')
? ? ? ? ? ? let oBtn7 = document.getElementById('btn7')
? ? ? ? ? ? let oBtn9 = document.getElementById('btn9')
? ? ? ? }
? ? ? ? function getBtn2() {
? ? ? ? ? ? let obj = document
? ? ? ? ? ? let oBtn1 = obj.getElementById('btn1')
? ? ? ? ? ? let oBtn3 = obj.getElementById('btn3')
? ? ? ? ? ? let oBtn5 = obj.getElementById('btn5')
? ? ? ? ? ? let oBtn7 = obj.getElementById('btn7')
? ? ? ? ? ? let oBtn9 = obj.getElementById('btn9')
? ? ? ? }
? ? ? ? // Jsperf中查看結(jié)果
? ? </script>
</body>
</html>
```
#### 通過原型新增方法
- 在原型對象上新增實(shí)例對象需要的方法
```javascript
var fn1= function(){
? ? this.foo = function(){
? ? ? ? console.log(11111)
? ? }
}
let f1 = new fn1()
var fn2 = function(){}
fn2.prototype.foo = function(){
? ? console.log(1111)
}
let f2 = new fn2()
// Jsperf中查看運(yùn)算速度
```
#### 避開閉包陷阱
- 閉包特點(diǎn)
? ? - 外部具有指向內(nèi)部的引用
? ? - 在‘外’部作用域訪問‘內(nèi)’部作用域的數(shù)據(jù)
- 閉包
? ? - 閉包是一種強(qiáng)大的語法
? ? - 閉包使用不當(dāng)很容易出現(xiàn)內(nèi)存泄漏
? ? - 不要為了閉包而閉包
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>閉包陷阱</title>
</head>
<body>
? ? <button id="btn">btn</button>
? ? <script>
? ? ? ? // function foo() {
? ? ? ? //? ? var el = document.getElementById('btn')
? ? ? ? //? ? el.onclick = function () {
? ? ? ? //? ? ? ? console.log(el.id)
? ? ? ? //? ? }
? ? ? ? // }
? ? ? ? // foo()
? ? ? ? // 閉包優(yōu)化
? ? ? ? function foo() {
? ? ? ? ? ? var el = document.getElementById('btn')
? ? ? ? ? ? el.onclick = function () {
? ? ? ? ? ? ? ? console.log(el)
? ? ? ? ? ? ? ? console.log(el, el.id)
? ? ? ? ? ? }
? ? ? ? ? ? el = null
? ? ? ? }
? ? ? ? foo()
? ? </script>
</body>
</html>
```
#### 避免屬性訪問方法使用
- JavaScript中的面向?qū)ο?/p>
? ? - JS不需要屬性的訪問方法, 所有屬性都是外部可見的
? ? - 書用屬性訪問方法只會增加一層重定義, 沒有訪問的控制力
```javascript
function Person (){
? ? this.name = 'Person'
? ? this.age = 19
? ? this.getAge = function(){
? ? ? ? return this.age
? ? }
}
const p1 = new Person()
const a = p2.getAge()
function Person1 (){
? ? this.name = 'Person'
? ? this.age = 19
}
const p2 = new Person1()
const b = p2.age
// Jsperf上查看運(yùn)行速度
```
#### For循環(huán)優(yōu)化 length提取
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>For循環(huán)優(yōu)化</title>
</head>
<body>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <p class="btn"></p>
? ? <script>
? ? ? ? var aBtns = document.getElementsByClassName('btn')
? ? ? ? for(var i = 0; i < aBtns.length; i++){
? ? ? ? ? ? console.log(i)
? ? ? ? }
? ? ? ? for(var i = 0, len = aBtns.length; i < len; i++){
? ? ? ? ? ? console.log(i)
? ? ? ? }
? ? ? ? // Jsperf上查看運(yùn)行速度
? ? </script>
</body>
</html>
```
#### 采用最優(yōu)循環(huán)方式
```javascript
// 采用最優(yōu)循環(huán)方式
const arr = new Array(1, 2, 3, 4, 5)
arr.forEach(function(item){
? ? console.log(item)
})
for(var i = arr.length; i; i--){
? ? console.log(arr[i])
}
for(var i in arr){
? ? console.log(arr[i])
}
// Jsperf中查看運(yùn)行速度
```
#### 節(jié)點(diǎn)添加優(yōu)化
- 節(jié)點(diǎn)添加操作必然會有回流和重繪
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>優(yōu)化節(jié)點(diǎn)添加</title>
</head>
<body>
? ? <script>
? ? ? ? for(var i = 0; i < 100; i++){
? ? ? ? ? ? var oP = document.createElement('p')
? ? ? ? ? ? oP.innerHTML = i
? ? ? ? ? ? document.body.appendChild(oP)
? ? ? ? }
? ? ? ? const fragEle = document.createDocumentFragment()
? ? ? ? for(var i = 0; i < 100; i++){
? ? ? ? ? ? var oP = document.createElement('p')
? ? ? ? ? ? oP.innerHTML = i
? ? ? ? ? ? fragEle.appendChild(oP)
? ? ? ? }
? ? ? ? document.body.appendChild(fragEle)
? ? ? ? // Jsperf 查看運(yùn)行速度
? ? </script>
</body>
</html>
```
#### 克隆優(yōu)化節(jié)點(diǎn)操作
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>克隆優(yōu)化節(jié)點(diǎn)操作</title>
</head>
<body>
? ? <p id="box1">old</p>
? ? <script>
? ? ? ? // 創(chuàng)建新節(jié)點(diǎn)時 : 先clone一個已有的節(jié)點(diǎn), 再在clone節(jié)點(diǎn)上作修改
? ? ? ? for(var i = 0; i< 5; i++){
? ? ? ? ? ? var oP = document.createElement("p")
? ? ? ? ? ? oP.innerHTML = i
? ? ? ? ? ? document.body.appendChild(oP)
? ? ? ? }
? ? ? ? var oldP = document.getElementById('box1')
? ? ? ? for(var i = 0; i< 5; i++){
? ? ? ? ? ? var newP = oldP.cloneNode(false)
? ? ? ? ? ? newP.innerHTML = i
? ? ? ? ? ? document.body.appendChild(newP)
? ? ? ? }
? ? ? ? // Jsperf 查看運(yùn)行速度
? ? </script>
</body>
</html>
```
#### 直接量替換Object操作
```javascript
var a = [1, 2, 3]
var a1 = new Array(3)
a1[0] = 1
a1[1] = 2
a1[2] = 3
// Jsperf中查看運(yùn)算速度
```
#### **JavaScript性能提升2**? 空間換時間 或者 時間換空間
#### JSBench的使用(JSBench.me)
#### 堆棧中代碼執(zhí)行流程
```javascript
let a = 10
function foo(b){
? ? let a = 2
? ? function baz(c){
? ? ? ? console.log(a+b+c)
? ? }
? ? return baz
}
let fn = foo(2)
fn(3)//=> 7 存在閉包 導(dǎo)致函數(shù)foo作用域沒有被釋放
// 減少判斷層級?
```
#### 減少判斷層級
```javascript
// function doSome(part, chapter){
//? ? const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']
//? ? if(part){
//? ? ? ? if(parts.includes(part)){
//? ? ? ? ? ? console.log('屬于前端課程')
//? ? ? ? ? ? if(chapter > 5){
//? ? ? ? ? ? ? ? console.log('需要提供Vip身份')
//? ? ? ? ? ? }
//? ? ? ? }
//? ? }else{
//? ? ? ? console.log('請確認(rèn)模塊信息')
//? ? }
// }
// doSome('ES2016', 6)
function doSome(part, chapter){
? ? const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']
? ? if(!part){
? ? ? ? console.log('請確認(rèn)模塊信息')
? ? ? ? return
? ? }
? ? if(!parts.includes(part)) return
? ? console.log('屬于前端課程')
? ? if(chapter > 5){
? ? ? ? console.log('需要提供Vip身份')
? ? }
}
doSome('ES2016', 6)
```
#### 減少作用域鏈查找層級
```javascript
// var name = 'foo'
// function foo(){
//? ? name = 'foo666'//全局的name變量
//? ? function baz(){
//? ? ? ? var age = 39
//? ? ? ? console.log(age)
//? ? ? ? console.log(name)
//? ? }
//? ? baz()
// }
// foo()
var name = 'foo'
function foo(){
? ? var name = 'foo666'//全局的name變量
? ? function baz(){
? ? ? ? var age = 39
? ? ? ? console.log(age)
? ? ? ? console.log(name)
? ? }
? ? baz()
}
foo()
```
#### 減少數(shù)據(jù)讀取次數(shù)
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>減少數(shù)據(jù)讀取次數(shù)</title>
</head>
<body>
? ? <div id="skip" class="skip"></div>
? ? <script>
? ? ? ? var oBox = document.getElementById('skip')
? ? ? ? // function hasEle(ele, cls){
? ? ? ? //? ? return ele.className == cls
? ? ? ? // }
? ? ? ? function hasEle(ele, cls){
? ? ? ? ? ? var clsname = ele.className
? ? ? ? ? ? return clsname == cls
? ? ? ? }
? ? ? ? console.log(hasEle(oBox, 'skip'))
? ? </script>
</body>
</html>
```
#### 字面量與構(gòu)造式
```javascript
// let test = () => {
//? ? let obj = new Object()
//? ? obj.name = 'liuchao'
//? ? obj.age = 39
//? ? obj.slogan = '我為前端而活'
//? ? return obj
// }
// let test = () => {
//? ? let obj = {
//? ? ? ? name : 'liuchao',
//? ? ? ? age : 39,
//? ? ? ? slogan : '我為前端而活'
//? ? }
//? ? return obj
// }
// console.log(test())
// ----------------------
var str1 = '我為前端而活'
var str2 = new String('我為前端而活')
console.log(str1)
console.log(str2)
```
#### 減少循環(huán)體活動
```javascript
// var test = () => {
//? ? var i
//? ? var arr = ['liuchao', 39, '我為前端而活']
//? ? for( i =0;i<arr.length;i++){
//? ? ? ? console.log(arr[i])
//? ? }
// }
// var test = () => {
//? ? var i
//? ? var arr = ['liuchao', 39, '我為前端而活']
//? ? var len = arr.length
//? ? for( i =0;i<len;i++){
//? ? ? ? console.log(arr[i])
//? ? }
// }
var test = () => {
? ? var arr = ['liuchao', 39, '我為前端而活']
? ? var len = arr.length
? ? while(len--){
? ? ? ? console.log(arr[len])
? ? }
}
test()
```
#### 減少聲明及語句數(shù)--詞法分析消耗時間
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>減少聲明及語句數(shù)-詞法分析消耗時間</title>
</head>
<body>
? ? <div id="box" style="width: 100px;height: 200px;"></div>
? ? <script>
? ? ? ? var oBox = document.getElementById('box')
? ? ? ? // var test = function (ele){
? ? ? ? //? ? let w = ele.offsetWidth
? ? ? ? //? ? let h = ele.offsetHeight
? ? ? ? //? ? return w * h
? ? ? ? // }
? ? ? ? // var test = function (ele){
? ? ? ? //? ? return ele.offsetWidth * ele.offsetHeight
? ? ? ? // }
? ? ? ? // console.log(test(oBox))
? ? ? ? // var test = () => {
? ? ? ? //? ? var name = 'liucaho'
? ? ? ? //? ? var age = 39
? ? ? ? //? ? var slogan = '我為前端而活'
? ? ? ? //? ? return name + age + slogan
? ? ? ? // }
? ? ? ? var test = () => {
? ? ? ? ? ? var name = 'liucaho',
? ? ? ? ? ? ? ? age = 39,
? ? ? ? ? ? ? ? slogan = '我為前端而活'
? ? ? ? ? ? return name + age + slogan
? ? ? ? }
? ? ? ? console.log(test())
? ? </script>
</body>
</html>
```
#### 惰性函數(shù)與性能
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>惰性函數(shù)與性能</title>
</head>
<body>
? ? <button id="btn">點(diǎn)擊</button>
? ? <script>
? ? ? ? var oBtn = document.getElementById('btn')
? ? ? ? function foo(){
? ? ? ? ? ? console.log(this)
? ? ? ? }
? ? ? ? // function addEvent (obj, type, fn){
? ? ? ? //? ? if(obj.addEventListerer){
? ? ? ? //? ? ? ? obj.addEventListerer(type, fn, false)
? ? ? ? //? ? }else if(obj.attachEvent){
? ? ? ? //? ? ? ? obj.attachEvent('on' + type, fn)
? ? ? ? //? ? }else{
? ? ? ? //? ? ? ? obj['on' + type ] = fn
? ? ? ? //? ? }
? ? ? ? // }
? ? ? ? function addEvent (obj, type, fn){
? ? ? ? ? ? if(obj.addEventListerer){
? ? ? ? ? ? ? ? addEvent = obj.addEventListerer(type, fn, false)
? ? ? ? ? ? }else if(obj.attachEvent){
? ? ? ? ? ? ? ? addEvent = obj.attachEvent('on' + type, fn)
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? addEvent = obj['on' + type ] = fn
? ? ? ? ? ? }
? ? ? ? ? ? return addEvent
? ? ? ? }
? ? ? ? addEvent(oBtn, 'click', foo)
? ? </script>
</body>
</html>
```
#### 采用事件委托
```html
<!DOCTYPE html>
<html lang="en">
<head>
? ? <meta charset="UTF-8">
? ? <meta name="viewport" content="width=device-width, initial-scale=1.0">
? ? <title>使用事件委托</title>
</head>
<body>
? ? <ul id="ul">
? ? ? ? <li>liuchao</li>
? ? ? ? <li>28</li>
? ? ? ? <li>male</li>
? ? ? ? <li>我為前端而活</li>
? ? </ul>
? ? <script>
? ? ? ? var list = document.querySelectorAll('li')
? ? ? ? // function showTxt(ev){
? ? ? ? //? ? console.log(ev.target.innerHTML)
? ? ? ? // }
? ? ? ? // for(let item of list){
? ? ? ? //? ? item.onclick = showTxt
? ? ? ? // }
? ? ? ? var oUl = document.getElementById('ul')
? ? ? ? oUl.addEventListener('click', showTxt, false)
? ? ? ? function showTxt(ev){
? ? ? ? ? ? var obj = ev.target
? ? ? ? ? ? if(obj.nodeName.toLowerCase() === 'li'){
? ? ? ? ? ? ? ? console.log(ev.target.innerHTML)
? ? ? ? ? ? }
? ? ? ? }
? ? </script>
</body>
</html>
```