快速入門全棧 - 09-1 ES6(上)

一、ECMAScript的介紹與環境配置

1. 簡介

ECMAScript是JavaScript的版本標準,在2015年6月發行。我們通常學習的JavaScript指的是ES3。

2. 環境配置

使用ES6我們需要安裝Node.js,Node.js是運行在服務器端的JavaScript,對ES6的支持度更高。

二、聲明與表達式

1. let與const

let關鍵字
let關鍵字和var關鍵字相似,都是聲明變量的時候使用的,然而let關鍵字和var關鍵字有一定區別

  • 代碼塊內有效:let命令只有在let所在的代碼塊有效,而var全局有效
  • 不能重複聲明:let只能聲明一次,var可以聲明多次
  • 不存在變量提升:var關鍵字會變量提升,及開始運行的時候變量已經存在了,在賦值之前是undefined;而let關鍵字不會變量提升,如果再聲明和賦值之前引用變量會報錯

const關鍵字
const關鍵字聲明一個只讀變量,聲明之後不可以改變,一旦聲明必須初始化。

注意,ES6規定,一個代碼塊中存在let或者const,會讓這個代碼塊的作用域封閉,即使外部作用域有相同名稱的變量也無法訪問。

const關鍵字保證的實際上不是值不變,而是引用對象的指針不發生改變,因此const修飾簡單類型(number, string, boolean)相當於定義常量,而當const修飾複雜對象的時候只能確保指針是固定的,指針指向的數據結構是否改變是無法控制的,因此const修飾複雜對象要慎重。

2. 解構賦值

解構賦值是賦值的擴展,針對於數組或對象進行模式匹配,並對其中的變量進行賦值。

數組模型的解構

let [a,b,c] = [1,2,3];
// a = 1
// b = 2
// c = 3

數組的解構賦值有以下幾個特點

  • 可嵌套:let[a, [[b], c]] = [1, [[2], 3]];
  • 可忽略:let [a, , b] = [1, 2, 3];
  • 不完全解構:let [a, , b] = [1, 2, 3];
  • 剩餘運算符:let [a, ...b] = [1, 2, 3];
  • 使用字符串:let [a,b,c,d,e] = 'hello'
  • 結構默認值:let [a=2] = [undefined] // a = 2,掃描到undefined返回默認值

對象模型的解構

let {foo, bar} = { foo: 'aaa', bar:'bbb'};
// foo = 'aaa'
// bar = 'bbb'

let {baz: foo} = {baz: 'ddd'};
// foo = 'ddd'
  • 可嵌套可忽略
  • 不完全解構:let obj = {p: [{y: 'world'}]}; let {p:[{y}, x]}= obj; // x = undefined
  • 剩餘運算符:let {a, b, ...rest} = {a:10, b:20, c:30, d:40}; // rest = {c:30,d:40}
  • 結構默認值:let {a:aa = 10, b: bb= 5} = {a:3}; // aa = 3, bb = 5
3. Symbol

Symbol是一種原始數據類型,表示獨一無二的值,來定義對象的唯一屬性名。ES6中數據類型除了Number、String、Boolean、Object、null、undefined,還新增了Symbol。

Symbol類型的變量不能用new關鍵字實例化,因爲Symbol是原始數據類型,不是對象,可以接收一個字符串類型的變量作爲參數,來提供描述。Symbol類型的每一個變量都不相等。Symbol可以作爲對象的變量名,可以保證屬性不重名。

Symbol作爲對象屬性名的時候不能使用.運算符,要使用方括號,否則取到的值爲undefined。因爲.後面是字符串,所以取到的是字符串屬性,而不是Symbol屬性。Symbol作爲屬性名時,屬性是公有的,而不是私有的,可以在類的外部訪問。但是不會出現在for…in,for…of的循環中,也不會被Object.keys()、Object.getOwnPropertyNames()返回。可以通過Object.getOwnPropertySombols()和Reflect.ownKeys()取得。

Symbol.for()方法類似單例模式,會在全局搜索是否有給定的字符串作爲參數的Symbol值,若有則返回該Symbol值,若沒有則返回一個以該字符串爲參數的Symbol值,並在全局登記。

Symbol.keyFor()會根據給定的Symbol變量的變量名返回Symbol變量中的字符串值。

三、內置對象

1. Map與Set

Map對象
Map對象保存鍵值對,任何原始值或對象都可以作爲鍵,而Object的鍵只能是字符串或Symbol。

Map獨享使用set()和get()來存儲和訪問,key可以是原始值、對象、函數或者Nan。

Map對象可以使用for…of和forEach來進行遍歷

for (var [key, value] of myMap){
	console.log(key + "=" + value);
}

for (var [key, value] of myMap.entries()){
	console.log(key + "=" + value);
}

for (var key of myMap.keys()){
	console.log(key);
}

for (var valueof myMap.values()){
	console.log(value);
}

myMap.forEach(function(value,key){
	console.log(key + " = " + value);
}, myMap)

