ES6的梳理

前言

ES6 的教程非常多用的時候只需稍稍百度即可,所以很多時候教主都是高不成低不就的,所以稍微記錄一下對教主來說有些小糾結的地方其實本身也無可厚非吧,至於像letconst、作用域、解構賦值、默認參數不定參數、箭頭函數等等的就直接查一下就行了不記錄了。

INFO

其中很多部分都來自菜鳥教程ECMAScript 6 入門以及一些很著名的網站,主要目的是記錄更經典的例子。

參考:



Symbol 類型

定義

Symbol是 ES6 新增的一個原始數據類型,表示獨一無二的值,最大的用法就是用來定義對象的唯一屬性名。

  • 因爲 Symbol 是原始數據類型,所以不能用 new 創建。
  • 因爲 Symbol 可以表示獨一無二的值,所以可以用於:作爲常量值的類型、作爲屬性名的類型。

作爲常量值的類型

作爲常量值的類型好理解,直接將常量值換爲Symbol類型的就行了:

// String 類型
const A_RED = 'RED';	
const B_RED = 'RED';

console.log(A_RED === B_RED);	// true
// Symbol 類型
const A_RED = Symbol('RED');
const B_RED = Symbol('RED');

console.log(A_RED === B_RED);	// false

PS

咋一看感覺String類型的沒毛病啊,那不就應該是這樣的嘛,那的確應該是這樣的啊???emmmmmm對於常量來說我們需要的是:常量和常量值都應該是一一對應的關係。

作爲屬性名的類型

那表示屬性呢?教主也一直很疑問:如果說表示常量是爲了嚴謹,那表示屬性名是爲了啥,難不成屬性名還會重名不成?

  • Symbol值作爲屬性名時,該屬性是公有屬性不是私有屬性,可以在類的外部訪問。
  • 不會出現在 for...infor...of 的循環中,也不會被 Object.keys()Object.getOwnPropertyNames() 返回。
  • 如果要讀取到一個對象的 Symbol 屬性,可以通過 Object.getOwnPropertySymbols()Reflect.ownKeys() 取到。

舉例:

// Symbol 作爲對象的屬性名的類型
let obj = { };						
obj[Symbol('k1')] = 'v1';
obj.k2 = 'v2';

// let obj = {[Symbol("k1")]: "v1", k2: "v2"};

console.log(obj);	// {k2: "v2", Symbol(k1): "v1"}

關於中括號運算符(obj[key])和點運算符(obj.key):

  • 中括號運算符總是能代替點運算符。但點運算符卻不一定能全部代替中括號運算符。
  • 中括號運算符可以用字符串變量的內容作爲屬性名。點運算符不能。
  • 中括號運算符可以用純數字爲屬性名。點運算符不能。
  • 中括號運算符可以用js的關鍵字和保留字作爲屬性名。點運算符不能。

這一點怎麼理解呢:

  • 首先可以聯想到C中結構體和數組的彙編是一樣的;
  • 其次JS中基於原型鏈的繼承方式,大道歸一Object.prototype
  • 最後如果從解析代碼爲語法樹來說,點運算符沒辦法解析動態的屬性,而中括號運算符只需要滿足括號內的表達式能被計算(轉換)成一個字符串就行了,比如說obj['key' + i]
  • 那麼是否可以理解成:點運算符是不是隻是爲了更好的描述JS面向對象的特徵呢。

至此還是沒得到問題的答案。又或者說“爲什麼不同的庫可能會向同一個對象添加可能重名的字段?”這屬於語言的哲學問題是另一個範疇,畢竟欲戴王冠必承其重,因爲自由所以可能。

那麼還可以從另一些特性想:

  • Symbol類型的屬性不會被輕易迭代,也不會被自動轉換爲字符串(可以使用toString()方法獲得獲得),所以可以“定製化”的JSON.stringfy()成一個JSON。
  • Symbol類型的屬性無法跨模塊訪問,所以在模塊化中,用於私有化對象大的字段或方法是個很不錯的選擇。

PS

關於Symbol在JS內部的很多應用,還有待了解。



Proxy 對象

簡單來說就是代理模式,這裏記錄一下最簡單用法:

// 目標對象
let girl = { name: "xxx", sex: 2, single: true };

// 攔截方法
let way = {
    get: function (target, property) {
        if (property === 'name') return target.name;
        else if (property === 'sex') return 4;
        else if (property === 'single') return false;
        else return 'unkown';
    }
}

// 代理對象
let bestie = new Proxy(girl, way);

QUOTE 《GB/T 2261.1-2003 性別代碼》

  • 0 = 未知項
  • 1 = 男性
  • 2 = 女性
  • 3 = 女改男,男性
  • 4 = 男改女,女性
  • 9 = 未指明
// 訪問者
let boy = {
    name: "yyy", sex: 1, single: true, askabout: function (proxy) {
        return (proxy.sex === 2 && proxy.single === true);
    }
}

