👇 內容速覽 👇javascript
ES6新增了let
和const
,它們聲明的變量,都處於「塊級做用域」。而且不存在「變量提高」,不容許重複聲明。前端
同時,const
聲明的變量所指向的內存地址保存的數據不得改變:java
const
只能保證這個指針是固定的(即老是指向另外一個固定的地址),不能保證指向的數據結構不可變。若是要保證指向的數據結構也不可變,須要自行封裝:webpack
/** * 凍結對象 * @param {Object} obj * @return {Object} */ function constantize(obj) { if(Object.isFrozen(obj)) { return obj } Reflect.ownKeys(obj).forEach(key => { // 若是屬性是對象,遞歸凍結 typeof obj[key] === 'object' && (obj[key] = constantize(obj[key])) }); return Object.freeze(obj) } /********測試代碼 **********/ const obj = { a: 1, b: { c: 2, d: { a: 1 } }, d: [ 1, 2 ] } const fronzenObj = constantize(obj) try { fronzenObj.d = [] fronzenObj.b.c = 3 } catch(error) { console.log(error.message) }
題目:解釋下Set
和Map
。
①Set經常使用方法css3
// 實例化一個set const set = new Set([1, 2, 3, 4]); // 遍歷set for (let item of set) { console.log(item); } // 添加元素,返回Set自己 set.add(5).add(6); // Set大小 console.log(set.size); // 檢查元素存在 console.log(set.has(0)); // 刪除指定元素,返回bool let success = set.delete(1); console.log(success); set.clear();
其餘遍歷方法:因爲沒有鍵名,values()
和keys()
返回一樣結果。git
for (let item of set.keys()) { console.log(item); } for (let item of set.values()) { console.log(item); } for (let item of set.entries()) { console.log(item); }
②Map經常使用方法es6
Map接口基本和Set一致。不一樣的是增長新元素的API是:set(key, value)
github
const map = new Map(); // 以任意對象爲 Key 值 // 這裏以 Date 對象爲例 let key = new Date(); map.set(key, "today"); console.log(map.get(key));
generator
函數是es6提供的新特性,它的最大特色是:控制函數的執行。讓咱們從網上最火的一個例子來看:web
function* foo(x) { var y = 2 * (yield x + 1); var z = yield y / 3; return x + y + z; } var b = foo(5); b.next(); // { value:6, done:false } b.next(12); // { value:8, done:false } b.next(13); // { value:42, done:true }
通俗的解釋下爲何會有這種輸出:
done
爲true
。難點:在於爲何最後的value
是42呢?
首先,x
的值是剛開始調用foo函數傳入的5。而最後傳入的13被看成第二個yield的返回值,因此z
的值是13。對於y
的值,咱們在前面第三步中已經計算出來了,就是24。
因此,x + y + z = 5 + 24 + 13 = 42
看懂了上面的分析,再看下面這段代碼就很好理解了:
function* foo(x) { var y = 2 * (yield x + 1); var z = yield y / 3; return x + y + z; } var a = foo(5); a.next(); // Object{value:6, done:false} a.next(); // Object{value:NaN, done:false} a.next(); // Object{value:NaN, done:true}
只有第一次調用next函數的時候,輸出的value是6。其餘時候因爲沒有給next傳入參數,所以yield的返回值都是undefined
,進行運算後天然是NaN
。
簡單概括下 Promise:三個狀態、兩個過程、一個方法
pending
、fulfilled
、rejected
兩個過程(單向不可逆):
pending
->fulfilled
pending
->rejected
then
:Promise
本質上只有一個方法,catch
和all
方法都是基於then
方法實現的。請看下面這段代碼:
// 構造 Promise 時候, 內部函數當即執行 new Promise((resolve, reject) => { console.log("new Promise"); resolve("success"); }); console.log("finifsh"); // then 中 使用了 return,那麼 return 的值會被 Promise.resolve() 包裝 Promise.resolve(1) .then(res => { console.log(res); // => 1 return 2; // 包裝成 Promise.resolve(2) }) .then(res => { console.log(res); // => 2 });
async
函數返回一個Promise
對象,可使用then
方法添加回調函數。
當函數執行的時候,一旦遇到await
就會先返回,等到異步操做完成,再接着執行函數體內後面的語句。
這也是它最受歡迎的地方:能讓異步代碼寫起來像同步代碼,而且方便控制順序。
能夠利用它實現一個sleep
函數阻塞進程:
function sleep(millisecond) { return new Promise(resolve => { setTimeout(() => resolve, millisecond) }) } /** * 如下是測試代碼 */ async function test() { console.log('start') await sleep(1000) // 睡眠1秒 console.log('end') } test() // 執行測試函數
雖然方便,可是它也不能取代Promise
,尤爲是咱們能夠很方便地用Promise.all()
來實現併發,而async/await
只能實現串行。
function sleep(second) { return new Promise(resolve => { setTimeout(() => { console.log(Math.random()); resolve(); }, second); }); } async function chuanXingDemo() { await sleep(1000); await sleep(1000); await sleep(1000); } async function bingXingDemo() { var tasks = []; for (let i = 0; i < 3; ++i) { tasks.push(sleep(1000)); } await Promise.all(tasks); }
運行bingXingDemo()
,幾乎同時輸出,它是併發執行;運行chuanXingDemo()
,每一個輸出間隔1s,它是串行執行。
題目:es6 class 的new實例和es5的new實例有什麼區別?
在ES6
中(和ES5
相比),class
的new
實例有如下特色:
class
的構造參數必須是new
來調用,不能夠將其做爲普通函數執行es6
的class
不存在變量提高prototype
上的方法能夠枚舉。爲此我作了如下測試代碼進行驗證:
console.log(ES5Class()) // es5:能夠直接做爲函數運行 // console.log(new ES6Class()) // 會報錯:不存在變量提高 function ES5Class(){ console.log("hello") } ES5Class.prototype.func = function(){ console.log("Hello world") } class ES6Class{ constructor(){} func(){ console.log("Hello world") } } let es5 = new ES5Class() let es6 = new ES6Class() // 推薦在循環對象屬性的時候,使用for...in // 在遍歷數組的時候的時候,使用for...of console.log("ES5 :") for(let _ in es5){ console.log(_) } // es6:不可枚舉 console.log("ES6 :") for(let _ in es6){ console.log(_) }
參考/推薦:《JavaScript建立對象—從es5到es6》
他能夠實現js中的「元編程」:在目標對象以前架設攔截,能夠過濾和修改外部的訪問。
它支持多達13種攔截操做,例以下面代碼展現的set
和get
方法,分別能夠在設置對象屬性和訪問對象屬性時候進行攔截。
const handler = { // receiver 指向 proxy 實例 get(target, property, receiver) { console.log(`GET: target is ${target}, property is ${property}`) return Reflect.get(target, property, receiver) }, set(target, property, value, receiver) { console.log(`SET: target is ${target}, property is ${property}`) return Reflect.set(target, property, value) } } const obj = { a: 1 , b: {c: 0, d: {e: -1}}} const newObj = new Proxy(obj, handler) /** * 如下是測試代碼 */ newObj.a // output: GET... newObj.b.c // output: GET... newObj.a = 123 // output: SET... newObj.b.c = -1 // output: GET...
運行這段代碼,會發現最後一行的輸出是 GET ...
。也就是說它觸發的是get
攔截器,而不是指望的set
攔截器。這是由於對於對象的深層屬性,須要專門對其設置Proxy。
更多請見:《阮一峯ES6入門:Proxy》
目前js社區有4種模塊管理規範:AMD、CMD、CommonJS和EsModule。 ES Module 是原生實現的模塊化方案,與 CommonJS 有如下幾個區別:
require(${path}/xx.js)
,後者目前不支持,可是已有提案:import(xxx)
require/exports
來執行的《前端知識體系》
《設計模式手冊》
《Webpack4漸進式教程》