本文篇幅較長,有興趣的能夠先收藏再看。本文將重要的 ES6 特性介紹了一遍,而且詳細解釋了一些重難點。git
let
與 var
的聲明用法相同,可是多了一個臨時死區(Temporal Distonrtion Zone)的概念。github
console.log(a) // -> undefined
var a = 1
console.log(b) // -> Uncaught ReferenceError: b is not defined
let b = 1複製代碼
能夠發如今聲明前使用 let
聲明的變量會致使報錯,這解決了 JS 不少奇怪的問題。而且使用 let
會生成一個塊級做用域,做用域外不能訪問該變量。編程
{
let a = 1;
var b = 1;
}
console.log(b); // -> 1
console.log(a); // -> Uncaught ReferenceError: b is not defined複製代碼
在 JS 中,聲明變量都會提高,不論用什麼關鍵字聲明。當使用 let
時變量也會被提高至塊級做用域的頂部,可是隻提高聲明,不提高初始化。而且會產生臨時死區,該區域會存放變量,直到執行過聲明語句後,方可以使用該變量。json
在循環中 let
會與前面有些不一樣,每次迭代都會產生一個新的變量,並用以前的值初始化,如何理解這句話呢,請看如下代碼。數組
for(let i = 0; i < 10; i++) {
console.log(i) // -> 輸入 0 - 9
}
// 上面的循環代碼能夠這樣看
{ // 造成塊級做用域
let i = 0
{
let ii = i
console.log(ii)
}
i++
{
let ii = i
console.log(ii)
}
i++
{
let ii = i
console.log(ii)
}
...
}複製代碼
const
與 let
基本相似,只是用 const
聲明必須賦值,而且不得修改綁定,什麼意思呢,請看代碼。promise
const a = 1;
a = 2 // -> Uncaught TypeError: Assignment to constant variable
// but
const b = {a: 1};
b.a = 2 // 起效複製代碼
固然了,有辦法讓這個不能改變app
const b = Object.freeze({a: 1})
b.a = 2 // 沒有報錯,可是 b.a 沒有被改變複製代碼
可是 Object.freeze
只能在這裏有效,對於數組這些能夠看看這個提案。ecmascript
這兩個新的聲明方式在全局做用域下不會自動加上
window
異步
let string = 'startend'
string.includes('a') // -> true 是否包含
string.endsWith('end') // -> true 是否由 end 結尾
string.startsWith('start') // -> true 是否由 start 開頭複製代碼
很棒的新功能,解決了以前不少麻煩的寫法。模塊化
// 語法就是 `` 代替以前的引號,在 `` 中使用引號不須要轉義
let s = `it's string`複製代碼
多行字符串
// 這樣在語法中就能夠換行了
let s = `start \ end`
// 注意在模板字面量中的任何空白符都是起效的
let s = `start \ end` // -> start end複製代碼
佔位符和標籤模板
let s = 'string'
let message = `start${s}` // -> startstring
// ${} 就是佔位符語法,能夠更簡便的實現字符串插入
// 定義一個 tag 函數,而後直接在 `` 前使用就能夠
let m = tag`s${s}e${message}`
// strings 是一個數組,value 是模板字面量中全部的佔位符的值
function tag(strings, ...value) {
// -> ['s', 'e', '']
console.log(strings)
// -> ['string', 'startstring']
console.log(value)
}
// 上面的 ...value 也是 ES6新出的擴展語句,在這裏表明不定參數的寫法,用於替換 arguments
// 不定參數使用上也是有限制的,必須放在全部參數的末尾,而且在每一個函數中只能聲明一次
// 擴展語句和 arguments 區別就是表明了 strings 參數後面的全部參數
// 除了上面的寫法,還能夠用於展開能夠迭代(有Symbol.iterator屬性)的對象
let array = [1, 2, 3]
console.log(...array)
// 該語法能夠解決以前不少地方只能傳入單個參數,只能使用 apply 解決的問題
Array.prototype.unshift.apply([4, 5], array) // -> [1, 2, 3, 4, 5]
// 如今能夠直接這樣寫
[4, 5].unshift(...array)
// 展開運算不受不定參數的條件限制,能夠一塊兒用複製代碼
ES6 容許給函數增長默認參數
function fn(a = 1, b = 2) {}
// 默認值也能夠經過調用函數得到,注意必須調用函數
function fn1(a = 1, b = fn()) {}複製代碼
在 JS 中,函數有多種用法,能夠直接調用,也能夠經過 new
構造函數。
在 ES6中,函數內部新增了 [[Call]] 和 [[Construct]] 兩個方法。後者會在使用 new
構造函數時執行,其餘狀況會執行前者方法。
當一個函數必須使用 new
構造時,你可使用這個新屬性 new.target
判斷
// new.target 只能在函數中使用
function Fn() {
if (typeof new.target === 'underfined') { throw ....... }
}複製代碼
這個特性真的很棒,先介紹下他的幾種語法
// 最簡單的寫法,只有一個參數,單行表達式
value => value
// 多個參數須要使用小括號包裹
(v1, v2) => v2 + v1
// 沒有參數須要使用小括號包裹
() => "balabala"
// 多行表達式須要大括號包裹
(v1, v2) => {
return v1 + v2
}
// 返回一個對象,須要用小括號包裹
() => ({a: 1})
// 當即執行函數,注意普通的當即執行函數的小括號包裹在最外面,箭頭函數不須要
((value) => value)("balabala")複製代碼
箭頭函數和普通函數區別仍是蠻大的,說幾個經常使用的
this
,不能改變 this
綁定new
調用,固然也沒有原型arguments
對象,不能有相同命名參數箭頭函數雖然沒有 this
,可是仍是能夠在內部使用 this
的
this
的綁定取決於定義函數時的上下文環境this
的方法都無效// let 有個細節
let x = 11111
let a = {
x: 1,
init() {
// 箭頭函數的 this 取決於 init,因此能夠打印出 1
document.addEventListener('click', () => console.log(this.x))
},
allowInit: () => {
// allowInit 直接是個箭頭函數,因此這時的 this 變成了 window
// 可是並不會打印出 11111,忘了 let 的一個細節的能夠回到上面看看
console.log(this.x))
}
otherInit() {
// 普通函數的 this 取決於調用函數的位置,this 指向 document
// 若是想打印出 x,可使用 bind
document.addEventListener('click', function() {
console.log(this.x)
})
}
}
a.init() // -> 1
a.allowInit() // -> undefined
a.otherInit() // -> undefined複製代碼
let a = 1
// 當 key 和 value 名字相同時能夠簡寫
let b = { a }
// 對象中的方法也能夠簡寫
let a = {
init() {}
}
// 對象屬性名也能夠計算
let name = 'name'
b[name + '1'] = 2 // === b['name1'] = 2複製代碼
ES6 也新增了幾個對象方法
Object.is(NaN, NaN) // ->true
// 結果基本於 === 類似,除了 NaN 和 +0 -0
Object.is(+0, -0) // -> false
let o = {a: 1}
let a = Object.assign({}, o) // -> {a: 1}
// 第一個參數爲目標參數,後面的參數是不定的,參數屬性名若是有重複,後面的會覆蓋以前的複製代碼
原型相關
ES6 以前改變對象原型很麻煩
let obj = {a: 1}
let obj1 = {a: 2}
// 已 obj 爲原型
let a = Object.create(obj)
// 改變 a 的原型爲 obj1
Object.setPrototypeOf(a, obj1) // a.a === 2複製代碼
訪問原型
Object.getPrototypeOf(a) // 訪問原型
// ES6 中能夠直接經過 super 表明原型
let a = {
init() {
return 'Hello'
}
}
let b = {
init() {
// 不能在 super 以前訪問 this
return super.init() + 'World'
}
}
Object.setPrototypeOf(b, a)
b.init() // -> 'HelloWorld'複製代碼
可是 super 不是每一個函數均可以使用的,只有在函數的簡寫語法中方可以使用。由於在 ES6中新增了一個函數內部屬性 [[HomeObject]],這個屬性決定了是否能夠訪問到 super
。首先在該屬性上調用 Object.getPrototypeOf(綁定的對象)
,而後找到原型中的同名函數,在設置 this
綁定而且調用函數,其實就是一個新增的語法糖。
該特性能夠用於對象,數組和傳參。
let obj = {a: 1, b: 2}
// 對象解構使用 {},數組解構使用 [],由於這裏是對象解構,c 不是 obj 的屬性,因此 underfined
// 數組解構中,若是須要解構的變量大於數組索引,多出來的變量也是 undefined
// 解構必須賦值,不然報錯。不能 let {a, b, c};
// 賦值不能爲 null 或者 undefined,會報錯
let {a, b, c} = obj
// 等於 let a = obj.a,能夠看作以前介紹的對象屬性簡寫
console.log(a, b, c) // -> 1, 2, underfined
// 若是已經聲明瞭變量而且想使用解構,必須最外面是小括號
({a, b} = obj)
// 若是不想使用 obj 中的對象名,又想使用解構賦值
let {x: a} = obj
// 若是想使用默認值
let {a = 2, c= 3} = obj // -> 1, 3
// 由於 a 是 obj 中的對象,因此默認值被覆蓋
// 解構也能夠嵌套
let obj = {data: {code: 1}, message: [1, 2]}
// 這個寫法在 json 中很好用
// 注意在這個寫法中,data 和 message 都是指代了 obj 的屬性,並無被聲明變量
let { data: {code}, message: [a] } = obj
console.log(code, a)
// 數組解構和對象解構基本類似,而且簡單多了
let message = [1, 2, 3, 4]
// 由於數組取值只能索引取,因此想跳過某幾個索引,就用逗號代替
// 一樣,數組解構也可使用默認值和嵌套解構,和對象解構如出一轍就不贅述了
let [a, , b] = message // -> 1, 3
// 在上面章節介紹了擴展語法,一樣也可使用在數組解構中
// 能夠看到 b 變成了一個數組
let [a, ...b] = message // -> 1, [2, 3, 4]
// 傳參使用解構可讓要傳的參數更加清晰
function fn(name, {key, value}) {
console.log(name, key, value)
}
// 使用,注意:傳參解構必須起碼傳入一個值,不然報錯
fn(1, {key: 2, value: 3})
// 由於傳參解構相似如下寫法
function fn(name, {key, value}) {
let {key, value} = null // 這個上面講過不能這樣寫
}複製代碼
ES6 新出的第六個原始類型。多用於避免代碼衝突,做爲一個私有屬性使用,不會被屬性遍歷出來。可使用 Object.getOwnPropertySymbols()
檢索 Symbol 屬性。
// 建立
let a = Symbol()
// 更推薦這種寫法,能夠更加明確這個Symbol的用途
// 而且有函數能夠經過這個字符串取到相應的Symbol
let b = Symbol('is b')
// 使用,通常做爲可計算屬性使用
let a = {}
let b = Symbol('is b')
a[b] = 1
// 能夠在全局註冊表中共享同一個 Symbol,但不推薦使用
// 不存在 is a 會自動建立
let a = Symbol.for('is a')複製代碼
Symbol 中預約義了一些 well-know Symbol,這些 Symbol 定義了一些語言的內部實現
instanceof
時檢測對象的繼承信息concat
函數時是否將數組展開toString
Set 是新增的無重複的有序集合,多用於集合去重或者判斷集合中是否含有某個元素。
// 建立
let set = new Set()
// 添加元素
set.add(1)
set.add('1')
// 重複的元素不會被添加
set.add(1)
// 判斷是否包含元素
set.has(1) // -> true
// 判斷長度
set.size() // -> 2
// 刪除某個元素
set.delete()
// 移除全部元素
set.clear()複製代碼
Map 是新增的有序鍵值對列表,鍵值能夠是任何類型。
// 建立
let map = new Map()
// 設置鍵值對
map.set('year', "2017")
map.set({}, 'obj')
// 取值
map.get('year') // -> '2017'
// 判斷是否有該鍵值
map.has('year') // -> true
// 得到長度
map.size() // -> 2
// 刪除某個鍵值
map.delete('year')
// 移除全部鍵值
map.clear()複製代碼
顧名思義,用來迭代的。以前介紹過 Symbol.iterator
,能夠迭代的對象都有這個屬性,包括數組,Set,Map,字符串和 NodeList。ES6新增的 for-of
就用到了迭代器的功能,可是默認只有上面這些對象能使用。
let a = [1, 2]
for (let value of a) {
console.log(value) // -> 1, 2
}
// 上面的代碼其實就是調用了數組的默認迭代器
let iterator = a[Symbol.iterator]()
// 當調用 next 時會輸出此次迭代的 value 和是否迭代完成
console.log(iterator.next()) // {value: 1, done: false}
console.log(iterator.next()) // {value: 2, done: false}
// 已經沒元素能夠迭代了
console.log(iterator.next()) // {value: undefined, done: true}
// 數組的默認迭代器只會輸出 value,若是想同時輸出索引的話
// 這裏可使用新特性數組解構 let [index, value]
for (let value of a.entries()) {
console.log(value) // -> [0, 1] [1, 2]
}複製代碼
對於本身建立的對象都是不可迭代的,固然咱們也可讓他變成迭代的
let a = {
array: [],
// 這是一個 Generator 函數,立刻就會講到
*[Symbol.iterator]() {
for(let item in this.array) {
yield item
}
}
}
a.array.push(...[1, 2, 3])
for(let item of a) {
console.log(item)
}複製代碼
用於異步編程。該函數能夠暫停和恢復執行,和同步寫法很像。
// 星號表示這是一個 Generator 函數
function *gen() {
// 第一次 next 只執行到等號右邊
let first = yield 1
// 第二次 next 執行 let first = 和 yield 2
let second = yield 2
// 不執行接下來的 next 就卡在上一步了
let thrid = yield 3
}
let g = gen()
g.next() // -> {value: 1, done: false}
g.next() // -> {value: 2, done: false複製代碼
接下來看下 Generator 函數如何用於異步
function getFirstName() {
setTimeout(function(){
gen.next('alex')
}, 1000);
}
function getSecondName() {
setTimeout(function(){
gen.next('perry')
}, 2000);
}
function *sayHello() {
var a = yield getFirstName();
var b = yield getSecondName();
// settimeout 原本是異步的,經過 Generator 函數寫成了同步寫法
console.log(a, b); // ->alex perry
}
var gen = sayHello();
gen.next();複製代碼
JS 中的類不是其餘語言中的類,只是個語法糖,寫法以下。
class Person {
// 構造函數
constructor() {
this.name = name
}
sayName() {
console.log(this.name)
}
}
let p = new Person('name')
p.sayName() // -> 'name'
// class 就是如下代碼的語法糖
// 對應 constructor
function Person(name) {
this.name = name
}
// 對應 sayName
Person.prototype.sayName = function() {
console.log(this.name)
}複製代碼
類聲明相比以前的寫法有如下幾點優勢
在 ES6 以前寫繼承很麻煩,既然有個類,那麼必然也能夠繼承類了
class Person {
// 構造函數
constructor() {
this.name = name
}
sayName() {
console.log(this.name)
}
}
// extends 表明繼承自Person
class Student extends Person {
constructor(name, age) {
// super 的注意事項以前有說過
super(name)
// 必須在 super 以後調用 this
this.age = age
}
sayName() {
// 若是像使用父類的方法就使用這個方法使用
// 不像使用的話就不寫 super,會覆蓋掉父類的方法
super.sayName(this.name)
console.log(this.age)
}
}複製代碼
用於異步編程。
// 你可使用 new 建立一個 Promise 對象
let promise = new Promise(function(resolve, reject)) {}
resole() // 表明成功
reject() // 表明失敗
promise.then(onFulfilled, onRejected) // 當調用 resole 或者 reject ,then 能夠監聽到
promise.catch() // reject 或者 throw時能夠監聽到複製代碼
Promise 有三個狀態
function delay() {
// 建立一個 promise
return new Promise((resolve, reject) => {
// 當調用 promise 時,裏面的內容會當即執行
console.log('in delay')
setTimeout(() => {
resolve(1)
}, 1000)
});
}
function otherDelay() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 1000)
});
}
// 這裏會先輸出 delay 函數中的 log,而後再輸出 outer,接下來1秒之後輸出3個1
delay()
// then 能夠捕獲 resolve 和 reject
.then((value) => {
console.log(value)
})
console.log('outer')
otherDelay()
// 捕獲 reject時,若是不須要捕獲 resolve 時能夠這樣寫
.then(null, (value) => {
console.log(value)
})
// 捕獲 reject 或者 throw 時推薦使用這個寫法,緣由後面會說
otherDelay()
.catch((value) => {
console.log(value);
})複製代碼
以上是最經常使用的 Promise 寫法,如今介紹 Promise 鏈
delay()
// then 會返回一個新的 promise 對象
.then((value) => {
// 這樣就能夠傳參了
return value + 1
}).then((value) => {
console.log(value) // -> 2
// then 裏面能夠也能夠傳入一個函數名,會自動調用
// 若是傳入的函數有參數會自動傳入
}).then(delay).then((value) => {
console.log(value) // -> 1
// 若是在then 中拋出錯誤,只有 catch 才能監聽到,因此推薦使用 catch 監聽錯誤
throw new Error('error')
}).then((value) => {
console.log(value) // 這個then 不會執行
}).catch((error) => {
console.log('catch' + error) // -> catch Error
})複製代碼
開發中可能會有需求,須要一次上傳幾張圖片,所有上傳成功之後有個提示,這時候就能夠用到 Promise.all()
function updateOne() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('one')
}, 1000)
});
}
function updateTwo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('two')
}, 2000)
});
}
// all 函數接收一個可迭代對象,注意這裏傳入函數必須調用
let promise = Promise.all([updateOne(), updateTwo()])
// 只有當 all 中的異步所有完成了纔會調用 then
promise
.then((value) => {
// value 是個函數,順序按照 all 裏的迭代對象的順序
console.log(value) // -> ["one", "two"]
})複製代碼
若是一個異步任務超時了,你想直接取消,能夠經過 Promise.race()
// 假設該任務執行時間超過1秒就算超時,應該 cancel
function delay() {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve('finish')
}, 1500);
});
}
function cancel() {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve('cancel')
}, 1000);
});
}
// 接收的參數和 all 相同
let promise = Promise.race([delay(), cancel()])
// race 中只要有一個任務完成,then 就會被調用,這樣就能夠 cancel 掉全部超時任務
promise
.then((value) => {
console.log(value) // -> cancel
})複製代碼
Proxy 能夠建立一個代替目標對象的代理,攔截語言內部的操做。
let handle = {}
let target = {}
// 這樣就建立了target 對象的代理,可是這個代理其實沒有任何用處
let p = new Proxy(target, handle)複製代碼
上面的代碼中能夠看到傳入了一個 handle
的對象,只有當這個對象中包含一些代理行爲的函數時,這個代理纔有用。具備的代理行爲函數能夠去 MDN查看,這裏舉例幾個用法。
let handle = {
// 改變 set 的內部操做
set(target, key, value) {
// 當給 age 屬性賦值小於19時報錯
console.log(value)
if (key === 'age') {
if (value < 19) {
throw new Error('未成年')
}
}
}
}
let target = {}
let p = new Proxy(target, handle)
p.age = 1 // -> 報錯
p.age = 19 // -> 沒問題複製代碼
ES6 引入了原生的模塊化,這樣就能夠拋棄以前的 AMD 或者 CMD 規範了,若是對模塊化還沒什麼瞭解,能夠看下我以前的文章 明白 JS 模塊化。
// example.js 文件下
// export 能夠導出任何變量,函數或者類
export var age = 14
export function sum(n1, n2) {
return n1 + n2
}
export class Person {
constructor(age) {
this.age = age
}
}
// 別的 JS 文件中導入
// 若是想導入整個模塊而且本身命名,就能夠這樣使用
// import 後面表明模塊名,from 後面表明要導入的文件地址
import * as Example from './example'
console.log(Example.age) // -> 14
// 固然你也能夠只使用模塊中的一個功能
// 這裏使用了對象解構的方法拿到須要的功能,注意這裏名字必須相同,不然使用會報錯
import { age, sum } from './example'
console.log(age) // -> 14
console.log(sum(1, 2)) // -> 3
// 如今我只想導出一個功能,而且外部能夠隨便命名該如何作呢?
// default 一個文件中只能使用一次
export default var age = 14
// MyAge 能夠隨便本身喜歡命名
import MyAge from './example'
console.log(MyAge) // -> 14複製代碼
以上就是本文的所有內容了,感謝你們能看到這裏,若是有任何錯誤或者解釋的不明白的,能夠留言回覆,謝謝!