ES 新特性與 TypeScript、JS 性能優(yōu)化

es2015

模板字符串

帶標(biāo)簽的模板字符串

定義模板字符串之前可以添加一個(gè)標(biāo)簽,該標(biāo)簽就是一個(gè)函數(shù),添加這個(gè)標(biāo)簽就是調(diào)用這個(gè)函數(shù)

let str = console.log`hello` // [ 'hello' ]
function func (string, str1, str2) { 
    console.log(string)
    console.log(str1, str2) // 可以接收到模板字符串中表達(dá)式的返回值
}
let a = 'tom'
let b = true
let result = func`${a} is man, $` // [ '', ' is man, ', '' ]
let result1 = func`asd ${a} is man, $asd` // [ 'asd ', ' is man, ', 'asd' ]

在模板字符串中會(huì)有嵌入的表達(dá)式,所以最后輸出的結(jié)果就是按照表達(dá)式分割過(guò)后那些靜態(tài)的內(nèi)容,相當(dāng)于對(duì)傳入使用split方法分割

字符串的擴(kuò)展方法

let message = 'Error: foo is not defined'
// 判斷字符串是否以指定字符串開(kāi)頭
console.log(message.startsWith('Error')) // true
// 判斷字符串是否以指定字符串結(jié)尾
console.log(message.endsWith('.')) // false
// 判斷字符串是否包含指定字符串
console.log(message.includes('Error')) // true

Object擴(kuò)展方法

Object.assign

將多個(gè)源對(duì)象中的屬性復(fù)制到另一個(gè)目標(biāo)對(duì)象中

let obj1 = {
    a: 123,
    b: 456
}
let obj2 = {
    a: 111,
    c: 789
}
let newObj = Object.assign(obj1, obj2)
console.log(newObj) // { a: 111, b: 456, c: 789 }
console.log(obj1) // { a: 111, b: 456, c: 789 }
console.log(obj1 === newObj) // true

Object.is

用來(lái)比較兩個(gè)值是否嚴(yán)格相等

// === 全等運(yùn)算符比較+0 和 -0時(shí)的表現(xiàn)是相等的, NaN和NaN比較是不等的
// Object.is比較+0 和 -0是不等的,NaN和NaN是相等的

Proxy

const p = new Proxy(target, handle)
// target: 要使用Proxy包裝的目標(biāo)對(duì)象,可以是任何類型的對(duì)象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理
// handle: 一個(gè)通常以函數(shù)作為屬性的對(duì)象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為

Proxy和Object.defineProperty()的區(qū)別:

  • Object.defineProperty只能監(jiān)聽(tīng)到對(duì)象屬性的讀取和寫(xiě)入,Proxy能監(jiān)聽(tīng)到更多對(duì)象操作例如delete操作,對(duì)象方法的調(diào)用等

Proxy監(jiān)聽(tīng)可監(jiān)聽(tīng)的對(duì)象操作

handle方法 觸發(fā)方式
get 讀取某個(gè)屬性
set 寫(xiě)入某個(gè)屬性
has in操作符
deleteProperty delete操作符
getPrototypeOf Object.getPrototypeOf()
setPrototypeOf Object.setPrototypeOf()
isExtensible Object.isExtensible()
preventExtensions Object.preventExtensions()
getOwnPropertyDescriptor Object.getOwnPropertyDescriptor()
defineProperty Object.defineProperty()
ownKeys Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()
apply 調(diào)用一個(gè)函數(shù)
construct 用new調(diào)用一個(gè)函數(shù)
  • Proxy更好的支持?jǐn)?shù)組對(duì)象的監(jiān)視
    原始的Object.defineProperty()對(duì)數(shù)組的監(jiān)視最常見(jiàn)的就是重寫(xiě)數(shù)組的方法,這也是vue.js中的操作方式,大體思路就是通過(guò)自定義方法覆蓋數(shù)組原有的方法
  • Proxy是以非侵入的方式監(jiān)管了對(duì)象的讀寫(xiě)(對(duì)已定義好的對(duì)象無(wú)需對(duì)對(duì)象本身進(jìn)行任何操作就可以監(jiān)視到對(duì)象的讀寫(xiě))

Reflect

Reflect屬于一個(gè)靜態(tài)類,不能通過(guò)new操作符構(gòu)建一個(gè)實(shí)例,只能調(diào)用該靜態(tài)類中的靜態(tài)方法(Reflect.get()).
Reflect內(nèi)部封裝了一系列對(duì)對(duì)象的底層操作,Reflect成員方法就是Proxy處理對(duì)象的默認(rèn)實(shí)現(xiàn),Reflect統(tǒng)一提供了一套用于操作對(duì)象的API

