Proxy 是 ES6 中新增的功能,它能夠用來自定義對象中的操做。 Vue3.0 中將會經過 Proxy 來替換本來的 Object.defineProperty 來實現數據響應式。es6
let p = new Proxy(target, handler)
target
表明須要添加代理的對象,handler
用來自定義對象中的操做,好比能夠用來自定義 set 或者 get 函數。ajax
let onWatch = (obj, setBind, getLogger) => { let handler = { set(target, property, value, receiver) { setBind(value, property) return Reflect.set(target, property, value) }, get(target, property, receiver) { getLogger(target, property) return Reflect.get(target, property, receiver) } } return new Proxy(obj, handler) } let obj = { a: 1 } let p = onWatch( obj, (v, property) => { console.log(`監聽到屬性${property}改變爲${v}`) }, (target, property) => { console.log(`'${property}' = ${target[property]}`) } ) p.a = 2 // 控制檯輸出:監聽到屬性a改變 p.a // 'a' = 2
自定義 set 和 get 函數的方式,在本來的邏輯中插入了咱們的函數邏輯,實現了在對對象任何屬性進行讀寫時發出通知。數組
固然這是簡單版的響應式實現,若是須要實現一個 Vue 中的響應式,須要咱們在 get 中收集依賴,在 set 派發更新,之因此 Vue3.0 要使用 Proxy 替換本來的 API 緣由在於 Proxy 無需一層層遞歸爲每一個屬性添加代理,一次便可完成以上操做,性能上更好,而且本來的實現有一些數據更新不能監聽到,可是 Proxy 能夠完美監聽到任何方式的數據改變,惟一缺陷可能就是瀏覽器的兼容性很差了。promise
map 做用是生成一個新數組,遍歷原數組,將每一個元素拿出來作一些變換而後返回一個新數組,原數組不發生改變。瀏覽器
map 的回調函數接受三個參數,分別是當前索引元素,索引,原數組安全
var arr = [1,2,3]; var arr2 = arr.map(item => item + 1) arr //[ 1, 2, 3 ] arr2 // [ 2, 3, 4 ]
['1','2','3'].map(parseInt) // -> [ 1, NaN, NaN ]
filter 的做用也是生成一個新數組,在遍歷數組的時候將返回值爲 true 的元素放入新數組,咱們能夠利用這個函數刪除一些不須要的元素app
filter 的回調函數接受三個參數,分別是當前索引元素,索引,原數組異步
reduce 能夠將數組中的元素經過回調函數最終轉換爲一個值。
若是咱們想實現一個功能將函數裏的元素所有相加獲得一個值,可能會這樣寫代碼async
const arr = [1, 2, 3] let total = 0 for (let i = 0; i < arr.length; i++) { total += arr[i] } console.log(total) //6
可是若是咱們使用 reduce 的話就能夠將遍歷部分的代碼優化爲一行代碼模塊化
const arr = [1, 2, 3] const sum = arr.reduce((acc, current) => acc + current, 0) console.log(sum)
對於 reduce 來講,它接受兩個參數,分別是回調函數和初始值,接下來咱們來分解上述代碼中 reduce 的過程
this
,arguments
__proto__
Promise
翻譯過來就是承諾的意思,這個承諾會在將來有一個確切的答覆,而且該承諾有三種狀態,這個承諾一旦從等待狀態變成爲其餘狀態就永遠不能更改狀態了。
當咱們在構造 Promise 的時候,構造函數內部的代碼是當即執行的。
new Promise((resolve, reject) => { console.log('new Promise') resolve('success') }) console.log('finifsh') // 先打印new Promise, 再打印 finifsh
Promise 實現了鏈式調用,也就是說每次調用 then 以後返回的都是一個 Promise,而且是一個全新的 Promise,緣由也是由於狀態不可變。若是你在 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 })
固然了,Promise 也很好地解決了回調地獄的問題
ajax(url) .then(res => { console.log(res) return ajax(url1) }).then(res => { console.log(res) return ajax(url2) }).then(res => console.log(res))
其實它也是存在一些缺點的,好比沒法取消 Promise,錯誤須要經過回調函數捕獲。
一個函數若是加上 async ,那麼該函數就會返回一個 Promise
async function test() { return "1" } console.log(test()) // -> Promise {<resolved>: "1"}
async 就是將函數返回值使用 Promise.resolve() 包裹了下,和 then 中處理返回值同樣,而且 await 只能配套 async 使用。
async function test() { let value = await sleep() }
async 和 await 能夠說是異步終極解決方案了,相比直接使用 Promise 來講,優點在於處理 then 的調用鏈,可以更清晰準確的寫出代碼,畢竟寫一大堆 then 也很噁心,而且也能優雅地解決回調地獄問題。
固然也存在一些缺點,由於 await 將異步代碼改形成了同步代碼,若是多個異步代碼沒有依賴性卻使用了 await 會致使性能上的下降。
async function test() { // 如下代碼沒有依賴性的話,徹底可使用 Promise.all 的方式 // 若是有依賴性的話,其實就是解決回調地獄的例子了 await fetch(url) await fetch(url1) await fetch(url2) }
看一個使用 await 的例子:
let a = 0 let b = async () => { a = a + await 10 console.log('2', a) } b() a++ console.log('1', a) //先輸出 ‘1’, 1 //在輸出 ‘2’, 10
上述解釋中提到了 await 內部實現了 generator,其實 await 就是 generator 加上 Promise 的語法糖,且內部實現了自動執行 generator。
function wait() { return new Promise(resolve => setTimeout(resolve, 1000) ) } async function main() { console.time(); const x = wait(); const y = wait(); const z = wait(); await x; await y; await z; console.timeEnd(); } main();
答案: 輸出耗時: 1秒多一點點。
緣由: 3個wait函數在賦值的時候就已經開始執行了。
稍微改造一下就能夠獲得3 * 1000 ms以上的結果
function wait () { return new Promise( resolve => setTimeout(resolve, 1000) ) } async function main () { console.time() const x = await wait() const y = await wait() const z = await wait() console.timeEnd() } main()
function *foo(x) { let y = 2 * (yield (x + 1)) let z = yield (y / 3) return (x + y + z) } let it = foo(5) console.log(it.next()) // => {value: 6, done: false} console.log(it.next(12)) // => {value: 8, done: false} console.log(it.next(13)) // => {value: 42, done: true}
首先 Generator 函數調用和普通函數不一樣,它會返回一個迭代器
當執行第一次 next 時,傳參會被忽略,而且函數暫停在 yield (x + 1) 處,因此返回 5 + 1 = 6
當執行第二次 next 時,傳入的參數等於上一個 yield 的返回值,若是你不傳參,yield 永遠返回 undefined。此時 let y = 2 * 12,因此第二個 yield 等於 2 * 12 / 3 = 8
當執行第三次 next 時,傳入的參數會傳遞給 z,因此 z = 13, x = 5, y = 24,相加等於 42
當yeild產生一個值後,生成器的執行上下文就會從棧中彈出。但因爲迭代器一直保持着隊執行上下文的引用,上下文不會丟失,不會像普通函數同樣執行完後上下文就被銷燬
ES Module 是原生實現的模塊化方案,與 CommonJS 有如下幾個區別
// 引入模塊 API import XXX from './a.js' import { XXX } from './a.js' // 導出模塊 API export function a() {} export default function() {}
私有方法和私有屬性,是隻能在類的內部訪問的方法和屬性,外部不能訪問。這是常見需求,有利於代碼的封裝,但 ES6 不提供,只能經過變通方法模擬實現。
一種作法是在命名上加以區別,即在函數名或屬性名前加_
,但這並不安全,只是一種團隊規範。
另外一種方法就是索性將私有方法移出類,放到模塊裏,由於模塊內部的全部方法都是對外可見的。
class Widget { foo (baz) { bar.call(this, baz); } // ... } function bar(baz) { return this.snaf = baz; }
上面代碼中,foo是公開方法,內部調用了bar.call(this, baz)。這使得bar實際上成爲了當前模塊的私有方法。
還有一種方法是利用Symbol值的惟一性,將私有方法的名字命名爲一個Symbol值。
const bar = Symbol('bar'); const snaf = Symbol('snaf'); export default class myClass{ // 公有方法 foo(baz) { this[bar](baz); } // 私有方法 [bar](baz) { return this[snaf] = baz; } // ... };
上面代碼中,bar和snaf都是Symbol值,通常狀況下沒法獲取到它們,所以達到了私有方法和私有屬性的效果。可是也不是絕對不行,Reflect.ownKeys()依然能夠拿到它們。
const inst = new myClass(); Reflect.ownKeys(myClass.prototype) // [ 'constructor', 'foo', Symbol(bar) ]
Proxy 能夠理解成,在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這裏表示由它來「代理」某些操做,能夠譯爲「代理器」。
var obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); } });
Proxy 支持的攔截操做一覽,一共 13 種。