標籤:

工作中常用到的ES6語法

點擊上方「

程序員大咖

」,選擇「置頂公眾號」

關鍵時刻,第一時間送達!

來自:沉靜地閃光

鏈接:https://segmentfault.com/a/1190000016068235

程序員大咖整理髮布,轉載請聯繫作者獲得授權

一、let和const

在JavaScript中咱們以前主要用關鍵var來定義變數,ES6之後,新增了定義變數的兩個關鍵字,分別是let和const。

對於變數來說,在ES5中var定義的變數會提升到作用域中所有的函數與語句前面,而ES6中let定義的變數則不會,let聲明的變數會在其相應的代碼塊中建立一個暫時性死區,直至變數被聲明。

let和const都能夠聲明塊級作用域,用法和var是類似的,let的特點是不會變數提升,而是被鎖在當前塊中。

一個非常簡單的例子:

function

 

test

() 

{

if

(

true

) {  

console

.log(a)

//TDZ,俗稱臨時死區,用來描述變數不提升的現象

  

let

 a = 

1

}}test()  

// a is not defined

function

 

test

() 

{    

if

(

true

) {      

let

 a = 

1

    }    

console

.log(a)}    test() 

// a is not defined

唯一正確的使用方法:先聲明,再訪問。

function

 

test

() 

{    

if

(

true

) {      

let

 a = 

1

      

console

.log(a)    }}test() 

// 1

const

聲明常量,一旦聲明,不可更改,而且常量必須初始化賦值。

const雖然是常量,不允許修改默認賦值,但如果定義的是對象Object,那麼可以修改對象內部的屬性值。

const

 

type

 = 

{  a: 1}

type

.a = 

2

 

//沒有直接修改type的值,而是修改type.a的屬性值,這是允許的。

console.log(

type

// {a: 2}

const和let的異同點

相同點:

const和let都是在當前塊內有效,執行到塊外會被銷毀,也不存在變數提升(TDZ),不能重複聲明。

不同點:

const不能再賦值,let聲明的變數可以重複賦值。

cons

t實際上保證的,

並不是變數的值不得改動,而是變數指向的那個內存地址所保存的數據不得改動。

對於簡單類型的數據(數值、字元串、布爾值),值就保存在變數指向的那個內存地址,因此等同於常量。但對於複合類型的數據(主要是對象和數組),變數指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即總是指向另一個固定的地址),至於它指向的數據結構是不是可變的,就完全不能控制了。因此,將一個對象聲明為常量必須非常小心。

塊級作用域的使用場景

除了上面提到的常用聲明方式,我們還可以在循環中使用,最出名的一道面試題:循環中定時器閉包的考題

在for循環中使用var聲明的循環變數,會跳出循環體污染當前的函數。

for

(

var

 i = 

0

; i < 

5

; i++) {  setTimeout(

()

 =>

 {    

console

.log(i) 

//5, 5, 5, 5, 5

  }, 

0

)}

console

.log(i) 

//5 i跳出循環體污染外部函數

//將var改成let之後

for

(

let

 i = 

0

; i < 

5

; i++) {  setTimeout(

()

 =>

 {    

console

.log(i) 

// 0,1,2,3,4

  }, 

0

)}

console

.log(i)

//i is not defined i無法污染外部函數

在實際開發中,我們選擇使用var、let還是const,取決於我們的變數是不是需要更新,通常我們希望變數保證不被惡意修改,而使用大量的const。使用const聲明,聲明一個對象的時候,也推薦使用const,當你需要修改聲明的變數值時,使用let,var能用的場景都可以使用let替代。

symbol

ES6 以前,我們知道5種基本數據類型分別是Undefined,Null,Boolean,Number以及String,然後加上一種引用類型Object構成了JavaScript中所有的數據類型,但是ES6出來之後,新增了一種數據類型,名叫symbol,像它的名字表露的一樣,意味著獨一無二,意思是每個 Symbol類型都是獨一無二的,不與其它 Symbol 重複。

可以通過調用 Symbol() 方法將創建一個新的 Symbol 類型的值,這個值獨一無二,不與任何值相等。

