知足如下條件之一:git
定義: 有權訪問另外一個函數做用域中變量的函數(來源紅寶書
)github
柯里化(Currying),把接受多個參數的函數轉換成接受一個單一參數的函數編程
let add = function(x) {
return function(y) {
return x + y
}
}
add(3)(4) // 7
複製代碼
實際開發若是須要用到 柯里化,推薦使用 lodash.curry 設計模式
typeof
沒法判斷 對象 類型數組
constructor
判斷 是誰構造出來的promise
instanceof
判斷 誰是誰的實例(即引用類型)瀏覽器
Object.prototype.toString.call()
完美判斷閉包
function isType(type) {
return function (content) {
return Object.prototype.toString.call(content) === `[object ${type}]`
}
}
let isString = isType('String')
console.log(isString('132456'))
// 函數柯里化
console.log(isType('Number')(132456))
複製代碼
也成爲 裝飾者模式異步
定義:
指在不修改原有代碼的狀況下增長新功能函數
function sleep(who) {
who?console.log(who + '睡覺'):console.log('睡覺')
}
Function.prototype.before = function (callback) {
return (...args)=> {// args 傳入的參數數組
callback()
args?this(...args):this() // 傳入參數
}
}
let Wash = sleep.before(function () {
console.log('洗臉')
})
Wash() // 洗臉 睡覺
Wash('我') // 洗臉 我睡覺
複製代碼
有掙議
,有些人說這個不算是觀察者模式。我的以爲算是屬於
let e = {
_obg:{}, // 事件
_callback:[], // 處理函數列表
on(callback) { // 訂閱
this._callback.push(callback)
},
emit(key,value){ // 發佈
this._obg[key] = value
this._callback.forEach(fn=>{
fn(this._obg) // 參數傳入
})
}
}
e.on(function (obj) {
console.log('發佈一個')
console.log(obj)
})
setTimeout(function () {
e.emit('name','琛')
},1000)
複製代碼
舉一個簡單的例子,即微博,你關注了A,A發動態就會通知你。你和A沒有直接聯繫,經過 微博本身的調度來完成
發佈訂閱模式
是 二者之間沒有直接關係,經過實踐調度中心來完成。而觀察者模式
是相互依賴的,一個改變。另外一個也發生改變
// 設計模式 觀察者模式
// 與發佈訂閱二者區別
// 發佈訂閱 是基於一箇中間管理 on 和 emit 沒有直接關係
// 觀察者模式 是 Observer and Observed 有直接關係
// 一個依賴改變,另外一個也改變 Vue
class Observer { // 觀察者
constructor(name) { // 傳遞參數
this.name = name
}
update(baby){
console.log(this.name+'知道'+baby.name+baby.state)
}
}
class Observed{ // 被觀察者
constructor(name) {
this.name = name
this.state = '開心'
this.Observer = []
}
addObserver(o){
this.Observer.push(o)
}
setState(state){
this.state = state
this.Observer.forEach(o=>{
o.update(this)
})
}
}
let baby = new Observed('寶寶')
let dad = new Observer('爸爸')
let mom = new Observer('媽媽')
// 添加觀察者
baby.addObserver(dad)
baby.addObserver(mom)
// 設置狀態
baby.setState('不開心')
baby.setState('開心')
複製代碼
用來解決異步
Promise 是一個天生的類 須要傳入一個函數 默認會當即執行
有三種狀態 ,成功(resolve) ,失敗(reject), 等待
let a = new Promise((resolve, reject) => {
// 這兩個方法能夠更改promise 狀態
// resolve()若是是這樣 下面的輸出爲 undefined
resolve('成功')
// reject('失敗')
})
a.then((data)=>{
console.log(data) // 成功
},(err)=>{
console.log(err) // 失敗
})
複製代碼
resolve,reject 這兩個方法能夠改變狀態。若是是 resolve
,則走then
的第一個函數,reject
走then
的第二個函數。而且都將參數傳入。
**注意:**走成功了就不能夠走失敗,反之亦然
明白了基本用法,咱們開始模仿一下Promise
。這裏使用ES6
語法來模仿。
首先,須要就收一個executor
,是一個函數。當即執行,有三那種狀態,裏面還有resolve
,reject
兩個參數,這兩個參數是函數,須要接收兩個參數。同時promise
還有then
方法
// promise 同步
// 三種狀態的定義
const Pending = 'PENDING'
const Success = 'SUCCESS'
const Error = 'Error'
// promise 的實現
class WritePromise {
constructor(fn) { // 初始化
this.state = Pending // 狀態初始化
this.value = undefined // 成功信息
this.err = undefined // 錯誤信息
let resolve = (value)=>{
if (this.state === Pending){
this.value = value
this.state = Success
}
}
let reject = (err)=>{
if (this.state === Pending){
this.value = err
this.state = Error
}
}
// 可能會出錯
try {
fn(resolve,reject) // 當即執行
}catch (e) {
console.log(e) // 若是內部出錯 直接交給reject 方法向下傳遞
reject(e)
}
}
then(onfulfilled,onrejected){
switch (this.state) {
case Success:
onfulfilled(this.value)
break
case Error:
onrejected(this.err)
break
}
}
}
// export default WritePromise 瀏覽器端
module.exports = WritePromise
複製代碼
在new的過程當中,執行constructor
,傳入的 resolve
orreject
,進行賦值和狀態改變。而後then
方法更具 state
的狀態進行不一樣的操做。onfulfilled
and onrejected
是傳入的操做函數
爲何要加try catch
在使用過程當中,不只僅只有reject
能夠接收錯誤,也能夠手動拋出錯誤。這樣就reject
捕獲不到錯誤。 因此要加上 try catch 。 保證能夠正常運行
let WritePromise = require('./WritePromise')
let promise = new WritePromise((resolve, reject) => {
// 1.
resolve('成功')
// 2.
// reject('失敗')
})
promise.then((data) => { // onfulfilled 成功
console.log(data)
}, (err) => { // onrejected 失敗
console.log(err)
})
// 輸出 1. 成功 2. 失敗
複製代碼
你們會發現。若是在resolve
orreject
,執行異步代碼(例如定時器)。會發現沒有結果。這是由於咱們剛纔寫的都是同步代碼。如今要改一下,改爲異步的
這時候就用到咱們前面的知識了,發佈訂閱模式
首先,咱們應該知道在constructor
中傳入的fn
,若是加上定時器的話,它的狀態state
不會發生任何改變。也就是一直處於等待狀態
, 因此並不會執行then
裏面的函數。因此咱們應該考慮一下當他處於等待
的時候。是否是應該吧傳入的函數存儲起來,等到上面執行resolve
orreject
的時候,再把這個函數執行。
// promise 異步
const Pending = 'PENDING'
const Success = 'SUCCESS'
const Error = 'Error'
class WritePromiseAsync {
constructor(fn) {
this.state = Pending
this.value = undefined
this.err = undefined
// 回調函數的存儲
this.SuccessCal = []
this.ErrorCal = []
let resolve = (value)=>{
if (this.state === Pending){
this.value = value
this.state = Success
// 對回調函數進行變量 而後執行
this.SuccessCal.forEach((fn)=>fn())
}
}
let reject = (err)=>{
if (this.state === Pending){
this.value = err
this.state = Error
this.ErrorCal.forEach((fn)=>fn())
}
}
// 可能會出錯
try {
fn(resolve,reject) // 當即執行
}catch (e) {
console.log(e) // 若是內部出錯 直接交給reject 方法向下傳遞
reject(e)
}
}
then(onfulfilled,onrejected){
switch (this.state) {
case Success:
onfulfilled(this.value)
break
case Error:
onrejected(this.err)
break
case Pending:
this.SuccessCal.push(()=>{
// 爲何要這樣寫 由於這樣能夠作一些邏輯 AOP
// 這裏面能夠作一些邏輯
onfulfilled(this.value)
})
this.ErrorCal.push(()=>{
onrejected(this.err)
})
break
}
}
}
module.exports = WritePromiseAsync
複製代碼
在順一遍。 建立對象以後,調用then
方法, 代碼開始執行,執行到then
的時候,發現沒有對應的狀態改變,就先把它存儲起來。等到定時器結束以後,在把全部的函數都執行一次
then
)// 第二點的 代碼解釋
let b = new Promise((resolve, reject) => {
resolve('data')
}).then((data)=>{
data = data + '132456'
// then能夠返回一個值,若是是普通值。就會走到下一個then 的成功中
return data
}).then((data)=>{
console.log(data) // 輸出 data132456
})
複製代碼
若是返回的不是普通值,是promise,則會使用這個promise的結果
let b = new Promise((resolve, reject) => {
resolve('data')
}).then((data)=>{
data = data + '132456'
return data
}).then(()=>{
return new Promise((resolve, reject) => {
resolve('我是promise 的返回')
// 若是返回的是一個promise,那麼會採用這個promise的結果
})
}).then((data)=>{
console.log(data) // 輸出 我是promise 的返回
})
複製代碼
用來捕獲 最近的且沒有捕獲的錯誤
let b = new Promise((resolve, reject) => {
reject('data')
}).then().catch(err=>{ // 捕獲錯誤 捕獲最近的沒有捕獲的錯誤
console.log(err+'catch') // datacatch
// 注意 返回的也是undefined
})
複製代碼
上述走的是成功
,失敗也同樣。但會有一個小坑。
let b = new Promise((resolve, reject) => {
resolve('data')
}).then(()=>{},err=>{
console.log(err)
// 在失敗函數中若是返回的是一個普通值,也會走下一次then的成功中
// return undefined 至關於返回了一個這個
}).then((data)=>{
console.log(data+'success') // 這個會走 成功的值 輸出 underfinedsuccess
},(err)=>{
console.log(err+'err')
})
複製代碼
特別注意,這裏會常常有遺漏。
接着上次的WritePromiseAsync
原版作法中,當連續調用then
方法的時候,會把上一次的結果傳遞給下一個then
。
上面說過每次調用then
方法會返回一個promise
實例。因此,咱們須要在調用then
方法的時候返回一個promise
的實例,而且接收到then
方法的結果。在傳遞給這個promise
// 多餘的我就不寫了,主要寫差別化 的 then方法
then(onfulfilled, onrejected) {
let promise2 = new ChainPromise((resolve, reject) => {
let x
switch (this.state) {
case Success:
x = onfulfilled(this.value)
resolve(x)
break
case Error:
x = onrejected(this.value)
reject(x)
break
case Pending:
this.SuccessCal.push(() => {
try {
let x = onfulfilled(this.value)
resolve(x)
} catch (e) {
reject(e)
}
})
this.ErrorCal.push(() => {
try {
let x = onrejected(this.err)
reject(x)
} catch (e) {
reject(e)
}
})
break
}
})
return promise2
}
複製代碼
注意,調用的時候要把 then
的兩個函數都要寫上,不然會報錯(尚未處理)
這樣事後 就能夠實現 屢次then方法傳遞結果了
說一下上面得哪一個x
,咱們是直接把它返回給對應得處理方法,若是x
是一個promise
呢? 按照原版得來講。咱們應該把這個promise
的結果做爲返回值來繼續傳遞。因此咱們應該對這個x
進行處理
建立一個方法solveX
,來處理x
。
function solveX(promise2, x, resolve, reject) {
if (promise2 === x){
return reject(new TypeError('引用自己'))
}
if ((typeof x === 'object' && x != null)|| typeof x === 'function'){
// 處理promise
try {
let then = x.then
if (typeof then === 'function'){ // 只能認定他是promise了
then.call(x,(data)=>{
console.log(data)
resolve(data)
},(err)=>{
reject(err)
})
} else {
resolve(x)
}
} catch (e) {
reject(e) // 取值失敗 走err
}
}else {
// 是一個普通值
resolve(x)
}
}
複製代碼
爲何要把promise2
傳進來呢? 由於若是 x
就是promise2
呢?則會是一個死循環。
對x
進行判斷,若是是普通值,直接返回就能夠了。若是不是,咱們取then
方法(注意是方法,不是結果). 若是有這個方法,咱們就認定他是一個promise
(可能有人會說若是then
是一個空方法呢?,那也只能認定了,咱們最多隻能作到這種程度的判斷了。)
注意then
的this 指向問題
try {
x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
// 須要用x 來比較promise2的值
// resolve()
} catch (e) { // 一旦出錯,走下一個promise 的錯誤處理方法
reject(e)
}
複製代碼
若是直接傳入promise2
的話,由於是同步的過程,在建立的時候promise2
尚未生成,因此會報錯。這時候咱們能夠加一個定時器,把它變成異步。這就解決了這個問題
then(onfulfilled, onrejected) {
let promise2 = new ChainPromise((resolve, reject) => {
let x
switch (this.state) {
case Success:
setTimeout(() => { // 若是不加定時器,promise2獲取不到
try {
x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject) // 須要用x 來比較promise2的值
// resolve()
} catch (e) { // 一旦出錯,走下一個promise 的錯誤處理方法
reject(e)
}
}, 0)
// 實現以後要判斷 X 若是x是一個普通值,就正常返回。若是是一個promise 則把promise的執行結果做爲參數傳遞給 相應的處理函數
break
case Error:
setTimeout(() => {
try {
x = onrejected(this.err)
// reject(x)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
break
case Pending:
this.SuccessCal.push(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.ErrorCal.push(() => {
try {
let x = onrejected(this.err)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
break
}
})
return promise2
}
複製代碼
注意,即便寫的是0
,也不會當即執行。
上面咱們寫了一個方法來處理promise
,只須要進行一個遞歸就能夠解決
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('引用自己'))
}
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
// 處理promise
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, (data) => {
// resolve(data) 將data從新放入這個函數。直到是一個普通值再進行返回
resolvePromise(promise2, data, resolve, reject)
}, (err) => {
// reject(err)
resolvePromise(promise2, err, resolve, reject)
})
} else {
resolve(x)
}
} catch (e) {
reject(e) // 取值失敗 走err
}
} else {
// 是一個普通值
resolve(x)
}
}
複製代碼
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function'?onfulfilled:v=>v
onrejected = typeof onrejected === 'function'?onrejected:err=>{throw err}
........
}
複製代碼
將兩個函數進行判斷。若是不是函數,默認賦一個函數
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>--'))
}
let called // 添加一個變量進行控制
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return // 若是發現被調用過 直接return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
複製代碼
// promise 鏈式調用的實現
const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED '
const REJECTED = 'REJECTED'
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>--'))
}
let called
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, r => {
if (called) return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.SuccessCal = []
this.ErrorCal = []
let resolve = value => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.SuccessCal.forEach(fn => fn())
}
}
let reject = reason => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.ErrorCal.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onRESOLVED, onrejected) {
onRESOLVED = typeof onRESOLVED === 'function' ? onRESOLVED : v => v
onrejected = typeof onrejected === 'function' ? onrejected : err => {
throw err
}
let promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => { // 若是不加定時器,promise2獲取不到
try {
let x = onRESOLVED(this.value)
resolvePromise(promise2, x, resolve, reject) // 須要用x 來比較promise2的值
} catch (e) { // 一旦出錯,走下一個promise 的錯誤處理方法
reject(e)
}
}, 0)
// 實現以後要判斷 X 若是x是一個普通值,就正常返回。若是是一個promise 則把promise的執行結果做爲參數傳遞給 相應的處理函數
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === PENDING) {
this.SuccessCal.push(() => { // 爲何要這樣寫 由於這樣能夠作一些邏輯
// 這裏面能夠作一些邏輯
setTimeout(() => {
try {
let x = onRESOLVED(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.ErrorCal.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return promise2
}
}
// 測試 須要測試再添加
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
複製代碼
這個是測試工具的github
安裝以後, 執行
npx promises-aplus-tests promise.js
爲何是npx
? 我沒有全局安裝
所有經過