ES6(ESMAScript 6, ESMAScript2015)
是指ESMA
組織在2015
年發佈的ESMAScript 2015
標準,因爲相比ES5
(2009年發佈),ES6
的改動很是大,故廣義上,咱們也將ES6
統稱全部ES5
以後的新特性,包括ES7
(ESMAScript2016
),ES8
(ESMAScript2017
)...ES10
(ESMAScript2019
)等,這裏專指ES6
。編程
var
,let
與const
咱們知道在ES5
中,用var
關鍵字聲明的變量存在變量提高,函數及變量的聲明都將被提高到函數的最頂部,這就致使能夠在使用以後在聲明變量,這種不規範的行爲不少時候都依靠編程習慣來約束,尤爲對於新手來講存在很多隱患,因而let
與const
應運而生,兩者的做用域都是當前代碼塊域,let
聲明的變量能夠修改,const
聲明的是常量,聲明後不可修改。數組
let foo = 10;
const bar = {key: 20};
foo = 20;
bar.key = 30;
bar = 30; // TypeError
複製代碼
這裏解釋一下bar.key = 30
這句話改變的不是bar
的值,只是修改了其中的屬性,就比如古代的女人嫁雞隨雞、從一而終,後來這隻雞變了,那也只是變成了一隻「大雞」,但雞仍是這隻雞……app
在JavaScript
中,咱們總能聽到這樣的話:在JavaScript
中只有函數做用域,沒有塊級做用域。那麼他們究竟是什麼呢?async
以一個function
聲明的代碼塊是一個函數做用域,以下代碼塊中的scope 1
和scope 2
就是同一個函數做用域,但他們是兩個不一樣的代碼塊函數
function fn(){
console.log(foo1); // undefined
console.log(bar1); // undefined
// scope 1
var foo1 = 10;
let foo2 = 20;
if(true) {
// scope 2
var bar1 = 30;
let bar2 = 40;
}
console.log(foo1); // 10
console.log(foo2); // 20
console.log(bar1); // 30
console.log(bar2); // ReferenceError
}
複製代碼
咱們能夠看出用let
聲明的變量bar2
在if
語句外是不能訪問的(ReferenceError
引用錯誤,表明引用了一個未聲明的變量)ui
在上面的例子中第一句console.log(bar1);
輸出了undefined
,並未報錯,說明用var
聲明的變量在函數開始時就已經定義(即變量提高),但須要注意的是此時並未初始化。但採用let
和const
聲明的變量則不存在變量提高,必須先聲明後使用,不然就會報錯:ReferenceError
,同時咱們把這種在聲明以前不可以使用這種特性成爲 暫時性死區(Temproal Dead Zone, TDZ)。this
console.log(foo); // ReferenceError
let foo = 10;
複製代碼
var foo = 10;
var foo = 20;
let bar = 20;
let bar = 20; // SyntaxError
複製代碼
當咱們用var
聲明變量的時候,是能夠重複聲明的,後聲明的會覆蓋前面的聲明,但當咱們用let
或const
聲明的時候則會報語法錯誤SyntaxError
,注意,function
聲明的函數也和var
有類似的表現:存在 變量提高 和 重複聲明。spa
解構顧名思義按照必定的結構「解析」一個對象,經過這種方式咱們能夠從數組或對象中取值,本質上屬於 模式匹配,這是ES6
給咱們提供的新語法,只要等號兩邊的模式相同便可解析相應的值。prototype
// 解構一個對象,能夠只解構咱們須要的部分
const {foo, bar} = {foo: 1, bar: 2, other: 3};
console.log(foo, bar); // 1 2
// 解構一個數組
const [first, second] = [10, 20, 30];
console.log(first, second); // 10 20
// 忽略數組的某個值
const [first, , third] = [10, 20, 30];
console.log(first, third); // 10 30
// 解構一個不存在的鍵時,其值爲 undefined
const {foo, bar} = {foo: 1}; // undefined
// 注意數組的 length
const [length] = [12];
console.log(length); // 12
// 用方括號可花括號解析的差異
const {length} = [12];
console.log(length); // 1
// 當屬性中包含關鍵字或保留字時須要重命名
const {class: clazz} = {class: 'class-name'};
// 動態匹配,一樣須要重命名
const key = 'thisKey';
const {[key]: value} = {thisKey: 23};
console.log(value); // 23
// 一樣動態匹配也可用在對象聲明中
const key = 'currentKey';
const obj = {[key]: 20};
console.log(obj); // {currentKey: 20}
// 用解構賦值交換兩個變量,不須要聲明第三個變量
let foo = 10;
let bar = 20;
console.log(foo, bar); // 10 20
[foo, bar] = [bar, foo];
console.log(foo, bar); // 20 10
複製代碼
在使用解構賦值的時候,可使用擴展運算符...
來將剩餘值統一放入一個對象中,rest
// 用於對象
const {foo, ...rest} = {foo: 1, bar: 2, other: 3};
console.log(foo, rest); // 1 {bar: 2, other: 3}
// 用於數組
const [first, ...others] = [1, 2, 3];
console.log(first, others); // 1 [2, 3]
// 函數調用中傳遞參數
function callback(first, second){
console.log(first, second);
}
let params = [1, 2];
callback(...params); // 輸出 1 2
// 用於獲取 arguments,這種特性主要用於箭頭函數
const arrow = (...args) => {
console.log(args);
};
arrow(1, 2, 3); // 輸出數組 [1, 2, 3]
複製代碼
擴展運算符其實是調用了Iterator
接口,一般的Array
,String
,NodeList
,Arguments
,Set
,Map
都實現了這個接口,因此可經過擴展運算符得到arguments
,也可用於函數調用時傳遞參數,但Object
並未實現Iterator
,因此當咱們調用fn(...obj)
時,會報錯TypeError
。
在咱們使用解構賦值的時候,能夠爲不存在的值或值爲undefined
賦默認值
// 不存在指定映射
const {foo, bar = 20} = {foo: 10};
console.log(foo, bar); // 10 20
// 不存在指定值爲 undefined
const {foo, bar = 20} = {foo: 10, bar: undefined};
console.log(foo, bar); // 10 20
複製代碼
只有爲undefined
時纔會執行默認值賦值步驟
const {foo, bar = (function(){
console.log('默認值');
return 20;
})()} = {foo: 10};
// 默認值
console.log(foo, bar); // 10 20
function defaultVal(){
console.log('默認值');
return 20;
}
const {key1, key2 = defaultVal()} = {key1: 10, key2: null};
console.log(key1, key2); // 10 null
// 可見 key2 爲 null,並未輸出 「默認值」
複製代碼
在聲明函數時,咱們也能夠爲函數參數賦默認值,一樣只有在傳入參數爲undefined
時,才執行默認值賦值操做
function fn(foo, bar = 20){
console.log(foo, bar);
}
fn(10); // 10 20
複製代碼
ES6
給咱們帶來了新的函數語法箭頭函數
,在日常的書寫中,極大的提升了咱們的開發效率,可是箭頭函數和普通函數卻有着很大的差異
// 基本使用
const arrow = () => {};
// 有一個參數
const arrow = (arg) => {};
const arrow = arg => {}; // 括號可省略
// 有多個參數
const arrow = (arg1, arg2, arg3) => {}
// 函數體只有一句
const arrow = () => 1; // 花括號可省略
// 等價於
const arrow = () => {return 1;};
// 函數體有多句
const arrow = arg => {
if(arg){
return 1;
} else {
return 2;
}
};
// 返回一個空對象
// 這實際上只有一句,但括號不能省略,不然會被解析成代碼塊
const arrow = () => ({});
複製代碼
this
箭頭函數的this
是和聲明時的父做用域綁定的,不可經過其餘方式(call
,apply
,bind
)更改this
指向,如:
'use strict';
function fn1(){
console.log(this.foo);
return function() {
console.log(this.foo)
}
}
const obj = {foo: 10};
const bar = fn1.call(obj); // 10
bar(); // TypeError(由於此時 this 指向 undefined)
const changedObj = {foo: 60};
bar.call(changedObj); // 60 (this 能夠從新綁定)
複製代碼
若是是箭頭函數:
'use strict';
function fn1(){
console.log(this.foo);
return () => {
console.log(this.foo)
}
}
const obj = {foo: 10};
const bar = fn1.call(obj); // 10
bar(); // 10 (此時 this 仍然指向 obj)
obj.foo = 20;
bar(); // 20
const changedObj = {foo: 60};
bar.call(changedObj); // 20
複製代碼
new
const foo = () => {};
const bar = function(){
}
new bar(); // bar {}
new foo(); // TypeError
複製代碼
arguments
箭頭函數沒有arguments
,但可使用上面的解構語法得到 參數列表,但獲得的不是Arguments
對象,而是一個包含全部參數的數組
const arrow = (...args) => {
console.log(args);
}
arrow(1, 2, 3); // [1, 2, 3]
複製代碼
class
語法在ES5
中,只能經過Function.prototype
來實現類的聲明和繼承,ES6
提供了新的語法來聲明一個類,使其更加貼合面向對象編程,
// ES5
function Student(){}
Student.staticMethod = function(){
// ES5 的靜態方法
}
Student.prototype.memberMethod = function(){
// ES5 的實例方法
}
// ES6
class Person {
constructor(name) {
this.name = name;
this.memberMethod = this.memberMethod.bind(this);
}
static staticMethod(){
console.log('這是一個靜態方法');
}
static memberMethod(){
// 注意實例方法 ES6 並未綁定 this
// React 中也建議了,在構造器中應手動綁定 this
// 如上
console.log('這是實例方法');
}
}
複製代碼
(相比以上,這是大概是最複雜的,下一篇將專門將這一特性,後續還會介紹ES7
的async
,await
,他們都是依賴Promise
,因此……很重要!)