目錄
- 前言
- 一、開發環境配置
- 1.babel
- 為啥需要babel?
- 如何配置babel?
- 驗證配置是否成功
- 2.webpack
- 為啥要使用WebPack?
- 如何配置webpack?
- 二、塊級作用域
- 三、陣列的擴展
- 1. Array.from() : 將偽陣列物件或可遍歷物件轉換為真陣列
- 2.Array.of(v1, v2, v3) : 將一系列值轉換成陣列
- 3.陣列實體的 find() 和 findIndex()
- 4.陣列實體的includes()
- 5.陣列實體的 entries(),keys() 和 values()
- 四、箭頭函式
- 1. 縮減代碼
- 2. 改變this指向
- 3.使用注意點
- 五、rest 引數
- 六、展開運算子
- 七、解構賦值----更方便的資料訪問
- 1.解構為何有用?
- 2.物件
- 3.陣列
- 4.注意點
- 八、模板字串(template string)
- 九、Class 和傳統建構式有何區別
- 對比在傳統建構式和 ES6 中分別如何實作類:
- 對比在傳統建構式和 ES6 中分別如何實作繼承:
- Class 和傳統建構式有何區別
- 十、Promise的基本使用和原理
- Promise原理
- Promise的使用流程
- 十一、Iterator 和 for...of 回圈
- 1.Iterator的作用:
- 2.原生具備iterator介面的資料(可用for of遍歷)
- 3.幾種遍歷方式比較
- 十二、ES6模塊化
- 后記
前言
ES6 雖提供了許多新特性,但我們實際作業中用到頻率較高并不多,根據二八法則,我們應該用百分之八十的精力和時間,好好專研這百分之二十核心特性,將會收到事半功倍的奇效!寫文章不容易,請大家多多支持與關注!

一、開發環境配置
這部分著重介紹:babel 編譯ES6語法,如何用webpack實作模塊化,
1.babel
為啥需要babel?
ES6 提供了許多新特性,但并不是所有的瀏覽器都能夠完美支持,下圖是各個瀏覽器對ES6兼容性一覽表(以export為例)

由上圖可知,有些瀏覽器對于ES6并不是很友好,針對 ES6 的兼容性問題,很多團隊為此開發出了多種語法決議轉換工具(比如babel,jsx,traceur 等),可以把我們寫的 ES6 語法轉換成 ES5,相當于在 ES6 和瀏覽器之間做了一個翻譯官,其中Babel是一個廣泛使用的轉碼器,可以將ES6代碼轉為ES5代碼,從而在現有環境執行,
如何配置babel?
·首先要先安裝node.js,運行npm init,然后會生成package.json檔案
·npm install --save-dev babel-core babel-preset-es2015 babel-preset-latest
·創建并配置.babelrc檔案//存放在專案的根目錄下,與node_modules同級
·npm install -g babel-cli
·babel-version
Babel的組態檔是.babelrc,存放在專案的根目錄下,該檔案用來設定轉碼規則和插件,具體內容如下:
//.babelrc檔案
{
"presets": ["es2015", "latest"],
"plugins": []
}
驗證配置是否成功
·創建./src/index.js
·內容:[1,2,3].map(item=>item+1);
·運行babel./src/index.js
運行后得到以下部分,說明已經成功配置了babel
"use strict";
[1, 2, 3].map(function (item) {
return item + 1;
});
2.webpack
為啥要使用WebPack?
現今的很多網頁其實可以看做是功能豐富的應用,它們擁有著復雜的JavaScript代碼和一大堆依賴包,模快化工具就應運而生了,其中webpack 功能強大深受人們喜愛,
Webpack的作業方式是:把你的專案當做一個整體,通過一個給定的主檔案(如:index.js),Webpack將從這個檔案開始找到你的專案的所有依賴檔案,使用loaders處理它們,最后打包為一個(或多個)瀏覽器可識別的JavaScript檔案,

