ES6 學習筆記

1. 認識 ES6

1. ECMAScript 和 JavaScript 關係

  • ECMA“European Computer Manufacturers Association" 的縮寫,中文稱歐洲計算機制造聯合會,這個組織的目標是評估,開發和認可電信和計算機標準
  • ECMA 是標準,JavaScript 是實現
    • 類似 HTML5 是標準,IE10ChromeFF 都是實現
    • 目的是讓所有前端腳本都實現 ECMA
    • 目前只有 JavaScript 實現 ECMA 標準
  • ECMAScript 簡稱 ECMAESES6
  • 目前版本
    • 高級瀏覽器支持 ES6
    • 低級瀏覽器主要支持 ES3.1

2. ECMA 標準出臺流程

  • ECMAScript 是跨多個平臺的許多廠商(包括瀏覽器廠商在內的各方組成)實施的不斷髮展的標準。
  • ES6(ECMAScript 2015)花費六年的時間敲定,是一個很大的發行版。新的年度發佈流程被制定,以簡化流程並更快的添加功能。
  • ES9(ES2018)是目前的最新版本。推動 JavaScript 提案沿着一條嚴格的發展道路前進:
    • strawman ----- 最初想法的提交
    • proposal(提案) ------ 由至少一名成員倡導的正式提案文件,該文件包括 API 事例
    • draft(草案) ----- 功能規範的初始版本,該版本包含功能規範的兩個實驗實現
    • candidate(候選) ------ 提案規範通過審查並從廠商那裏收集反饋
    • finished(完成) ------ 提案准備加入 ECMAScript,但是到瀏覽器或者 Node.js 中可能需要更長的時間

3. 接觸 ES6 的意義

  • 對語法的改進,功能的增加
  • 使用 Vue,React,小程序,Node.js 等都在用

2. ES6 兼容性解決

  • 兼容表:http://kangax.github.io/compat-table/es6/
  • IE10+ChromeFireFox移動端Node.js 現在都支持
  • 兼容低版本瀏覽器
    • 在線轉換(這種編譯會加大頁面渲染的時間)
    • 提前編譯(強烈建議這種方式,不影響瀏覽器渲染時間)
    • 比較通用的工具方案有 babeljsxtraceures6-shim
// ES6
var fn = (
	v => console.log(v);
)

轉爲

// ES6
"use strict"

var fn = function fn(v){
	return console.log(v);
};

1. 使用 Babel 工具

  • 使用 npm 來安裝 babel,npm 是隨同 Node.js 一起安裝的包管理工具,新版的 Node.js 已經繼承了 npm ,只要安裝 Node.js 即可
  • 在命令行安裝:
    • npm i babel-core

2. 在線轉換

  • 在安裝位置下找到 node_modules\babel-core\lib\api\browser.js
  • https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js
<script src="browser.js"></script>
<script type="text/babel">
	const Name = '張三';
	alert(Name);
</script>