class

class類中,static靜態(tài)方法中的this指向的事當(dāng)前的類而非實(shí)例

class A {
    constructor() {
        this.name = 1
    }
    static say() {
        console.log(this)
    }
}
A.say() // class A

extends繼承

super關(guān)鍵字指向父類

Set數(shù)據(jù)結(jié)構(gòu)

Set內(nèi)部的成員都是唯一的不允許重復(fù)的

let s = new Set()
s.add(1).add(2) // add方法往Set集合中添加成員且返回集合對(duì)象本身,所以可以鏈?zhǔn)秸{(diào)用
s.size // size獲取集合長(zhǎng)度
s.has(1) // 判斷集合中是否存在某個(gè)特定的值
s.delete(1) // 刪除集合中某個(gè)特定的值
s.clear() // 清除當(dāng)前集合中全部的值

Map數(shù)據(jù)結(jié)構(gòu)

原始對(duì)象只能使用字符串作為對(duì)象的鍵,如果使用非字符串作為鍵則內(nèi)部會(huì)調(diào)用toString()方法將其轉(zhuǎn)換為字符串,
es6中也可以使用Symbol作為鍵
Map數(shù)據(jù)結(jié)構(gòu)解決了這一問(wèn)題,是嚴(yán)格意義上的鍵值對(duì)集合,用來(lái)映射兩個(gè)任意數(shù)據(jù)類型之前的對(duì)應(yīng)關(guān)系
Map數(shù)據(jù)結(jié)構(gòu)和對(duì)象最大的區(qū)別就是對(duì)象只能使用字符串作為鍵,Map集合可以使用任何類型作為鍵

// 原始對(duì)象
let obj = {}
obj[123] = false
obj['name'] = false
obj[true] = false
obj[{a: 1}] = false // [object Object]
for (let k in obj) {
    console.log(typeof k) // string
}

// Map
let m = new Map()
let key = {a: 1}
m.set(key, false) // 設(shè)置屬性
m.get(key)  // 獲取指定鍵對(duì)應(yīng)的值
m.has(key) // 判斷指定鍵是否存在
m.clear() // 清空Map集合

Symbol

Symbol是一種新的原始數(shù)據(jù)類型,表示一個(gè)獨(dú)一無(wú)二的數(shù)據(jù)
最主要的作用就是為對(duì)象添加獨(dú)一無(wú)二的屬性名

    // 每次用過(guò)Symbol函數(shù)創(chuàng)建的值都是唯一的一個(gè)值,無(wú)論傳入的描述文本是否相同
    console.log(Symbol('foo') === Symbol('foo')) // false
    // 如果想復(fù)用一個(gè)相同的Symbol值,可以使用變量接收或Symbol提供的靜態(tài)方法for
    // for方法會(huì)根據(jù)給的值從Symbol注冊(cè)表中找到對(duì)應(yīng)的ymbol,如果找到了,則返回它,否則,新建一個(gè)與該鍵關(guān)聯(lián)的Symbol
    let s1 = Symbol.for('foo') // 
    let s2 = Symbol.for('foo')
    console.log(s1 === s2) // true
    // for維護(hù)的是字符串與Symbol值對(duì)應(yīng)的關(guān)系,如果傳入非字符串類型,會(huì)轉(zhuǎn)換成字符串
    console.log(Symbol.for(true) === Symbol.for('true')) // true
    console.log(Symbol.for('[object Object]') === Symbol.for({b: 1}))

Symbol.toStringTag 是一個(gè)內(nèi)置Symbol,它通常作為對(duì)象的屬性鍵使用,對(duì)應(yīng)的屬性值應(yīng)該為字符串類型,這個(gè)字符串用來(lái)表示該對(duì)象的自定義類型標(biāo)簽

let obj = {}
console.log(obj.toString()) // [object Object]
let obj1 = {
    [Symbol.toStringTag]: 'asd'
}
console.log(obj1.toString()) // [object asd]

用Symbol值作為對(duì)象的屬性名,使用for...in循環(huán)和Object.keys()是無(wú)法拿到的,使用JSON.stringify將對(duì)象轉(zhuǎn)換成字符串時(shí)Symbol值也會(huì)被忽略掉,所以Symbol適合作為對(duì)象的私有屬性