如何配置webpack?
·npm install webpack babel-loader --save-dev
·創建并配置 webpack.config.js//webpack.config.js檔案與package.json同級
·配置 package.json中的scripts
·運行 npm start
//配置 webpack.config.js 針對.js結尾的檔案除了node_modules都用babel決議
module.exports = {
entry: './src/index.js',
output: {
path: __dirname,
filename: './build/bundle.js'
},
module: {
rules: [{
test: /.js?$/,
exclude: /(node_modules)/,
loader: 'babel-loader'
}]
}
}
//配置 package.json中的scripts
"scripts": {
"start": "webpack",
"test": "echo "Error: no test specified" && exit 1"
}
二、塊級作用域
ES5 只有全域作用域和函式作用域(例如,我們必須將代碼包在函式內來限制作用域),這導致很多問題:
情況1:內層變數覆寫外層變數
var tmp = new Date();
function f() {
console.log(tmp); //undefined
if (false) {
var tmp = "hello world";
}
}
情況2:變數泄露,成為全域變數
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
ES6 提供 let 和 const 來代替 var 宣告變數,新的宣告方式支持用大括號表示的塊級作用域,這會帶來一些好處:
1.不再需要立即執行的函式運算式(IIFE)
在 ES5 中,我們需要構造一個立即執行的函式運算式去保證我們不污染全域作用域,在 ES6中, 我們可以使用更簡單的大括號({}),然后使用 const 或者 let 代替 var 來達到同樣的效果,
2.回圈體中的閉包不再有問題
在 ES5 中,如果回圈體內有產生一個閉包,訪問閉包外的變數,會產生問題,在 ES6,你可以使用 “let” 來避免問題,

3.防止重復宣告變數
ES6 不允許在同一個作用域內用 let 或 const 重復宣告同名變數,這對于防止在不同的 js 庫中存在重復宣告的函式運算式十分有幫助,
三、陣列的擴展
1. Array.from() : 將偽陣列物件或可遍歷物件轉換為真陣列
如果一個物件的所有鍵名都是正整數或零,并且有length屬性,那么這個物件就很像陣列,稱為偽陣列,典型的偽陣列有函式的arguments物件,以及大多數 DOM 元素集,還有字串,
...
<button>測驗1</button>
<br>
<button>測驗2</button>
<br>
<button>測驗3</button>
<br>
<script type="text/javascript">
let btns = document.getElementsByTagName("button")
console.log("btns",btns);//得到一個偽陣列
btns.forEach(item=>console.log(item)) Uncaught TypeError: btns.forEach is not a function
</script>
針對偽陣列,沒有陣列一般方法,直接遍歷便會出錯,ES6新增Array.from()方法來提供一種明確清晰的方式以解決這方面的需求,
Array.from(btns).forEach(item=>console.log(item))將偽陣列轉換為陣列