3. 提前編譯

  • 改鏡像(可不改)
    • npm config set registry https://registry.npm.taobao.org/
    • npm config get registry
  • 輸入 npm install babel-cli-g 來全局安裝 babel-cli
    • 檢驗(babel -V)
  • 找一個目錄,用 npm 來初始化一個項目,用來搭建我們的 babel 環境
    • npm init)或(npm init -y
  • 進入 babel 目錄,然後打開命令行工具驗證環境
    • babel-node
  • babel-nodebabel 提供的一個編譯工具,也可執行 js 代碼
    • babel-node index.js
  • babel 目錄新建 .babelrc 文件(這是 babel 的配置文件)
    • Window 下可以用如下命令創建
(echo .> .babelrc)
{ 	"presets":["es2015","stage-2"], // 設置轉碼規則
	"plugins":["transform-runtime"] // 設置插件
}
  • 安裝需要的庫:
    • npm install babel-core babel-preset-es2015 babel-plugin-transform-runtime babel-preset-stage-2 -save-dev
  • 打開 babel 項目下的 package.json 文件,做如下修改
"scripts":{
	"build":"babel src -w -d lib"
}
  • 編譯整個 src 目錄(需要轉換的目錄)並將其輸出到 lib 目錄(輸出內容的存放目錄),-w 其實是 -watch 的意思,就是監聽文件,實時編譯輸出
  • 新建 src 目錄和 lib 目錄,記得一定要建,不然會報錯,然後啓動 babel 工程
    • npm run build

4. Babel 兼容性列表

ES6 特性 兼容性
箭頭函數 支持
類的聲明和繼承 部分支持,IE8不支持
增強的對象字面量 支持
字符串模版 支持
解構 支持,但注意使用方式
參數默認值,不定參數,拓展參數 支持
let 與 const 支持
for of IE 不支持
iterator,generator 不支持
模版 module,Proxies,Symbol 不支持
Map,Set 和 WeakMap,WeakSet 不支持
Promises,Math,Number,String,Object 的新 API 不支持
export & import 支持
生成器函數 不支持
數組拷貝 支持

3. let 和 const

1. var 命令

  • var 可以重複聲明
var a = 10;
var a = 20;
console.log(a); // 20
  • var 無法限制修改
var a = 10;
a = 30;
  • var 沒有塊級作用域
if(true){
	var a = 10;
	console.log(a); // 10
}
console.log(a); // 10

2. let 命令

  • ES6 新增 let 命令,用來聲明變量。用法類似於 var
  • let 不能重複聲明
let a = 10;
let a = 20;
console.log(a); // 報錯
  • let 聲明的變量,只在 let 命令所在的代碼塊內有效
  • let具有塊級作用域
例子一:
if(true){
	let a = 10;
	console.log(a); // 10
}
console.log(a); // 報錯

例子二:
for(var i=0; i<10; i++){
	console.log(i); // 1~9
}
console.log(i); // 10

for(let i=0; i<10; i++){
	console.log(i); // 1~9
}
console.log(i); // 報錯
<input type="button" value="one">
<input type="button" value="two">
<input type="button" value="three">
<script>
	window.onload = function(){
		var btns = document.getElementsByTagName("input");
		
		// 當用 var 時,需要閉包方法
		for(var i=0; i<btns.length; i++){
			(function(i){
				btns[i].onclick = function(){
					console.log(i + 1);
				}
			})(i);
		}
		// 當用 let 時,計數器 i 只在 for 循環體內有效,在循環體外引用就會報錯
		for(let i=0; i<btns.length; i++){
			btns[i].onclick = function(){
				console.log(i + 1);
			}
		}
	}
</script>

3. const 命令

  • const 命令聲明一個只讀的常量
  • 一旦聲明,常量的值就不能改變
const PI = 3.1415;
console.log('PI=' + PI);

PI = 3; // 報錯
  • const 命令聲明的常量不得改變值,即一旦聲明,就必須立即初始化
const a = 10;
const foo; // 報錯
  • const 命令聲明的常量,只在聲明所在的塊級作用域內有效
  • const 命令聲明的常量不提升,只能在聲明的位置後使用
  • const 命令聲明的常量,與 let 一樣不可重複聲明

4. const 命令本質

  • const 實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址不得改動
  • 對於簡單類型的數據(數值,字符串,布爾值),值就保存在變量指向的那個內存地址,因此等同於常量
  • 但對於複合類型的數據(主要是對象數組),變量指向的內存地址,保存的只是一個指針const 只能保證這個指針是固定的,至於它指向的數據結構是不是可變的,就完全不能控制了
  • 因此,將一個對象聲明爲常量必須非常小心
const foo = {};
foo.prop = 123; // 爲 foo 添加一個屬性,可以成功
console.log(foo.prop); // 123

foo = {}; // 將 foo 指向另一個對象,就會報錯

分析:
	上面的代碼中,常量 foo 存儲的是一個地址,這個地址指向一個對象。不可變的只是這個地址,即不能把 foo 指向另一個地址,但對象本身是可變的,所以依然可以爲其添加新屬性。 

4. 箭頭函數

1. 基本用法

  • ES6 允許使用"箭頭"(=>)定義
var f = v => v;
// 等同於
var f = function(v){
	return v;
}
  • 不需要參數或需要多個參數,就用圓括號代
var f = () => 5;
// 等同於
var f = function(){
	return 5
};

var sum = (num1,num2) => num1+num2;
// 等同於
var sum = function(num1,num2){
	return num1 + num2;
}
  • 代碼塊部分多於一條語句,就用大括號括起來,並且用 return 返回
var sum = (num1,num2) => {return num1+num2;}
  • 箭頭函數返回對象時,必須在對象外面加上括號
var getTempItem = id => ({ id: id, name: 'Temp' });
console.log(getTempItem(2));
  • 箭頭函數使得表達更加簡潔
const isEven = n => n % 2 == 0;
const square = n => n * n;
  • 箭頭函數能夠簡化回調函數
[1,2,3].map(function(x){
	return x * x;
)}
// 等同於
[1,2,3].map(x => x * x);

var result = values.sort(function(a,b){
	return a - b;
});
// 等同於
var result = values.sort((a,b) => a - b);

2. this 指向問題

2.1 普通函數中的 this
  • this 總是代表它的直接調用者(jsthis 是執行上下文)
    • 例如 obj.func,那麼 func 中的 this 就是 obj
  • 在默認情況下(非嚴格模式下,未使用 ‘use strict’),沒找到直接調用者,則 this 指的是 window (約定俗成)
  • 在嚴格模式下,沒有直接調用者的函數中的 thisundefined
  • 使用 callapplybind(ES5 新增)綁定的,this 指的是綁定的對象
2.2 箭頭函數中的 this
  • 箭頭函數沒有自己的 this,它的 this 是繼承而來的;默認指向在定義它時所處的對象(宿主對象),而不是執行時的對象,定義它的時候,可能環境是 window;箭頭函數可以方便地讓我們在 setTimeoutsetInterval 中方便的使用 this
  • 箭頭函數中,this 指向的固定化,並不是因爲箭頭函數內部有綁定 this 的機制,實際原因是箭頭函數根本沒有自己的 this ,導致內部的 this 就是外層代碼塊的 this
function Person (){
	this.name = 'little bear123',
	this.age = 1234
	setInterval(() => {
		console.log(this);
		console.log('我叫' + this.name + '我今年' + this.age + '歲')
	},10000)
}
let p = new Person()

// 結果
Person{name:"little bear123",age:1234}
我叫little bear123我今年1234// 解析
這段函數中,this 指向的是 `Person` 對象,可以看到控制檯打印結果,此時的箭頭函數所處的宿主對象是 `Person`,所以 this 指向的是 `person`

5. 數組的新增方法

1. map 映射

// map 映射
// 一個對應一個
let arr = [2,4,6,8,10];
let narr = arr.map(function(item){
	console.log(item);// 2,4,6,8,10
	return item*item;// 4,16,36,64,100	
));
// 等同於
let narr = arr.map(item => item*item);
console.log(narr);

2. reduce 彙總

// reduce 彙總
// 進去多個出來一個
// 總和,平均數,最大最小值
let arr = [2,4,6,8,10];
let narr = arr.reduce((a,b,c)=>{
	console.log(a,b,c);
	return a+b;
});
// 等同於
let narr = arr.reduce((a,b,c)=>a+b);
console.log(narr);

3. filter 過濾器

// filter 過濾器
// 判讀真假
let arr = [2,4,6,8,10];

let narr = arr.filter(item => item%3 == 0);
console.log(narr); // [6]

4. forEach 循環(迭代)

// forEach 遍歷
let arr = [2,4,6,8,10];

arr.forEach((value,index)=>{
	console.log(index,value);
});

5. some()

  • 一個爲 true 就會返回 true
let arr = [2,4,6,8,10];
let narr = arr.some(item => item>10);
console.log(narr); // false

6. every()

  • 必須所有都返回 true 纔會返回 true
let arr = [2,4,6,8,10];
let narr = arr.every(item => item>2);
console.log(narr); // false

6. 字符串和模版字符串

1. 字符串新方法

  • startsWith 判斷以什麼字符串開頭
  • endsWith 判斷以什麼字符串結尾
let url = "https://www.baidu.com/";

if(url.startsWith("http://")){
	console.log('普通URL');
}else if(url.startsWith("https://")){
	console.log('加密URL');
}else if(url.startsWith('ftp://')){
	console.log('文件傳輸協議');
}
let str = "logo.jpg";

if(str.endsWith(".jpg")){
	console.log('圖片');
}else if(str.endsWith(".txt")){
	console.log('文本');
}

2. 模版字符串

  • 傳統的 JavaScript 語言,輸出模版:
$('#result').append(
	'There are <br>' + basket.count + '</b>' + 
	'items in your basket,' +
	'<em>' + basket.onSale + 
	'</em> are on sale'
);
  • ES6 模版字符串
$('#result').append(`
	There are <b>${basket.count}</b> items
	in your basket,<em>${basket.onSale}</em>
	are on sale!
	`
);
  • 模版字符串(template string)是增強版的字符串,用反引號標識。他可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`

console.log(`string text line 1
string text line2`);
// 字符串中嵌入變量
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);

7. 函數的參數

1. 參數擴展

let arr = [1,2,3,4];
let arr2 = [5,6,7,8];

let narr = [...arr,...arr2];

let xarr = ...arr; // 報錯,不能直接賦值
console.log(narr); // [1,2,3,4,5,6,7,8]

2. 展開數組

// 可變參數
function demo(...args){
	console.log(args);
}

demo(1,2,3,4,5,6,...);

3. 默認參數

functon demo(a,b=2,c=3,d){
	console.log(a,b,c,d);
}
demo(1,5,8); // 1,5,8,undefined

8. 解構賦值

  • 左右兩邊結構必須一樣
let [a,b,c] = [1,2,3];
let {a,b,c} = {a:12,b:2,c:4};
// 粒度 json arr
let [{a,b},[c,d,e],num,str] = [{a:12,b:33},[12,4,3],8,'saf'];
let [json,arr,num,str] = [{a:12,b:33},[12,4,3],8,'saf'];
  • 右邊必須有值
let {a,b} = {12,5}; 
let {a,b} = {a,b}; // 錯誤 
  • 聲明和賦值不能分開
let [a,b];
[a,b] = [12,5];

9. Class 用法

1. Class 基本用法

  • 傳統的生成實例對象的方法–構造函數
function Point(x,y){
	this.x = x;
	this.y = y;
}
Point.prototype.toString = function(){
	return '(' + this.x + ',' + this.y + ')';
};
var p = new Point(1,2);
  • ES6 Class(類)概念
// constructor
// this 關鍵字代表實例對象
class Point{
	constructor(x,y){
		this.x = x;
		this.y = y;
	}
	
	toString(){
		return '(' + this.x + ',' + this.y + ')';
	}
}

2. Class 類的繼承

class Person{
	constructor(name,age){
		this.name = name;
		this.age = age;
	}
	say(){
		console.log(this.name +"==="+this.age);
	}
}

class Teacher extends Person{
	constructor(name,age,school){
		super(name,age);
		this.school = school;
	}

	study(){
		console.log(this.name+"==="+this.age+"==="+this.school);
	}
}

let t = new Teacher("張三",12,'html');

t.say();
t.study();

10. JSON 新應用

1. json的標準寫法

  • 只能用雙引號("")
  • 所有的(屬性)名字只能用雙引號("")包起來
let str = {"a":1,"b":"abc","c":true}

2. JSON 對象

  • JSON.stringify() 串行化
let obj = {a:10,b:20,c:30};
let str = JSON.stringify(obj);
console.log(str);// {"a":10,"b":20,"c":30}
  • JSON.parse() 反串行化
let str = {"a":10,"b":20,"c":30};
let obj = JSON.parse(str);
console.log(obj);// 10 20 30

3. 簡寫

  • (屬性和值)名字一樣可以簡寫
  • 方法一樣可以簡寫(:function省

11. Promise 應用

  • Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作--接口--Ajax--setTimeout--img)的結果
  • ES6 規定,Promise對象是一個構造函數,用來生成Promise實例
const promise = new Promise((resolve,reject)=>{
	// ...some code
	
	if(/*異步操作成功*/){
		resolve(value);
	}else{
		reject(error);
	}
});
  • Promise構造函數接受一個函數作爲參數,該函數的兩個參數分別是resolvereject,它們是兩個函數,由JavaScript引擎提供,不用自己部署
  • resolve函數的作用是,將Promise對象的狀態從“未完成”變爲“成功”,在異步操作成功時調用,並將異步操作的結果,作爲參數傳遞出去
  • reject函數的作用是,將Promise對象的狀態從“未完成”變爲“失敗”(即從pending變爲rejected),在異步操作失敗時調用,並將異步操作報出的錯誤,作爲參數傳遞出去
  • Promise實例生成後,可以用 then 方法分別指定 resolved 狀態和 rejected 狀態的回調函數
promise.then(function(value){
	// success
},function(error){
	// false
});
  • then 方法可以接受兩個回調函數作爲參數。
    • 第一個回調函數是 Promise 對象的狀態變爲 resolved 時調用
    • 第二個回調函數是 Promise 對象的狀態變爲 rejected 時調用。
    • 其中,第二個函數是可選的,不一定要提供。這兩個函數都接受 Promise 對象傳出的值作爲參數

1. Promise 的使用

  • all()
let one = new function(){
	setTimeout(()=>{
		resolve('aa'):
	},1000);
})

let two = new function(){
	setTimeout(()=>{
		resolve('bb'):
	},1000);
})

let three = new function(){
	setTimeout(()=>{
		resolve('cc'):
	},1000);
})

// 所有異步操作執行後再調用
// 可以並行執行多個異步操作
// 並且在一個回調中處理所有數據返回結果
Promise.all([one,two,three]).then(arr=>{
	let[one,two,three] = arr;
	console.log(one);
	console.log(two);
	console.log(three);	
  • race()
let one = new function(){
	setTimeout(()=>{
		resolve('aa'):
	},1100);
})

let two = new function(){
	setTimeout(()=>{
		resolve('bb'):
	},1050);
})

let three = new function(){
	setTimeout(()=>{
		resolve('cc'):
	},1300);
})

// 多臺服務器的時候,請求資源同列,分佈式
// 同時連接多個服務器地址,做負載均衡
// 先請求到哪個服務器數據,就用哪個服務器數據
Promise.race([one,two,three]).then(arr=>{
	console.log(arr); // bb
}
  • catch()
function foo(){
	return new Promise((resolve,reject)=>{
		setTimeout(()=>{
			resolve();
		},1000)
	});
}

foo().then(()=>{
	console.log('aa');
	return foo();
}).then(()=>{
	console.log('bb');
	console,log(data);
	return foo();
}).catch(rea=>{
 	console.log('error');
 	console.log(rea);
});
  • async/await
async function demo(){
	console.log('this is a async demo');
}

// 返回Promise對象
console.log(demo());
function demo(){
	return new Promise((resolve,reject) =>{
		setTimeout(()=>{
			resolve(2*num);
		},2000)
	})
}

async function test(){
	await demo(20);
}

test();

12. generator 應用

1. generator 和 yield 的使用

  • Generator 主要用於異步編程,就是封裝一個異步任務,或者說是異步任務的容器
  • 特點是可以交出函數執行全(展停執行)
  • 在聲明函數的 function 關鍵字與函數名之間有一*(用於區別普通函數)
  • yieldGenerator 函數體內使用,可以定義不同的內部狀態(可以設置不同時候不一樣的值)
  • yield 命令是異步不同階段的分界線,有時也會把 yield 當成是 return(當然有本質區別)
  • 使用啓動 next() 方法,分階段執行 Generator 函數
function *demo(){
	console.log('1');
	yield
	console.log('2');
	yield
	console.log('3');
}

let genob = demo();
genobj.next();
genobj.next();
genobj.next();

13. Map 數據結構

  • Map 數據結構類似於對象,是鍵值對的集合,傳統的鍵只能用字符串,Map的鍵不限於字符串,各種類型的值(包括對象)都可以當作鍵

1. 屬性和操作方法

  • size 屬性
// size 屬性返回 Map 結構的成員函數
const map = new Map();
map.set('foo',true);
map.set('bar',false);

map.size // 2
  • set(key, value)
    • set 方法設置 set 方法設置鍵名 key 對應的鍵值爲 value,然後返回整個 Map 結構,如果 key 已經有值,則鍵值會被更新,否則就新生成該鍵
const m = new Map();
m.set('edition',6)       // 鍵是字符串
m.set(262,'standard')	 // 鍵是數值
m.set(undefined,'nah')	 // 鍵是 undefined

14. Module 模塊

export 命令

  • 用於規定模塊的對外接口
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

// 等同於
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export{firstName,lastName,year};
  • 一個模塊就是一個獨立的文件,該文件內部的所有變量,外部無法獲取,如果你希望外部能夠讀取模塊內部的某個變量,就必須使用 export 關鍵字輸出該變量
// 輸出函數
export function multiply(x,y){
	return x * y;
};

// as 重命名
function v1(){...}
function v2(){...}

export{
	v1 as streamV1,
	v2 as streamV2,
	v2 as streamLatesVersion
};

15. ES7-ES9 新特性

1. ES7 新功能

  • Array.prototype.includes 檢查數組中是否存在值;(區別ES6字符串的includes方法)
  • Exponentiation Operator 求冪運算(a**b等價於Math.pow(a,b))

2. ES8 部分功能

  • Object.values/entries/getOwnProertyDescriptors
  • String.prototype.padStart/padEnd
  • 函數參數列表和調用中的尾逗號(trailing commas)
  • Async Functions 異步函數(async/await

3. ES9 新增部分功能

  • 異步迭代
  • Promise.finally()
  • Rest/Spread 屬性
  • 正則表達式命名捕獲組
  • 正則表達式反向斷言
  • 非轉義序列的模版字符串
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章