除此之外,Map對象還有以下的操作

  • Array轉換Map myMap = new Map(array);
  • Map轉換Array array = Array.from(myMap);
  • Map的克隆 myMap2 = new Map(myMap1);
  • Map的合併 var merged = new Map([...first, ... second]);

Set對象
Set對象存儲的值總是唯一的,即Set對象無法存儲重複的值。注意:+0和-0不重複,undefined和undefined不重複,Nan和Nan不重複。

Set對象有以下的操作:

  • Array轉換爲Set var set = new Set(["value1", "value2", "value3"]);
  • Set轉換爲Array var array = [...set]
  • 取並集:var union = new Set([...a,...b]);
  • 取交集:var intersect = new Set([...a].filter(x => b.has(x)));
  • 取差集:var difference = new Set([...a].filter(x => !b.has(x)));
2. Reflect與Proxy

Proxy
Proxy像是代理模式,不直接操作對象,而是對對象的讀取、調用等操作進行攔截並處理。

Proxy是由target和handler兩個部分組成的,target是原來的對象,handler來實現對應的功能

let target = {
	name: 'Tom',
	age: 24
}
let handler = {
	get: function(target){
		return target[key];
	}
	set: function(target,key,value){
		target[key] = value;
	}
}
let proxy = new Proxy(target, handler);
proxy.name
proxy.age = 25

其中target可以是空對象,handler也可以是空對象,相當於不設置攔截操作。另外,目標對象和代理對象是淺拷貝關係,對一者的改變會影響另一者。

常用的可攔截的方法有:

  • get(target, propKey, receiver):用於target對象上propKey的讀取,可繼承
  • set(target, propKey, value, receiver):用於攔截target對象上propKey的賦值
  • apply(target,ctx,args):用於攔截函數的調用、call和reply
  • has(target, propKey):判斷target是否有某屬性
  • construct(target, args):用於攔截new關鍵字,返回值必須爲對象
  • deleteProperty(target, propKey):用於刪除屬性,如果報錯或返回false,則無法刪除
  • defineProperty(target, propKey, propDesc):用於添加屬性,若返回false,則無法添加
  • getOwnPropertyDescriptor(target, propKey):返回屬性的描述
  • getPrototypeOf(target):獲取對象原型
  • isExtensible(target):只能返回布爾值,否則會被強轉爲布爾值
  • ownKeys(target):返回所有屬性的名稱
  • preventExtensions(target):該方法必須返回一個布爾值,否則會強轉爲布爾值

Reflect
ES6將一些Object的屬於語言內部的方法移植到了Reflect對象上。下面我們來列舉一下常見的方法

  • Reflect.get(target, name, receiver);:返回target的name屬性
  • Reflect.set(target, name, value, receiver);:將target的name設置爲value。返回一個布爾值結果
  • Reflect.has(obj, name);:查找name屬性在obj對象中是否存在
  • Reflect.deleteProperty(obj, property);:刪除obj對象的property屬性,返回一個布爾值結果
  • Reflect.construct(obj, args);:相當於new target(..args);
  • Reflect.apply(func, thisArg, args);:func表示目標函數,thisArg寶石目標函數綁定的this對象,args表示函數的參數
  • Reflect.defineProperty(target, propertyKey, attributes):定義屬性
  • Reflect.getOwnPropertyDescriptor(target, propertyKey);:獲得屬性的描述
  • Reflect.isExtensible(target):查看一個對象是否可擴展
  • Reflect.preventExtension(target):讓一個對象不可擴展

一般來講Proxy和Reflect是組合使用的,下面我們用它們來實現一個觀察者模式

const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const obserbale = obj => new Proxy(obj, {set});
function set(target, key, value, receiver){
	const result = Reflect.set(target, key, value, receiver);
	queuedObservers.forEach(observer => observer());
	return result;
}
3. 字符串

ES6新增了識別字串的方法

  • includes(str):判斷是否找到參數字符串,返回布爾值
  • startsWith(str):判斷參數字符串是否在源字符串的頭部,返回布爾值
  • endsWith(str):判斷參數字符串是否在源字符串的尾部,返回布爾值
    需要注意的是,這三個函數不能將正則表達式作爲參數傳入,只能直接接收字符串

除了字符串的識別,還增加了其他方法

  • 字符串重複:console.log("HelloWorld ".repeat(2));
  • 字符串補全:console.log("h".padStart(5,'o')); 同樣也有padEnd();,默認填充字符爲空格
  • 模板字符串:`Hello’\n’World`,或者是
let name = "Mike";
let age = 25;
let info = `My Name is ${name}, I am ${age + 1} years old next year.`;

function f(){
	return "have fun!"
}
let string2 = `Game start, ${f()}`;
  • 標籤模板:把後面的模板字符串傳入前面的函數中,例如
alert`Hello World!`;
alert('Hello World!');

可以使用在過濾惡意輸入,或轉化其他語言等功能上

4. 數值