2.Array.of(v1, v2, v3) : 將一系列值轉換成陣列
當呼叫 new Array( )構造器時,根據傳入引數的型別與數量的不同,實際上會導致一些不同的結果, 例如:
let items = new Array(2) ;
console.log(items.length) ; // 2
console.log(items[0]) ; // undefined
console.log(items[1]) ;
let items = new Array(1, 2) ;
console.log(items.length) ; // 2
console.log(items[0]) ; // 1
console.log(items[1]) ; // 2
當使用單個數值引數來呼叫 Array 構造器時,陣列的長度屬性會被設定為該引數, 如果使用多個引數(無論是否為數值型別)來呼叫,這些引數也會成為目標陣列的項,陣列的這種行為既混亂又有風險,因為有時可能不會留意所傳引數的型別,
ES6 引入了Array.of( )方法來解決這個問題,該方法的作用非常類似Array構造器,但在使用單個數值引數的時候并不會導致特殊結果,Array.of( )方法總會創建一個包含所有傳入引數的陣列,而不管引數的數量與型別:
let items = Array.of(1, 2);
console.log(items.length); // 2
console.log(items[0]); // 1
console.log(items[1]); // 2
items = Array.of(2);
console.log(items.length); // 1
console.log(items[0]); // 2
Array.of基本上可以用來替代Array()或newArray(),并且不存在由于引數不同而導致的多載,而且他們的行為非常統一,
3.陣列實體的 find() 和 findIndex()
陣列實體的find方法,用于找出第一個符合條件的陣列成員,它的引數是一個回呼函式,所有陣列成員依次執行該回呼函式,直到找出第一個回傳值為true的成員,然后回傳該成員,如果沒有符合條件的成員,則回傳undefined,
[1, 4, -5, 10].find((n) => n < 0) // -5
陣列實體的findIndex方法的用法與find方法非常類似,回傳第一個符合條件的陣列成員的位置,如果所有成員都不符合條件,則回傳-1,
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
4.陣列實體的includes()
Array.prototype.includes方法回傳一個布林值,表示某個陣列是否包含給定的值,該方法的第二個引數表示搜索的起始位置,默認為0,如果第二個引數為負數,則表示倒數的位置,如果這時它大于陣列長度(比如第二個引數為-4,但陣列長度為3),則會重置為從0開始,
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(3, -1); // true
[1, 2, 3, 5, 1].includes(1, 2); // true
沒有該方法之前,我們通常使用陣列的indexOf方法,檢查是否包含某個值,indexOf方法有兩個缺點,一是不夠語意化,它的含義是找到引數值的第一個出現位置,所以要去比較是否不等于-1,表達起來不夠直觀,二是,它內部使用嚴格相等運算子(===)進行判斷,這會導致對NaN的誤判,
[NaN].indexOf(NaN) // -1
[NaN].includes(NaN) // true
5.陣列實體的 entries(),keys() 和 values()
ES6 提供entries(),keys()和values(),用于遍歷陣列,它們都回傳一個遍歷器物件,可以用for...of回圈進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷,
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
四、箭頭函式
ES6 允許使用“箭頭”(=>)定義函式,它主要有兩個作用:縮減代碼和改變this指向,接下來我們詳細介紹:
1. 縮減代碼
const double1 = function(number){
return number * 2; //ES5寫法
}
const double2 = (number) => {
return number * 2; //ES6寫法
}
const double4 = number => number * 2; //可以進一步簡化
多個引數記得加括號
const double6 = (number,number2) => number + number2;
如果箭頭函式的代碼塊部分多于一條陳述句,就要使用大括號將它們括起來,并且使用return陳述句回傳,
const double = (number,number2) => {
sum = number + number2
return sum;
}
由于大括號被解釋為代碼塊,所以如果箭頭函式直接回傳一個物件,必須在物件外面加上括號,否則會報錯,
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報
let getTempItem = id => ({ id: id, name: "Temp" });
此外還有個好處就是簡化回呼函式
// 正常函式寫法
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函式寫法
[1,2,3].map(x => x * x);//[1, 4, 9]
2. 改變this指向
長期以來,JavaScript 語言的this物件一直是一個令人頭痛的問題,在物件方法中使用this,必須非常小心,箭頭函式”系結”this,很大程度上解決了這個困擾,我們不妨先看一個例子:
const team = {
members:["Henry","Elyse"],
teamName:"es6",
teamSummary:function(){
return this.members.map(function(member){
return `${member}隸屬于${this.teamName}小組`; // this不知道該指向誰了
})
}
}
console.log(team.teamSummary());//["Henry隸屬于undefined小組", "Elyse隸屬于undefined小組"]
teamSummary函式里面又嵌了個函式,這導致內部的this的指向發生了錯亂,
那如何修改:
方法一、let self = this
const team = {
members:["Henry","Elyse"],
teamName:"es6",
teamSummary:function(){
let self = this;
return this.members.map(function(member){
return `${member}隸屬于${self.teamName}小組`;
})
}
}
console.log(team.teamSummary());//["Henry隸屬于es6小組", "Elyse隸屬于es6小組"]
方法二、bind函式
const team = {
members:["Henry","Elyse"],
teamName:"es6",
teamSummary:function(){
return this.members.map(function(member){
// this不知道該指向誰了
return `${member}隸屬于${this.teamName}小組`;
}.bind(this))
}
}
console.log(team.teamSummary());//["Henry隸屬于es6小組", "Elyse隸屬于es6小組"]
方法三、 箭頭函式
const team = {
members:["Henry","Elyse"],
teamName:"es6",
teamSummary:function(){
return this.members.map((member) => {
// this指向的就是team物件
return `${member}隸屬于${this.teamName}小組`;
})
}
}
console.log(team.teamSummary());//["Henry隸屬于es6小組", "Elyse隸屬于es6小組"]
3.使用注意點
(1)函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件,
(2)不可以當作建構式,也就是說,不可以使用new命令,否則會拋出一個錯誤,
(3)不可以使用arguments物件,該物件在函式體內不存在,如果要用,可以用 rest 引數代替,
(4)不可以使用yield命令,因此箭頭函式不能用作 Generator 函式,
五、rest 引數
ES6 引入 rest 引數(形式為...變數名),用于獲取函式的多余引數,這樣就不需要使用arguments物件了,
rest 引數搭配的變數是一個陣列,該變數將多余的引數放入陣列中,
我們舉個例子:如何實作一個求和函式?
傳統寫法:
function addNumbers(a,b,c,d,e){
var numbers = [a,b,c,d,e];
return numbers.reduce((sum,number) => {
return sum + number;
},0)
}
console.log(addNumbers(1,2,3,4,5));//15
ES6寫法:
function addNumbers(...numbers){
return numbers.reduce((sum,number) => {
return sum + number;
},0)
}
console.log(addNumbers(1,2,3,4,5));//15
也可以與解構賦值組合使用
var array = [1,2,3,4,5,6];
var [a,b,...c] = array;
console.log(a);//1
console.log(b);//2
console.log(c);//[3, 4, 5, 6]
rest 引數還可以與箭頭函式結合
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)// [1,2,3,4,5]
注意:①每個函式最多只能宣告一個rest引數,而且 rest引數必須是最后一個引數,否則報錯,
②rest引數不能用于物件字面量setter之中
let object = {
set name(...value){ //報錯
//執行一些邏輯
}
}
六、展開運算子
與剩余引數關聯最密切的就是擴展運算子,剩余引數允許你把多個獨立的引數合并到一個陣列中;而擴展運算子則允許將一個陣列分割,并將各個項作為分離的引數傳給函式,
當用在字串或陣列前面時稱為擴展運算子,個人覺得可以理解為rest引數的逆運算,用于將陣列或字串進行拆解,有些時候,函式不允許傳入陣列,此時使用展開運算子就很方便,不信的話,咱們看個例子:Math.max()方法,它接受任意數量的引數,并會回傳其中的最大值,
let value1 = 25,
let value2 = 50;
console.log(Math.max(value1, value2)); // 50
但若想處理陣列中的值,此時該如何找到最大值?Math.max()方法并不允許你傳入一個陣列,其實你可以像使用rest引數那樣在該陣列前添加...,并直接將其傳遞給 Math.max()
let values = [25,50,75, 100]
//等價于console.log(Math.max(25,50,75,100));
console.log(Math.max(...values)); //100
擴展運算子還可以與其他引數混用
let values = [-25,-50,-75,-100]
console.log(Math.max(...values,0)); //0
擴展運算子拆解字串與陣列
var array = [1,2,3,4,5];
console.log(...array);//1 2 3 4 5
var str = "String";
console.log(...str);//S t r i n g
還可以實作拼接
var defaultColors = ["red","greed"];
var favoriteColors = ["orange","yellow"];
var fallColors = ["fire red","fall orange"];
console.log(["blue","green",...fallColors,...defaultColors,...favoriteColors]
//["blue", "green", "fire red", "fall orange", "red", "greed", "orange", "yellow"]
七、解構賦值----更方便的資料訪問
ES6 新增了解構,這是將一個資料結構分解為更小的部分的程序,
1.解構為何有用?
在ES5及更早版本中,從物件或陣列中獲取資訊、并將特定資料存入本地變數,需要書寫許多并且相似的代碼,例如:
var expense = {
type: "es6",
amount:"45"
};
var type = expense.type;
var amount = expense.amount;
console.log(type,amount);
此代碼提取了expense物件的type與amount值,并將其存在同名的本地變數上,雖然 這段代碼看起來簡單,但想象一下若有大量變數需要處理,你就必須逐個為其賦值;并且若有一個嵌套的資料結構需要遍歷以尋找資訊,你可能會為了一點資料而挖掘整個結構,
這就是ES6為何要給物件與陣列添加解構,當把資料結構分解為更小的部分時,從中提取你要的資料會變得容易許多,
2.物件
上個例子中如果采用物件解構的方法,就很容易獲取expense物件的type與amount值,
const { type,amount } = expense;
console.log(type,amount);
我們再來看個例子:
let node = {type:"Identifier", name:"foo"},
type = "Literal",name = 5;
({type,name}= node);// 使用解構來分配不同的值
console.log(type); // "Identifier"
console.log(name); // "foo"
注意:你必須用圓括號包裹解構賦值陳述句,這是因為暴露的花括號會被決議為代碼塊陳述句,而塊陳述句不允許在賦值運算子(即等號)左側出現,圓括號標示了里面的花括號并不是塊陳述句、而應該被解釋為運算式,從而允許完成賦值操作,
默認值:
可以選擇性地定義一個默認值,以便在指定屬性不存在時使用該值,若要這么做,需要在 屬性名后面添加一個等號并指定默認值,就像這樣:
let node = {
type: "Identifier",
name: "foo"
};
let {
type,
name,
value = https://www.cnblogs.com/xzsj/p/true
} = node;
console.log(type); //"Identifier"
console.log(name); // "foo"
console.log(value); // true
嵌套物件解構:
使用類似于物件字面量的語法,可以深入到嵌套的物件結構中去提取你想要的資料,
let node = {
type: "Identifier",
name: "foo",
loc: {
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 4
}
}
};
let { loc: { start }} = node;
console.log(start.line); // 1
console.log(start.column); // 1
本例中的解構模式使用了花括號,表示應當下行到node物件的loc屬性內部去尋找start屬性,
必須傳值的解構引數
function setCookie(name, value, {
secure,
path,
domain,
expires
}) {
// 設定cookie的代碼
}
setCookie("type", "js");//報錯
在此函式內,name與value引數是必需的,而secure、path、domain與expires則不是,默認情況下呼叫函式時未給引數解構傳值會拋出錯誤,像上例中如果setCookie不傳第三個引數,就會報錯,若解構引數是可選的,可以給解構的引數提供默認值來處理這種錯誤,
function setCookie(name, value, {
secure,
path,
domain,
expires
} = {}) {}
setCookie("type", "js");//不會報錯
3.陣列
const names = ["Henry","Bucky","Emily"];
const [name1,name2,name3] = names;
console.log(name1,name2,name3);//Henry Bucky Emily
const [name,...rest] = names;//結合展開運算子
console.log(rest);//["Bucky", "Emily"]
用{}解構回傳陣列個數
const {length} = names;
console.log(length);//3
陣列解構也可以用于賦值背景關系,但不需要用小括號包裹運算式,這點跟物件解構的約定不同,
let colors = ["red", "green", "blue"],
firstColor = "black",
secondColor = "purple";
[firstColor, secondColor] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
默認值:陣列解構賦值同樣允許在陣列任意位置指定默認值,當指定位置的項不存在、或其值為undefined,那么該默認值就會被使用,
let colors = ["red"];
let [firstColor, secondColor = "green"] = colors;
console.log(firstColor); // "red"
console.log(secondColor);// "green"
與rest引數搭配
在ES5中常常使用concat()方法來克隆陣列,例如:
//在ES5中克隆陣列
var colors = ["red", "green", "blue"];
var clonedColors = colors.concat();
console.log(clonedColors); //"[red,green,blue]"
在ES6中,你可以使用剩余項的語法來達到同樣效果
//在ES6中克隆陣列
let colors = ["red", "green", "blue"];
let [...clonedColors] = colors;
console.log(clonedColors); //[red,green,blue]
接下我們看個例子:如何將陣列轉化為物件
const points = [
[4,5],
[10,1],
[0,40]
];
//期望得到的資料格式如下,如何實作?
// [
// {x:4,y:5},
// {x:10,y:1},
// {x:0,y:40}
// ]
let newPoints = points.map(pair => {
const [x,y] = pair;
return {x,y}
})
//還可以通過以下辦法,更為簡便
let newPoints = points.map(([x,y]) => {
return {x,y}
})
console.log(newPoints);
混合解構
const people = [
{name:"Henry",age:20},
{name:"Bucky",age:25},
{name:"Emily",age:30}
];
//es5 寫法
var age = people[0].age;
console.log(age);
//es6 解構
const [age] = people;
console.log(age);//第一次解構陣列 {name:"Henry",age:20}
const [{age}] = people;//再一次解構物件
console.log(age);//20
4.注意點
當使用解構來配合var、let、const來宣告變數時,必須提供初始化程式(即等號右邊的值),下面的代碼都會因為缺失初始化程式而拋出語法錯誤:
var { type, name }; // 語法錯誤!
let { type, name }; // 語法錯誤!
const { type, name }; // 語法錯誤!
八、模板字串(template string)
模板字串是增強版的字串,用反引號(`)標識,它可以當作普通字串使用,也可以用來定義多行字串,或者在字串中嵌入變數,
模板字串中嵌入變數和函式,需要將變數名寫在${}之中,
let name = "Henry";
function makeUppercase(word){
return word.toUpperCase();
}
let template =
`
<h1>${makeUppercase('Hello')}, ${name}!</h1>//可以存放函式和變數
<p>感謝大家收看我們的視頻, ES6為我們提供了很多遍歷好用的方法和語法!</p>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
`;
document.getElementById('template').innerHTML = template;

再舉個例子,作業中常用到ElementUI庫,在自定義一個彈出框時,使用模板字串就很方便:
await this.$alert(
`<p><strong>確認是否升級${
this.lectureName
}</strong><br>(若已存在講義套件,升級后請重新生成)</p>`,
{
dangerouslyUseHTMLString: true
}
)
九、Class 和傳統建構式有何區別
從概念上講,在 ES6 之前的 JS 中并沒有和其他面向物件語言那樣的“類”的概念,長時間里,人們把使用 new 關鍵字通過函式(也叫構造器)構造物件當做“類”來使用,由于 JS 不支持原生的類,而只是通過原型來模擬,各種模擬類的方式相對于傳統的面向物件方式來說非常混亂,尤其是處理當子類繼承父類、子類要呼叫父類的方法等等需求時,
ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為物件的模板,通過class關鍵字,可以定義類,但是類只是基于原型的面向物件模式的語法糖,
對比在傳統建構式和 ES6 中分別如何實作類:
//傳統建構式
function MathHandle(x,y){
this.x=x;
this.y=y;
}
MathHandle.prototype.add =function(){
return this.x+this.y;
};
var m=new MathHandle(1,2);
console.log(m.add())
//class語法
class MathHandle {
constructor(x,y){
this.x=x;
this.y=y;
}
add(){
return this.x+this.y;
}
}
const m=new MathHandle(1,2);
console.log(m.add())
這兩者有什么聯系?其實這兩者本質是一樣的,只不過是語法糖寫法上有區別,所謂語法糖是指計算機語言中添加的某種語法,這種語法對語言的功能沒有影響,但是更方便程式員使用,比如這里class語法糖讓程式更加簡潔,有更高的可讀性,
typeof MathHandle //"function"
MathHandle===MathHandle.prototype.constructor //true
對比在傳統建構式和 ES6 中分別如何實作繼承:
//傳統建構式繼承
function Animal() {
this.eat = function () {
alert('Animal eat')
}
}
function Dog() {
this.bark = function () {
alert('Dog bark')
}
}
Dog.prototype = new Animal()// 系結原型,實作繼承
var hashiqi = new Dog()
hashiqi.bark()//Dog bark
hashiqi.eat()//Animal eat
//ES6繼承
class Animal {
constructor(name) {
this.name = name
}
eat() {
alert(this.name + ' eat')
}
}
class Dog extends Animal {
constructor(name) {
super(name) // 有extend就必須要有super,它代表父類的建構式,即Animal中的constructor
this.name = name
}
say() {
alert(this.name + ' say')
}
}
const dog = new Dog('哈士奇')
dog.say()//哈士奇 say
dog.eat()//哈士奇 eat
Class之間可以通過extends關鍵字實作繼承,這比ES5的通過修改原型鏈實作繼承,要清晰和方便很多,
Class 和傳統建構式有何區別
- Class 在語法上更加貼合面向物件的寫法
- Class 實作繼承更加易讀、易理解,對初學者更加友好
- 本質還是語法糖,使用prototype
十、Promise的基本使用和原理
在JavaScript的世界中,所有代碼都是單執行緒執行的,由于這個“缺陷”,導致JavaScript的所有網路操作,瀏覽器事件,都必須是異步執行,Promise 是異步編程的一種解決方案,比傳統的解決方案(回呼函式和事件)更合理和更強大,

ES6中的promise的出現給我們很好的解決了回呼地獄的問題,所謂的回呼地獄是指當太多的異步步驟需要一步一步執行,或者一個函式里有太多的異步操作,這時候就會產生大量嵌套的回呼,使代碼嵌套太深而難以閱讀和維護,ES6認識到了這點問題,現在promise的使用,完美解決了這個問題,
Promise原理
一旦狀態改變,就不會再變,任何時候都可以得到這個結果,Promise物件的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected,promise 物件初始化狀態為 pending ;當呼叫resolve(成功),會由pending => fulfilled ;當呼叫reject(失敗),會由pending => rejected,具體流程見下圖:

Promise的使用流程
- new Promise一個實體,而且要 return
- new Promise 時要傳入函式,函式有resolve reject 兩個引數
- 成功時執行 resolve,失敗時執行reject
- then 監聽結果
function loadImg(src){
const promise=new Promise(function(resolve,reject){
var img=document.createElement('img')
img.onload=function(){
resolve(img)
}
img.onerror=function(){
reject()
}
img.src=https://www.cnblogs.com/xzsj/p/src
})
return promise//回傳一個promise實體
}
var src="https://img.uj5u.com/2021/04/01/232016012025359.png"
var result=loadImg(src)
result.then(function(img){
console.log(img.width)//resolved(成功)時候的回呼函式
},function(){
console.log("failed")//rejected(失敗)時候的回呼函式
})
result.then(function(img){
console.log(img.height)
})
promise會讓代碼變得更容易維護,像寫同步代碼一樣寫異步代碼,同時業務邏輯也更易懂,
十一、Iterator 和 for...of 回圈
JavaScript 原有的表示“集合”的資料結構,主要是陣列(Array)和物件(Object),ES6 又添加了Map和Set,這樣就需要一種統一的介面機制,來處理所有不同的資料結構,遍歷器(Iterator)就是這樣一種機制,它是一種介面,為各種不同的資料結構提供統一的訪問機制,任何資料結構只要部署 Iterator 介面,就可以完成遍歷操作(即依次處理該資料結構的所有成員),
1.Iterator的作用:
- 為各種資料結構,提供一個統一的、簡便的訪問介面;
- 使得資料結構的成員能夠按某種次序排列
- ES6創造了一種新的遍歷命令for...of回圈,Iterator介面主要供for...of消費,
2.原生具備iterator介面的資料(可用for of遍歷)
- Array
- set容器
- map容器
- String
- 函式的 arguments 物件
- NodeList 物件
let arr3 = [1, 2, 'kobe', true];
for(let i of arr3){
console.log(i); // 1 2 kobe true
}
let str = 'abcd';
for(let item of str){
console.log(item); // a b c d
}
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
console.log(e);
}
// Gecko
// Trident
// Webkit
3.幾種遍歷方式比較
- for of 回圈不僅支持陣列、大多數偽陣列物件,也支持字串遍歷,此外還支持 Map 和 Set 物件遍歷,
- for in回圈可以遍歷字串、物件、陣列,不能遍歷Set/Map
- forEach 回圈不能遍歷字串、物件,可以遍歷Set/Map
十二、ES6模塊化
ES6 在語言標準的層面上,實作了模塊功能,而且實作得相當簡單,旨在成為瀏覽器和服務器通用的模塊解決方案,其模塊功能主要由兩個命令構成:export和import,export命令用于規定模塊的對外介面,import命令用于輸入其他模塊提供的功能,
/** 定義模塊 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 參考模塊 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
如上例所示,使用import命令的時候,用戶需要知道所要加載的變數名或函式名,否則無法加載,為了給用戶提供方便,讓他們不用閱讀檔案就能加載模塊,就要用到export default命令,為模塊指定默認輸出,
// export-default.js
export default function () {
console.log('foo');
}
上面代碼是一個模塊檔案export-default.js,它的默認輸出是一個函式,
其他模塊加載該模塊時,import命令可以為該匿名函式指定任意名字,
// import-default.js
import customName from './export-default';
customName(); // 'foo'
上面代碼的import命令,可以用任意名稱指向export-default.js輸出的方法,這時就不需要知道原模塊輸出的函式名,需要注意的是,這時import命令后面,不使用大括號,
后記
如需前端指導、前端資料、Java指導和 Java 資料的請聯系本人,感謝您的支持,
WECHAT:xzsj07
備注:加好友請注明來源,
作者:浪里行舟
來源:github
地址:ES6核心特性
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/270865.html
標籤:JavaScript