var

 mySymbol=

Symbol

();

console

.log(

typeof

 mySymbol) 

//"symbol"

二、字元串

ES6字元串新增的方法

UTF-16碼位:ES6強制使用UTF-16字元串編碼。關於UTF-16的解釋請自行百度了解。

codePointAt():

該方法支持UTF-16,接受編碼單元的位置而非字元串位置作為參數,返回與字元串中給定位置對應的碼位,即一個整數值。

String.fromCodePoiont():

作用與codePointAt相反,檢索字元串中某個字元的碼位,也可以根據指定的碼位生成一個字元。

normalize():

提供Unicode的標準形式,接受一個可選的字元串參數,指明應用某種Unicode標準形式。

在ES6中,新增了3個新方法。每個方法都接收2個參數,需要檢測的子字元串,以及開始匹配的索引位置。

模板字元串

字元串是JavaScript中基本類型之一,應該算是除了對象之外是使用最為頻繁的類型吧,字元串中包含了例如substr,replace,indexOf,slice等等諸多方法,ES6引入了模板字元串的特性,用反引號來表示,可以表示多行字元串以及做到文本插值(利用模板佔位符)。

// 以前的多行字元串我們這麼寫:

console.

log

(

"hello world 1hello cala"

);

// "hello world

// hello cala"

//有了模板字元串之後

console.

log

(`hello world

string

 text line 

2

`);

// "hello world

// hello cala"

可以用${}來表示模板佔位符,可以將你已經定義好的變數傳進括弧中,例如:

var

 name=

"cala"

;

var

 age=

22

;

console

.log(

`hello,I"am 

${name}

,my age is 

${age}

`

)

//hello,I"am cala,my age is 22

includes(str, index):

如果在字元串中檢測到指定文本,返回true,否則false。

let

 t = 

"abcdefg"

if

(t.includes(

"cde"

)) {  

console

.log(

2

)}

//true

startsWith(str, index):

如果在字元串起始部分檢測到指定文本,返回true,否則返回false。

let

 t = 

"abcdefg"

if

(t.startsWith(

"ab"

)) {  

console

.log(

2

)}

//true

endsWith(str, index):

如果在字元串的結束部分檢測到指定文本,返回true,否則返回false。

let

 t = 

"abcdefg"

if

(t.endsWith(

"fg"

)) {  

console

.log(

2

)}

//true

如果你只是需要匹配字元串中是否包含某子字元串,那麼推薦使用新增的方法,如果需要找到匹配字元串的位置,使用indexOf()。

三、函數

函數的默認參數

在ES5中,我們給函數傳參數,然後在函數體內設置默認值,如下面這種方式。

function

 

a