數值的改動如下

  • 二進制可以添加前綴:0b或0B
  • 八進制可以添加前綴:0o或0O
  • 新增常量Number.EPSILON,其值是1與大於1的最小浮點數之間的差(2.2204460492503130808472633361816E-16)
  • 添加最大/最小安全整數,表示能夠精確表示的整數,在2^-53到2^53之間
    通過Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER來訪問
  • Number.isFinite(num):查看一個數值是否是Infinity
  • Number.parseInt():第一個參數是給定的數字,第二個參數是要轉換的進制
5. 對象

ES6中可以直接使用變量定義對象,也可以直接定義對象

const age = 12;
const name = "Amy";
const person = {age, name};
// 等同於
const person = {age: 12, name:"Amy"};

對象中可以定義函數,可以簡寫

const person = {
	sayHi(){
		console.log("Hi");
	}
}
// 等同於
const person = {
	sayHi:function(){
		console.log("Hi");
	}
}

const person = {
	*myGenerator(){
		yield 'Hello World';
	}
}
// 等效於
const person = {
	myGenerator: function*(){
		yield 'Hello World';
	}
}

對象同樣可以使用表達式作爲屬性名,但必須放在方括號中

const obj = {
	["hel"+"lo"](){
		console.log("Hello");
	}
}

注意,屬性的簡介寫法和屬性名錶達式不能同時使用,否則會報錯

對象也可以使用拓展運算符,用於對象的複製或合併。我們可以在拓展運算符後面添加自定義屬性,同名的會被覆蓋

let person = {name:"Amy",age:12};
// 複製
let someone = {...person}

let info = {gender:"female"}
// 合併
let amyInfo = {...person, ...info};
// 添加自定義屬性
let amyInfo = {...person, ...info, id:1,code:"1A"}; 

對象添加了以下兩個新方法

  • Object.assign(target, source_1, ...) 把從第二個參數開始的屬性添加到第一個對象中,其中的屬性拷貝是淺拷貝
  • Object.is(value1,value2) 判斷兩個對象是否嚴格相等
6. 數組

數組
數組可以通過下面的方法來新建

  • Array.of(value1,value2,...):將參數傳入數組,參數可以爲不同類型,也可以返回空數組
  • Array.from(iterable):將一個可迭代對象轉化爲數組,可以含有空數位

初此之外,數組還有這些新方法

  • arr.find(item => item > 2);:查找數組中符合條件的元素,若有多個元素,則返回第一個
  • arr.findIndex(item => item = 1);:查找數組中符合條件的索引,如果有多個索引,則返回第一個
  • arr.fill(a,b,c);:將一定範圍內的數組元素內容填充爲單個指定值,a是用來填充的值,b是被填充的起始索引,c是結束的索引
  • arr.copyWithin(a,b,c):將一定範圍內的數組元素修改爲此數組另一個元素,a是被修改的起始索引,b是被用來覆蓋的數據的索引,c是結束索引
  • arr.entries():遍歷鍵值對,裏面是索引和元素
  • arr.keys():遍歷鍵名,裏面是索引
  • arr.values():遍歷元素值
  • arr.includes(item):某元素是否存在於數組中
  • [1,2.[3,4]].flat(); // [1,2,3,4]:嵌套數組轉爲一維數組
  • [1,2,3].flatMap(n => [n * 2]); // [2, 4, 6]:對每個元素執行指定函數,再執行flat()函數

數組緩衝區
數組中還有數組緩衝區對象,是內存中的一段地址,也就是其它語言中的定型數組。數組的長度在創建的時候被確定,只能修改內容,不能修改長度。

let buffer = new ArrayBuffer(10);
console.log(buffer.byteLength); // 10
let buffer1 = buffer.slice(1,3);
console.log(buffer1.byteLength); // 2

視圖
視圖是用來操作數組緩衝區的子集,並按照一種數據類型讀取和寫入數據

let buffer = new ArrayBuffer(10);
let dataView = new DataView(buffer);
dataView.setInt(0,1);
console.log(dataView.getInt8(0)); // 1
// 設置偏移量(參數2)和長度(參數3)來控制DataView操作字節範圍
let dataView2 = new DataView(buffer, 0, 3);
dataView2.setInt(5,1); // RangeError

定型數組
定型數組強制使用特定的數據類型,而不是通過DataView對象來操作數組緩存區。

// 通過數組緩存區生成
let buffer = new ArrayBuffer(10), view = new Int8Array(buffer);
// 通過構造方法
let view = new Int32Array(10);

定型數組的length屬性代表長度,不可寫。定型數組可以使用entries()、keys()和values()來迭代。find方法等方法也可應用於定型數組,但會額外檢查類型是否匹配;定型數組也有of和from方法,區別在於返回值也是定型數組。

參考資料

[1] 菜鳥教程ES6


我和幾位大佬建立了一個微信公衆號,歡迎關注後查看更多技術乾貨文章
歡迎加入交流羣QQ1107710098
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章