let result = boy.askabout(bestie);	// 細思恐極
console.log(result);				// false



Reflect 對象

定義

Reflect是 ES6 爲了操作對象而提供的新 API。Reflect對象的設計目的有這樣幾個。

  • Object對象的一些明顯屬於語言內部的方法(比如Object.defineProperty),放到Reflect對象上。
  • 修改某些Object方法的返回結果,讓其變得更合理。
  • Object操作都變成函數行爲。
  • Reflect對象的方法與Proxy對象的方法一一對應,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法。

PS

簡單來說,Reflect是在JS的設計哲學上做改動以使其更加合理,與字面值“反射”有異曲同工之妙

靜態方法

Reflect對象有13個靜態方法,教主還有待熟悉。

Reflect.apply(func, thisArg, args)

Reflect.construct(target, args)

Reflect.get(target, name, receiver)

Reflect.set(target, name, value, receiver)

Reflect.defineProperty(target, name, desc)

Reflect.deleteProperty(target, name)

Reflect.has(target, name)

Reflect.ownKeys(target)

Reflect.isExtensible(target)

Reflect.preventExtensions(target)

Reflect.getOwnPropertyDescriptor(target, name)

Reflect.getPrototypeOf(target)

Reflect.setPrototypeOf(target, prototype)

其中大部分方法其實都可以從字面值理解,唯獨這個Reflect.apply(func, thisArg, args)方法不知所謂,請教羣友後結論:暫時將函數綁定在一個對象上來調用:

// 假設有某一函數
let func = function (param1, param2) {
    console.log(this);
    console.log(`${param1} ${param2}`);
}

// 假設有某一對象
let obj = { name: "xxx", sex: 1, single: true };
// 暫時以 obj.func() 的形式調用, 將參數放在數組中傳入
Reflect.apply(func, obj, ['param1', 'param2', 3]);	// { name: 'xxx', sex: 1, single: true } 
													// param1 param2

PS

比較糾結的是:爲什麼要暫時暫時綁定,永久綁定不好嘛!

同學說:爲什麼要永久綁定,用完就可以銷燬了呀!

教主:好吧~



Promise 對象

定義

Promise是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。所謂Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作)的結果。

PS

簡單來說,Promise就是把異步的操作用同步的直覺來寫。起碼少了嵌套回調,回調的······的那種回調。

簡單用法

const p = new Promise(function (resolve, reject) {
    // 2 秒後回調 resolve()
    setTimeout(resolve, 2000, 'done');
});

p.then(value => console.log(value));    // done

AJAX 可能是最好的模擬異步的操作了。假設某監聽 80 端口的服務器目錄下:

/					# localhost
|--- promise.html	
|--- data/
|    |--- 1.json	# order: 1
|    |--- 2.json	# order: 2
|    |--- 3.json	# order: 3

如果既要異步請求還要保證請求順序,可能會:

<!DOCTYPE html>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    axios.get('data/1.json').then((response) => {
        console.log(response.data);
        axios.get('data/2.json').then((response) => {
            console.log(response.data);
            axios.get('data/3.json').then((response) => {
                console.log(response.data);
            });
        });
    })
</script>

但是像JQ、axios等等的庫,它們的AJAX都會返回一個Promise對象,所以纔可以.then(succeedFunc)回調如果成功時的函數,或者.catch(faildFunc)回調如果失敗的函數。而剛好有一個Promise.all()的靜態方法,傳入一個Promise數組,然後統一去在.then()裏回調:

<!DOCTYPE html>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    Promise.all([
        axios.get('data/1.json'),
        axios.get('data/2.json'),
        axios.get('data/3.json')
    ]).then((results) => {
        let [r1, r2, r3] = results;
        console.log(r1.data);
        console.log(r2.data);
        console.log(r3.data);
    });
</script>



async 函數

定義

async函數是Generator函數的語法糖,語義化更好,而且會返回一個Promise對象。

  • async函數會立即返回一個Promise對象。
  • await後等待的表達式爲Promise對象時會等待執行並返回resolve()函數的結果;等待的表達式爲其他類型則該是什麼就是什麼。
  • async函數的return的值會傳給.then()時的回調函數;錯誤會傳給.catch()時的回調函數。

簡單使用

正常返回:

let func = async function () {
    let a = await (10 ** 3);
    console.log(a);                           // 1000
    let b = await new Promise((resolve, reject) => resolve('resolve'));
    console.log(b);                           // resolve
    return 'success';
}

let promise = func();
promise.then(success => console.log(success)) // success
       .catch(error => console.log(error));	 

拋出異常:

let func = async function () {
    throw new Error('error');
}

let promise = func();
promise.then(success => console.log(success))     
       .catch(error => console.log(error));   // Error: error

然後呢貌似比較簡單的就和Promise.all()那樣需要按順序讀取的效果差不多,但是控制起來就相對而言比較i方便了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章