let obj = {
    [Symbol()]: 1,
    a: 2
}
for (let k in obj) {
    console.log(k) // a
}
console.log(Object.keys(obj)) // [ 'a' ]
console.log(JSON.stringify(obj)) // {"a":2}
// 可以通過(guò)Object.getOwnPropertySymbol(obj) 獲取到對(duì)象中Symbol的屬性名

for...of

for...of方法可以遍歷任意可迭代對(duì)象包括 Array,Map,Set,String,TypedArray,arguments 對(duì)象等等,所以可以被for...of循環(huán)直接遍歷的數(shù)據(jù)結(jié)構(gòu)必須實(shí)現(xiàn)了Iterable接口
for...of方法可以使用break關(guān)鍵詞終止循環(huán),forEach循環(huán)除了拋出異常以外不能終止
for...of遍歷Map數(shù)據(jù)結(jié)構(gòu)返回鍵值對(duì)數(shù)組

let m = new Map()
m.set('a', 1)
m.set('b', 2)
for (let value of m) {
    console.log(value) // [ 'a', 1 ]
                       // [ 'b', 2 ]
}

可迭代對(duì)象Iterable

// 實(shí)現(xiàn)可迭代接口
let obj = {
    store: ['asd', 'qwe', 'zxc', 'fgh'],
    [Symbol.iterator]: function () {
        let index = 0
        let self = this
        return {
            next: function () {
                let result = {
                    value: self.store[index],
                    done: index >= self.store.length
                }
                index++
                return result
            }
        }
    }
}
for (let i of obj) {
    console.log(i)
}

迭代器模式

核心就是對(duì)外部提供統(tǒng)一的遍歷接口,讓外部不用關(guān)心數(shù)據(jù)內(nèi)部的結(jié)構(gòu)

let obj = {
    life: ['生活', '吃飯', '睡覺(jué)'],
    work: ['上班', '下班'],
    learn: ['js', 'java', 'python'],
    [Symbol.iterator]: function () {
        let all = [...this.life, ...this.work, ...this.learn]
        let index = 0
        return {
            next: function () {
                return {
                    value: all[index],
                    done: index++ >= all.length
                }
            }
        }
    }
}
for (let i of obj) {
    console.log(i)
}

Generator生成器

Generator的是避免異步編程中毀掉嵌套過(guò)深的問(wèn)題而提供的更好的異步編程解決方案,Generator內(nèi)部也實(shí)現(xiàn)了迭代器接口。
yield關(guān)鍵詞并不會(huì)結(jié)束掉當(dāng)前方法的執(zhí)行,而是暫停執(zhí)行
Generator會(huì)返回一個(gè)生成器對(duì)象,調(diào)用生成器對(duì)象的next方法才會(huì)讓函數(shù)體開(kāi)始執(zhí)行,遇到y(tǒng)ield關(guān)鍵詞會(huì)暫停后續(xù)代碼的執(zhí)行,yield后面的值會(huì)作為next的結(jié)果返回

es2016

Array.prototype.includes

indexOf方法不能查找NaN,includes可以

console.log([NaN].indexOf(NaN));  // -1
console.log([NaN].includes(NaN)); // true

指數(shù)運(yùn)算符

// es5
console.log(Math.pow(2, 10)) // 2底數(shù) 10指數(shù)
// es6
console.log(2 ** 10) // 2底數(shù) 10指數(shù)

es2017

Object.values

// 返回對(duì)象值的數(shù)組
let obj = {
    a: 1,
    b: 2
}
console.log(Object.values(obj)) // [ 1, 2 ]

Object.entries

// 返回以數(shù)組的方式返回鍵值對(duì)
let obj = {
    a: 1,
    b: 2
}
console.log(Object.entries(obj)) // [ [ 'a', 1 ], [ 'b', 2 ] ]

Object.getOwnPropertyDescriptors

// 獲得對(duì)象的完整描述信息
let obj = {
    a: 1,
    b: 2
}
console.log(Object.getOwnPropertyDescriptors(obj))
/*
{
  a: { value: 1, writable: true, enumerable: true, configurable: true },
  b: { value: 2, writable: true, enumerable: true, configurable: true }
}
*/

String.prototype.padStart、String.prototype.padEnd

用給定字符串去填充目標(biāo)字符串開(kāi)始或結(jié)束的位置,直到達(dá)到指定長(zhǎng)度為止

性能優(yōu)化

所有提高運(yùn)行效率,降低運(yùn)行開(kāi)銷的行為,都可以稱作性能優(yōu)化
如請(qǐng)求資源時(shí)用到的網(wǎng)絡(luò),數(shù)據(jù)的傳輸方式,框架等

內(nèi)存管理

