JavaScript性能優(yōu)化

#### 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>

```

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矾芙,一起剝皮案震驚了整個濱河市舍沙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剔宪,老刑警劉巖拂铡,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異葱绒,居然都是意外死亡感帅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門地淀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來失球,“玉大人,你說我怎么就攤上這事帮毁∷龋” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵作箍,是天一觀的道長硬梁。 經(jīng)常有香客問我,道長胞得,這世上最難降的妖魔是什么荧止? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上跃巡,老公的妹妹穿的比我還像新娘危号。我一直安慰自己,他們只是感情好素邪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布外莲。 她就那樣靜靜地躺著,像睡著了一般兔朦。 火紅的嫁衣襯著肌膚如雪偷线。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天沽甥,我揣著相機(jī)與錄音声邦,去河邊找鬼。 笑死摆舟,一個胖子當(dāng)著我的面吹牛亥曹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恨诱,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼媳瞪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了照宝?” 一聲冷哼從身側(cè)響起材失,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎硫豆,沒想到半個月后龙巨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熊响,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年旨别,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汗茄。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秸弛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洪碳,到底是詐尸還是另有隱情递览,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布瞳腌,位于F島的核電站绞铃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嫂侍。R本人自食惡果不足惜儿捧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一荚坞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧菲盾,春花似錦颓影、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至临谱,卻和暖如春璃俗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吴裤。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工旧找, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溺健,地道東北人麦牺。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像鞭缭,于是被迫代替她去往敵國和親剖膳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355