(

num, callback

{  num = num || 

6

  callback = callback || 

function

 (

data

{

console

.log(

"ES5: "

, data)}  callback(num * num)}a() 

//ES5: 36,不傳參輸出默認值

//你還可以這樣使用callback

a(

10

function

(

data

{  

console

.log(data * 

10

// 1000, 傳參輸出新數值

})

在ES6中,我們使用新的默認值寫法

function

 

a

(

num = 

6

, callback = function (data

{

console

.log(

"ES6: "

, data)}) {  callback(num * num)}a() 

//ES6: 36, 不傳參輸出默認值

a(

10

function

(

data

{  

console

.log(data * 

10

// 1000,傳參輸出新數值

})

四、箭頭函數(=>)

(箭頭函數比較重要,現在簡單提一下,遲一點有空專門寫一篇箭頭函數的文章。)

const

 arr = [

5

10

]

const

 s = arr.reduce(

(

sum, item

) =>

 sum + item)

console

.log(s) 

// 15

箭頭函數中this的使用跟普通函數也不一樣,在JavaScript的普通函數中,都會有一個自己的this值,主要分為:

普通函數:

1、函數作為全局函數被調用時,this指向全局對象2、函數作為對象中的方法被調用時,this指向該對象3、函數作為構造函數的時候,this指向構造函數new出來的新對象4、還可以通過call,apply,bind改變this的指向

箭頭函數:

1、箭頭函數沒有this,函數內部的this來自於父級最近的非箭頭函數,並且不能改變this的指向。2、箭頭函數沒有super3、箭頭函數沒有arguments4、箭頭函數沒有new.target綁定。5、不能使用new6、沒有原型7、不支持重複的命名參數。

箭頭函數的簡單理解

1、箭頭函數的左邊表示輸入的參數,右邊表示輸出的結果。

const

 s = 

a

 =>

 a

console

.log(s(

2

)) 

// 2

2、在箭頭函數中,this屬於詞法作用域,直接由上下文確定,對於普通函數中指向不定的this,箭頭函數中處理this無疑更加簡單,如下:

//ES5普通函數

function

 

Man

()

{  

this

.age=

22

;  

return

 

function

()

{    

this

.age+

1

;  }}

var

 cala=

new

 Man();

console

.log(cala())

//undefined

//ES6箭頭函數

function

 

Man

()

{  

this

.age=

22

;  

return

 

()

 =>

 

this

.age+

1

;}

var

 cala=

new

 Man();

console

.log(cala())

//23

3、箭頭函數中沒有arguments(我們可以用rest參數替代),也沒有原型,也不能使用new 關鍵字,例如:

//沒有arguments

var

 foo=

(

a,b

)=>

{

return

 

arguments

[

0

]*

arguments

[

1

]}

console

.log(foo(

3

,

5

))

//arguments is not defined

//沒有原型

var

 Obj = 

()

 =>

 {};

console

.log(Obj.prototype); 

// undefined

//不能使用new 關鍵字

var

 Obj = 

()

 =>

 {

"hello world"

};

var

 o = 

new

 Obj(); 

// TypeError: Obj is not a constructor

4、箭頭函數給數組排序

const

 arr = [

10

50

30

40

20

]

const

 s = arr.sort(

(

a, b

) =>

 a - b)

console

.log(s) 

// [10,20,30,40,50]

尾調用優化

尾調用是指在函數return的時候調用一個新的函數,由於尾調用的實現需要存儲到內存中,在一個循環體中,如果存在函數的尾調用,你的內存可能爆滿或溢出。

ES6中,引擎會幫你做好尾調用的優化工作,你不需要自己優化,但需要滿足下面3個要求:

1、函數不是閉包2、尾調用是函數最後一條語句3、尾調用結果作為函數返回

尾調用實際用途——遞歸函數優化

在ES5時代,我們不推薦使用遞歸,因為遞歸會影響性能。

但是有了尾調用優化之後,遞歸函數的性能有了提升。

//新型尾優化寫法

"use strict"

;  

function

 

a

(

n, p = 

1

{  

if

(n <= 

1

) {    

return

 

1

 * p  }  

let

 s = n * p  

return

 a(n - 

1

, s)}

//求 1 x 2 x 3的階乘

let

 sum = a(

3

)

console

.log(sum) 

// 6

五、ES6對象新增方法

Object.assign()

Object.assign()方法用於將所有可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。

Object.assign 方法只會拷貝源對象自身的並且可枚舉的屬性到目標對象。該方法使用源對象的[[Get]]和目標對象的[[Set]],所以它會調用相關 getter 和 setter。因此,它分配屬性,而不僅僅是複製或定義新的屬性。如果合併源包含getter,這可能使其不適合將新屬性合併到原型中。為了將屬性定義(包括其可枚舉性)複製到原型,應使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。

String類型和 Symbol 類型的屬性都會被拷貝。

合併對象

var

 o1 = { 

a

1

 };

var

 o2 = { 

b

2

 };

var

 o3 = { 

c

3

 };

var

 obj = 

Object

.assign(o1, o2, o3);

console

.log(obj); 

// { a: 1, b: 2, c: 3 }

console

.log(o1);  

// { a: 1, b: 2, c: 3 }, 注意目標對象自身也會改變。

合併具有相同屬性的對象

var

 o1 = { 

a

1

b

1

c

1

 };

var

 o2 = { 

b

2

c

2

 };

var

 o3 = { 

c

3

 };

var

 obj = 

Object

.assign({}, o1, o2, o3);

console

.log(obj); 

// { a: 1, b: 2, c: 3 }

六、Map和Set

Map和Set都叫做集合,但是他們也有所不同。Set常被用來檢查對象中是否存在某個鍵名,Map集合常被用來獲取已存的信息。

Set是有序列表,含有相互獨立的非重複值。

Array和Set對比

都是一個存儲多值的容器,兩者可以互相轉換,但是在使用場景上有區別。如下:Array的indexOf方法比Set的has方法效率低下Set不含有重複值(可以利用這個特性實現對一個數組的去重)Set通過delete方法刪除某個值,而Array只能通過splice。兩者的使用方便程度前者更優Array的很多新方法map、filter、some、every等是Set沒有的(但是通過兩者可以互相轉換來使用)

Object和Map對比

Object是字元串-值,Map是值-值Object鍵為string類型,Map的鍵是任意類型手動計算Object尺寸,Map.size可以獲取尺寸Map的排序是插入順序Object有原型,所以映射中有一些預設的鍵。可以理解為Map=Object.create(null)

Set操作集合

let

 

set

 = 

new

 Set()

// Set轉化為數組

let

 arr = Array.

from

(

set

)

let

 arr = [...

set

]

// 實例屬性(繼承自Set)

set

.constructor === Set 

set

.size 

// 操作方法

set

.

add

(

1

// 添加一個值

set

.delete(

1

//刪除一個值

set

.has(

1

//判斷是否有這個值(Array中的indexOf)

set

.clear() 

//清除所有值

// 獲取用於遍歷的成員方法(Set的遍歷順序就是插入順序)

set

.keys() 

// 返回鍵名的遍歷器

set

.values() 

// 返回鍵值得遍歷器

set

.entries() 

// 返回鍵值對的遍歷器

set

.forEach() 

// 循環遍歷每個值(和Array的方法一致)

for

 (

let

 key of 

set

.keys()){}

for

 (

let

 val of 

set

.values()){}

for

 (

let

 entry of 

set

.entries()){}

// 使用數組方法來處理set值

set

 = 

new

 Set(arr)

set

 = 

new

 Set([...

set

].map((x) => x = x * 

2

))

set

 = 

new

 Set([...

set

].filter((x) => x > 

2

))

Map的方法集合

let 

map

 = 

new

 Map()

// 實例屬性(繼承自Map)

map

.constructor === Map

map

.size

// 操作方法

map

.set(

1

,

2

)

map

.get(

1

)

map

.

delete

(

1

)

map

.has(

1

)

map

.clear()

// 遍歷方法

map

.keys()

map

.values()

map

.entries()

map

.forEach()

// Map和數組的轉換

map

 = 

new

 Map([[

"key"

,

"val"

],[

2

,

1

]]) 

// 要求雙成員數組

let arr = [...

map

]

// 值得注意的是Map的鍵是跟內存綁定的

map

.set([

1

], 

"s"

)

map

.get([

1

])let arr = [

1

]let arr1 = [

1

]

map

.set(arr, 

"s"

)

map

.get(arr)

map

.set(arr1, 

"s"

)

map

.get(arr1)

七、迭代器(Iterator)

1、entries() 返回迭代器:返回鍵值對

//數組

const

 arr = [

"a"

"b"

"c"

];

for

(

let

 v 

of

 arr.entries()) {  

console

.log(v)}

// [0, "a"] [1, "b"] [2, "c"]

//Set

const

 arr = 

new

 

Set

([

"a"

"b"

"c"

]);

for

(

let

 v 

of

 arr.entries()) {  

console

.log(v)}

// ["a", "a"] ["b", "b"] ["c", "c"]

//Map

const

 arr = 

new

 

Map

();arr.set(

"a"

"a"

);arr.set(

"b"

"b"

);

for

(

let

 v 

of

 arr.entries()) {  

console

.log(v)}

// ["a", "a"] ["b", "b"]

2、values() 返回迭代器:返回鍵值對的value

//數組

const

 arr = [

"a"

"b"

"c"

];

for

(

let

 v 

of

 arr.values()) {  

console

.log(v)}

//"a" "b" "c"

//Set

const

 arr = 

new

 

Set

([

"a"

"b"

"c"

]);

for

(

let

 v 

of

 arr.values()) {  

console

.log(v)}

// "a" "b" "c"

//Map

const

 arr = 

new

 

Map

();arr.set(

"a"

"a"

);arr.set(

"b"

"b"

);

for

(

let

 v 

of

 arr.values()) {  

console

.log(v)}

// "a" "b"

3、keys() 返回迭代器:返回鍵值對的key

//數組

const

 arr = [

"a"

"b"

"c"

];

for

(

let

 v 

of

 arr.keys()) {  

console

.log(v)}

// 0 1 2

//Set

const

 arr = 

new

 

Set

([

"a"

"b"

"c"

]);

for

(

let

 v 

of

 arr.keys()) {  

console

.log(v)}

// "a" "b" "c"

//Map

const

 arr = 

new

 

Map

();arr.set(

"a"

"a"

);arr.set(

"b"

"b"

);

for

(

let

 v 

of

 arr.keys()) {  

console

.log(v)}

// "a" "b"

雖然上面列舉了3種內建的迭代器方法,但是不同集合的類型還有自己默認的迭代器,在for of中,數組和Set的默認迭代器是values(),Map的默認迭代器是entries()。

for of循環解構

對象本身不支持迭代,但是我們可以自己添加一個生成器,返回一個key,value的迭代器,然後使用for of循環解構key和value。

const

 obj = {  

a

1

,  

b

2

,  *[

Symbol

.iterator]() {    

for

(

let

 i 

in

 obj) {      

yield

 [i, obj[i]]    }  }}

for

(

let

 [key, value] 

of

 obj) {  

console

.log(key, value)}

// "a" 1, "b" 2

字元串迭代器

const

 str = 

"abc"

;

for

(

let

 v 

of

 str) {  

console

.log(v)}

// "a" "b" "c"

ES6給數組添加了幾個新方法:find()、findIndex()、fill()、copyWithin()

1、find():

傳入一個回調函數,找到數組中符合當前搜索規則的第一個元素,返回它,並且終止搜索。

const

 arr = [

1

"2"

3

3

"2"

]

console

.log(arr.find(

n

 =>

 

typeof

 n === 

"number"

)) 

// 1

2、findIndex():

傳入一個回調函數,找到數組中符合當前搜索規則的第一個元素,返回它的下標,終止搜索。

const

 arr = [

1

"2"

3

3

"2"

]

console

.log(arr.findIndex(

n

 =>

 

typeof

 n === 

"number"

)) 

// 0

3、fill():

用新元素替換掉數組內的元素,可以指定替換下標範圍。

arr.fill(value, 

start

end

)

4、copyWithin():

選擇數組的某個下標,從該位置開始複製數組元素,默認從0開始複製。也可以指定要複製的元素範圍。

arr.copyWithin(target, start, end)

const

 arr = [

1

2

3

4

5

]console.

log

(arr.copyWithin(

3

)) 

// [1,2,3,1,2] 從下標為3的元素開始,複製數組,所以4, 5被替換成1, 2

const

 arr1 = [

1

2

3

4

5

]console.

log

(arr1.copyWithin(

3

1

)) 

// [1,2,3,2,3] 從下標為3的元素開始,複製數組,指定複製的第一個元素下標為1,所以4, 5被替換成2, 3

const

 arr2 = [

1

2

3

4

5

]console.

log

(arr2.copyWithin(

3

1

2

)) 

// [1,2,3,2,5] 從下標為3的元素開始,複製數組,指定複製的第一個元素下標為1,結束位置為2,所以4被替換成2

ES6中類class、Promise與非同步編程、代理(Proxy)和反射(Reflection)API,這幾塊內容比較複雜,以後有機會再詳細寫。

PS: 寫的太匆忙了,難免有錯漏的地方。

【點擊成為源碼大神】


推薦閱讀:

two ε are equal
初三英語學霸整理的語法筆記 太全了
漢語語法知識(一)
格律詩語法,修辭,布局。

TAG:工作 | 語法 |