開(kāi)發(fā)者人為的去操作一片空間的申請(qǐng)、使用和釋放

內(nèi)存

由可讀寫(xiě)單元組成,表示一片可操作空間

// 申請(qǐng)一片內(nèi)存空間
let obj = {}
// 使用內(nèi)存
obj.name = 'abc'
// 釋放內(nèi)存
obj = null

垃圾回收

JavaScript中內(nèi)存管理是自動(dòng)的,當(dāng)對(duì)象不再被引用或者不能從根上訪問(wèn)到時(shí),就會(huì)被看作垃圾,垃圾回收機(jī)制就會(huì)把它們占用的空間進(jìn)行回收

可達(dá)對(duì)象

可以訪問(wèn)到的對(duì)象就是可達(dá)對(duì)象(引用、作用域鏈)

GC算法

常見(jiàn)的GC算法包括引用計(jì)數(shù)、標(biāo)記清楚、標(biāo)記整理、分代回收

引用計(jì)數(shù)算法

設(shè)置引用數(shù),判斷當(dāng)前引用數(shù)是否為0,當(dāng)引用關(guān)系發(fā)生改變時(shí)引用計(jì)數(shù)器就會(huì)修改引用數(shù)字,引用數(shù)字為0時(shí)立即回收
優(yōu)點(diǎn):

  • 發(fā)現(xiàn)垃圾立即回收
  • 最大程度較少程序暫停 ()

缺點(diǎn):

  • 無(wú)法回收循環(huán)引用的對(duì)象
function fn () {
    let obj1 = {}
    let obj2 = {}
    obj1.name = obj2
    obj2.name = obj1
}
  • 時(shí)間開(kāi)銷大,引用計(jì)數(shù)需要維護(hù)一個(gè)數(shù)值的變化,所以時(shí)刻監(jiān)控著當(dāng)前對(duì)象的引用數(shù)值是否需要修改,本身數(shù)值修改就需要消耗時(shí)間,如果內(nèi)存中有更多的對(duì)象需要修改

標(biāo)記清除算法

遍歷所有對(duì)象找到活動(dòng)對(duì)象進(jìn)行標(biāo)記,然后遍歷所有對(duì)象將**沒(méi)有標(biāo)記的對(duì)象清除,把回收的空間放到空間鏈表中,方便后續(xù)程序直接申請(qǐng)使用,同時(shí)抹去所有標(biāo)記以便下一輪GC正常工作
優(yōu)點(diǎn):

  • 可以解決循環(huán)引用對(duì)象的回收的問(wèn)題

缺點(diǎn):

  • 空間碎片化
  • 不會(huì)立即回收垃圾對(duì)象

標(biāo)記整理算法

標(biāo)記整理可以看做是標(biāo)記清除的增強(qiáng),標(biāo)記階段和標(biāo)記清除一致,清理階段會(huì)先執(zhí)行整理,移動(dòng)對(duì)象的位置讓它們?cè)诘刂飞线B續(xù)

V8引擎

V8采用即時(shí)編譯,內(nèi)存設(shè)有上線,64位1.5G,32位800M

V8垃圾回收

采用分代回收,將內(nèi)存分為新生代和老生代,針對(duì)不同對(duì)象采用不同算法, V8中常用的GC算法包括分代回收,空間復(fù)制,標(biāo)記清除,標(biāo)記整理,標(biāo)記增量

回收新生代

V8將內(nèi)存空間一分為二,小空間用于存儲(chǔ)新生代對(duì)象(64位32M,32位16M),新生代指的是存活時(shí)間較短的對(duì)象
回收過(guò)程采用復(fù)制算法 + 標(biāo)記整理,新生代內(nèi)存分為兩個(gè)等大小空間,使用空間為From,空閑空間為To,活動(dòng)對(duì)象存儲(chǔ)于From空間,標(biāo)記整理后將活動(dòng)對(duì)象拷貝至To, from與to交換空間,完成釋放
拷貝過(guò)程中可能出現(xiàn)晉升,就是將新生代對(duì)象移動(dòng)至老生代, 一輪GC還存活的新生代需要晉升,當(dāng)To空間的使用率超過(guò)25%也需要晉升

回收老生代

老生代指的是存活時(shí)間較長(zhǎng)的對(duì)象,至64位1.4G, 32位700M
主要采用標(biāo)記清除、標(biāo)記整理、增量標(biāo)記算法
首先使用標(biāo)記清除完成垃圾空間回收, 采用標(biāo)記整理進(jìn)行空間優(yōu)化

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容