摘要:使用既然初衷是為了方便管理,那么就做一個(gè)關(guān)于的。打開這個(gè)的情況下,還會(huì)獲得一些調(diào)試用的信息。如果需要,可以在中對(duì)實(shí)際入?yún)⑦M(jìn)行特殊處理,比如轉(zhuǎn)換對(duì)象特定屬性檢查等等。
在 JavaScript 中使用接口
這篇是 js-interface 的 README,雖然并不是很復(fù)雜的一個(gè)東西,如果有人看的話我就寫寫源碼思路了 ORZ
介紹在做一個(gè)前后分離的項(xiàng)目時(shí),有些頭疼 Api 之類的東西要怎么管理,在閱讀 《JavaScript 設(shè)計(jì)模式》 一書時(shí),第二章提到了在 JavaScript 中模擬接口 (interface) 的概念,以方便使用眾多設(shè)計(jì)模式,因此嘗試著做一個(gè)接口的模擬。由于我本職是一名后端 Java 開發(fā),因此希望在這個(gè)模擬層可以加入 接口默認(rèn)實(shí)現(xiàn)、接口繼承、方法重載 等能力,雖然這些東西加上之后不可避免得會(huì)在性能上有所犧牲,但對(duì)我來說可以提升一些開發(fā)體驗(yàn)(我知道 TypeScript,只是想搞個(gè)輪子試試 :P)。
使用既然初衷是為了方便管理 Api,那么就做一個(gè)關(guān)于 Api 的 demo。
創(chuàng)建一個(gè)接口const config = { // 接口的名字 name: "IApi", // 是否打開此接口的 debug 開關(guān) // 開發(fā)時(shí)必須打開,否則不會(huì)啟動(dòng) (方法聲明、方法實(shí)現(xiàn)等)入?yún)⒌念愋蜋z查。 // 打開這個(gè)的情況下,還會(huì)獲得一些調(diào)試用的信息。 debug: true, } let IApi = new Interface(config)聲明方法
最簡(jiǎn)單的聲明方式:
IApi.method({name: "getName"}) // 等價(jià)于 IApi.method({name: "getName", args: undefined})
這樣就聲明了 IApi 接口含有一個(gè) getName 方法,它沒有任何參數(shù),也沒有默認(rèn)實(shí)現(xiàn),這就要求在后面任何 IApi 的子接口或?qū)崿F(xiàn)類必須實(shí)現(xiàn)該方法,否則會(huì)拋出一個(gè)異常。
如果想指定方法的參數(shù)列表:
IApi.method({ name: "getName", args: null })
注意!
args 為 null 時(shí)表示該方法可以接受任意數(shù)量的任意參數(shù),如果重載了一個(gè)方法(詳細(xì)的請(qǐng)參閱后面關(guān)于重載的說明):
// 聲明一個(gè)空參方法 IApi.method({ id: 0, name: "getName", args: null }) // 重載上面的方法,使其有且只有一個(gè) "string" 類型,名為 name 的參數(shù) IApi.method({ id: 1, name: "getName", args: [ {name: "name", type: "string", support: val => typeof val === "string"} ] })
注意!
在參數(shù)描述中,type 屬性只是一個(gè)字符串值,它并不真的代表參數(shù)的實(shí)際類型。它跟 name 屬性一樣只是提供用于調(diào)試的信息,因此你可以填入任何你認(rèn)為合適的、足以標(biāo)記該參數(shù)一些信息的字符串值。
真正決定方法是否接受該參數(shù)的是 support 屬性,當(dāng)它返回 true 時(shí)會(huì)檢查下一個(gè)參數(shù)直到所有參數(shù)檢查完畢或某個(gè)位置的參數(shù)不被接受。
如果需要,可以在 support 中對(duì)實(shí)際入?yún)⑦M(jìn)行特殊處理,比如轉(zhuǎn)換對(duì)象、特定屬性檢查等等。
如果想為方法提供默認(rèn)實(shí)現(xiàn):
IApi.method({ name: "getName", // 默認(rèn)實(shí)現(xiàn),不能為箭頭函數(shù)! implement: function() { return "IApi" } })
回到我們的 demo,綜合運(yùn)用一下:
// 聲明兩個(gè)方法,它們都沒有參數(shù),也不需要重載因此這樣就可以了 IApi .method({ name: "getName" }) // 項(xiàng)目中使用 Axios,因此這里需要一個(gè)方法來獲取 axios 實(shí)例 .method({ name: "getAxios" }) // 聲明四個(gè)請(qǐng)求方式對(duì)應(yīng)的方法 const methods = ["get", "post", "put", "delete"] methods.forEach(method => { IApi.method({ name: method, args: null, implement: function() { // 處理了 this 指向問題,放心用吧 return this.getAxios()[method].apply(this, arguments) .then(responseHandler) .catch(errorHandler) } }) })繼承接口
假定我們要?jiǎng)?chuàng)建接口 A,要繼承 B、C、D、E 等接口,使用如下語(yǔ)句:
const A = new Interface({ name: "A", debug: true }).extends([B, C, D, E])
extends 方法會(huì)將傳入的接口所持有的所有方法聲明(即通過 Interface.method(config) 所聲明的那些方法 )拷貝至接口 A,包括那些方法聲明的默認(rèn)實(shí)現(xiàn)。
注意!
一般來說,不會(huì)在多個(gè)接口中重載同一個(gè)方法簽名,但如果真的有這樣的需求,可以自行設(shè)置 id 的規(guī)則,比如:
const B = new Interface(...) .method({ id: "B00", name: "getName", args = [...] }) .method({ id: "B01", name: "getName", args = [...] }) const C = new Interface(...) .method({ id: "C00", name: "getName", args = [...] })
然后實(shí)現(xiàn)該方法時(shí)指定要實(shí)現(xiàn)哪一個(gè)聲明:
// 注意!如果一個(gè)方法被重載,則不能在 class 中聲明該方法。 class AImpl { ... } const AInstance = new AImpl(...) B.implement({ object: AInstance, id: "B00", // 指定要實(shí)現(xiàn)的方法聲明 name: "getName" })
再次回到我們的 demo,綜合運(yùn)用一下:
const IAuthenticationApi = new Interface({ name: "IAuthentication", debug: true }) // 指明 IAuthenticationApi 繼承自 IApi 接口 .extends(IApi) IAuthenticationApi // 重載方法 login // loin (username :string, password :string) .method({ id: 0, name: "login", args: [ {name: "username", type: "string", support: val => typeof val === "string"}, {name: "password", type: "string", support: val => typeof val === "string"} ] }) // login() .method({ id: 1, name: "login" })實(shí)現(xiàn)接口
// 編寫一個(gè)實(shí)現(xiàn)類 class AuthenticationApi { constructor(axios) { this.axios = axios } // 直接實(shí)現(xiàn) getName 方法 getName() { return "AuthenticationApi" } // 直接實(shí)現(xiàn) getAxios 方法 getAxios() { return this.axios } } // 實(shí)現(xiàn)重載方法 IAuthenticationApi .implement({ // 指定掛載實(shí)現(xiàn)到 AuthenticationApi 上 object: AuthenticationApi, // 指定此實(shí)現(xiàn)是對(duì)應(yīng) id 為 0 的方法聲明 id: 0, name: "login", implement: function(username, password) { console.log("帶參數(shù)的 login") // 還記得我們?cè)?IApi 接口中定義了 get 方法(包括默認(rèn)實(shí)現(xiàn))嗎? this.get("https://www.baidu.com") return Promise.resolve("hello") } }) .implement({ object: AuthenticationApi, id: 1, name: "login", implement: function () { console.log("無參數(shù)的 login") }, }) IAuthenticationApi.ensureImplements(AuthenticationApi)使用接口實(shí)現(xiàn)類
let authenticationService = new AuthenticationApi(axios) // 掛載代理函數(shù)到實(shí)例上,否則會(huì)提示 // Uncaught TypeError: authenticationService.login is not a function IAuthenticationApi.ensureImplements(authenticationService) authenticationService .login("sitdownload", "1498696873") // login(string, string) 會(huì)返回一個(gè) Promise 還記得嗎 :P .then(str => alert(`${str} world!`)) authenticationService.login()關(guān)于日志
首先確保在創(chuàng)建接口時(shí)打開了 debug 開關(guān)({ debug: true })。
上面的 demo 運(yùn)行正常的話你將會(huì)得到下面的日志:
// 注冊(cè)方法 Interface 注冊(cè)方法: IApi.getName() Interface 注冊(cè)方法: IApi.getAxios() Interface 注冊(cè)方法: IApi.get(any) Interface 注冊(cè)方法: IApi.post(any) Interface 注冊(cè)方法: IApi.put(any) Interface 注冊(cè)方法: IApi.delete(any) Interface 注冊(cè)方法: IAuthentication extends IApi.getName() Interface 注冊(cè)方法: IAuthentication extends IApi.getAxios() Interface 注冊(cè)方法: IAuthentication extends IApi.get(any) Interface 注冊(cè)方法: IAuthentication extends IApi.post(any) Interface 注冊(cè)方法: IAuthentication extends IApi.put(any) Interface 注冊(cè)方法: IAuthentication extends IApi.delete(any) Interface 注冊(cè)方法: [0]IAuthentication.login(username :string, password :string) Interface 注冊(cè)方法: [1]IAuthentication.login() // 實(shí)現(xiàn)方法 Interface 實(shí)現(xiàn)方法: 保存 [0]IAuthentication.login(...) 實(shí)現(xiàn): ? implement(username, password) Interface 實(shí)現(xiàn)方法: 保存 [1]IAuthentication.login(...) 實(shí)現(xiàn): ? implement() // 匹配方法 Interface 方法匹配: 精準(zhǔn)匹配 IAuthentication.login({ username: "sitdownload" } :string, { password: "1498696873" } :string). // 在控制臺(tái)這行是可以打開實(shí)現(xiàn)的具體位置的 ? implement(username, password) // 方法輸出 AuthenticationApi.js?7b55:25 帶參數(shù)的 login // 匹配方法 Interface 方法匹配: 無法精準(zhǔn)匹配 IAuthentication.get("https://www.baidu.com"),使用 any 實(shí)現(xiàn)匹配: ? implement() Interface 方法匹配: 精準(zhǔn)匹配 IAuthentication.login(). ? implement() // 方法輸出 AuthenticationApi.js?7b55:35 無參數(shù)的 login // AuthenticationApi.login(username, password) 中請(qǐng)求了 "https://www.baidu.com" Failed to load https://www.baidu.com/: No "Access-Control-Allow-Origin" header is present on the requested resource. Origin "http://127.0.0.1" is therefore not allowed access. // IApi.get(any) 中將異常直接向下拋了 Uncaught (in promise) {type: "network", payload: Error: Network Error at createError (webpack-internal:///./node_modules/_axios@0.18.0@axios/lib/…}后續(xù)
如果要發(fā)版了,確認(rèn)所有的接口方法都正確實(shí)現(xiàn)后,就可以把 debug 關(guān)掉,這樣就不會(huì)有 Interface 內(nèi)部的一些入?yún)z查和調(diào)試輸出。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/95204.html
摘要:它就是一套兼容方案,目前兼容的有以及原生支持。返回值問題在第一次使用時(shí),。具體是什么意義呢的返回值,其實(shí)就是插件提供的對(duì)外接口,而實(shí)際上,就是一個(gè)對(duì)象。而在環(huán)境下,只需要將這個(gè)返回值賦予即可完成該模塊的接口。 有更新,請(qǐng)到github上看源碼 什么是OMD 在node.js流行起來之前,javascript的開發(fā)方式都是函數(shù)式的順序依賴關(guān)系,直到node火起來。CommonJS其實(shí)首先...
摘要:前面講泛型的時(shí)候,提到了接口。和泛型一樣,接口也是目前中并不存在的語(yǔ)法。不過可不吃這一套,所以這里通過注釋關(guān)閉了對(duì)該接口的命名檢查。這樣的接口不能由類實(shí)現(xiàn)。 前面講 泛型 的時(shí)候,提到了接口。和泛型一樣,接口也是目前 JavaScript 中并不存在的語(yǔ)法。 由于泛型語(yǔ)法總是附加在類或函數(shù)語(yǔ)法中,所以從 TypeScript 轉(zhuǎn)譯成 JavaScript 之后,至少還存在類和函數(shù)(只是...
摘要:以這個(gè)工具庫(kù)作為依賴,只需要編寫實(shí)際通訊接口目前混合開發(fā)的方案包括就是一種簡(jiǎn)單的解決方案?;诮涌诹6鹊陌踩刂撇呗悦恳粋€(gè)實(shí)例也都會(huì)持有一個(gè)的實(shí)例,用于針對(duì)具體的開發(fā)接口設(shè)置安全檢查規(guī)則。 EasyBridge是一個(gè)簡(jiǎn)單易用的js-bridge的工具庫(kù),提供了日常開發(fā)中,JavaScript與Java之間通訊的能力,與其他常見的js-bridge工具庫(kù)實(shí)現(xiàn)方案不同,EasyBridge...
摘要:前言本章我們要講解的是五大原則語(yǔ)言實(shí)現(xiàn)的第篇,接口隔離原則。接口隔離原則和單一職責(zé)有點(diǎn)類似,都是用于聚集功能職責(zé)的,實(shí)際上可以被理解才具有單一職責(zé)的程序轉(zhuǎn)化到一個(gè)具有公共接口的對(duì)象。與我們下面討論的一些小節(jié)是里關(guān)于違反接口隔離原則的影響。 前言 本章我們要講解的是S.O.L.I.D五大原則JavaScript語(yǔ)言實(shí)現(xiàn)的第4篇,接口隔離原則ISP(The Interface Segreg...
摘要:例如允許的對(duì)象默認(rèn)情況下,通過使用內(nèi)置宏將核心對(duì)象和方法映射到。例如這被轉(zhuǎn)換為以下代碼類可以定義構(gòu)造函數(shù),具有超類,并且可以像在中一樣實(shí)例化。因此,它不違反原則。用于聲明該對(duì)象可以用作構(gòu)造函數(shù)。 這個(gè)工具可以將java代碼轉(zhuǎn)為js代碼,從而可以使用java編寫前端代碼 如果排版看著費(fèi)勁可以下載下方html,打開html后使用google翻譯 JSweet語(yǔ)言規(guī)范版本:2.x(快照) 作...
摘要:例如允許的對(duì)象默認(rèn)情況下,通過使用內(nèi)置宏將核心對(duì)象和方法映射到。例如這被轉(zhuǎn)換為以下代碼類可以定義構(gòu)造函數(shù),具有超類,并且可以像在中一樣實(shí)例化。因此,它不違反原則。用于聲明該對(duì)象可以用作構(gòu)造函數(shù)。 這個(gè)工具可以將java代碼轉(zhuǎn)為js代碼,從而可以使用java編寫前端代碼 如果排版看著費(fèi)勁可以下載下方html,打開html后使用google翻譯 JSweet語(yǔ)言規(guī)范版本:2.x(快照) 作...
閱讀 3389·2021-11-23 09:51
閱讀 1079·2021-09-26 09:55
閱讀 4101·2021-09-22 14:58
閱讀 1767·2021-09-08 09:35
閱讀 1156·2021-08-26 14:16
閱讀 950·2019-08-23 18:17
閱讀 2179·2019-08-23 16:45
閱讀 760·2019-08-23 15:55