這次帶給大家JS中使用接口步驟詳解,JS中使用接口的注意事項有哪些,下面就是實戰案例,一起來看一下。
這篇是js-interface 的README,雖然不是很複雜的一個東西,如果有人看的話我就寫寫源碼思路了ORZ
在做一個前後分離的專案時,有些頭痛Api 之類的東西要怎麼管理,在閱讀《JavaScript 設計模式》一書時,第二章提到了在JavaScript 中模擬介面(interface) 的概念,以方便使用眾多設計模式,因此試著著做一個介面的模擬。由於我本職是一名後端Java 開發,因此希望在這個模擬層可以加入介面預設實作、介面繼承、#方法重載 等能力,雖然這些東西加上之後不可避免得會在效能上有所犧牲,但對我來說可以提升一些開發體驗(我知道TypeScript,只是想搞個輪子試試:P)。
既然初衷是為了方便管理 Api,那就做一個關於 Api 的 demo。
const config = { // 接口的名字 name: 'IApi', // 是否打开此接口的 debug 开关 // 开发时必须打开,否则不会启动 (方法声明、方法实现等)入参的类型检查。 // 打开这个的情况下,还会获得一些调试用的信息。 debug: true, } let IApi = new Interface(config)
最簡單的宣告方式:
IApi.method({name: 'getName'}) // 等价于 IApi.method({name: 'getName', args: undefined})
這樣就宣告了IApi
介面含有一個getName
方法,它沒有任何參數,也沒有預設實現,這就要求在後面任何IApi
的子介面或實作類別必須實作該方法,否則會拋出一個異常。
如果想指定方法的參數清單:
IApi.method({ name: 'getName', args: null })
注意!
args
為null
時表示該方法可以接受任意數量的任意參數,如果重載了一個方法(詳細的請參考後面關於重載的說明):
// 声明一个空参方法 IApi.method({ id: 0, name: 'getName', args: null }) // 重载上面的方法,使其有且只有一个 'string' 类型,名为 name 的参数 IApi.method({ id: 1, name: 'getName', args: [ {name: 'name', type: 'string', support: val => typeof val === 'string'} ] })
注意!
在參數描述中,type
屬性只是一個字串值,它並不真的代表參數的實際型別。 它跟 name
屬性一樣只是提供用於調試的信息,因此你可以填入任何你認為合適的、足以標記該參數一些信息的字符串值。
真正決定方法是否接受該參數的是support
屬性,當它傳回true
時會檢查下一個參數直到所有參數檢查完畢或某個位置的參數不被接受。
如果需要,可以在 support
中對實際入參進行特殊處理,例如轉換物件、特定屬性檢查等等。
如果想為方法提供預設實作:
IApi.method({ name: 'getName', // 默认实现,不能为箭头函数! implement: function() { return "IApi" } })
回到我們的demo,綜合運用一下:
// 声明两个方法,它们都没有参数,也不需要重载因此这样就可以了 IApi .method({ name: 'getName' }) // 项目中使用 Axios,因此这里需要一个方法来获取 axios 实例 .method({ name: 'getAxios' }) // 声明四个请求方式对应的方法 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) } }) })
假定我們要建立接口A,要繼承B、C、D、E 等接口,使用下列語句:
const A = new Interface({ name: 'A', debug: true }).extends([B, C, D, E])
extends
方法會將傳入的介面所持有的所有方法宣告(即透過Interface.method(config)
所宣告的那些方法)拷貝至介面A,包括那些方法宣告的預設實作。
注意!
#一般來說,不會在多個介面中重載同一個方法簽名,但如果真的有這樣的需求,可以自行設定id
的規則,例如:
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 = [...] })
然後實作該方法時指定要實作哪一個宣告:
// 注意!如果一个方法被重载,则不能在 class 中声明该方法。 class AImpl { ... } const AInstance = new AImpl(...) B.implement({ object: AInstance, id: 'B00', // 指定要实现的方法声明 name: 'getName' })
再回到我們的demo,綜合運用:
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' })
// 编写一个实现类 class AuthenticationApi { constructor(axios) { this.axios = axios } // 直接实现 getName 方法 getName() { return "AuthenticationApi" } // 直接实现 getAxios 方法 getAxios() { return this.axios } } // 实现重载方法 IAuthenticationApi .implement({ // 指定挂载实现到 AuthenticationApi 上 object: AuthenticationApi, // 指定此实现是对应 id 为 0 的方法声明 id: 0, name: 'login', implement: function(username, password) { console.log('带参数的 login') // 还记得我们在 IApi 接口中定义了 get 方法(包括默认实现)吗? this.get('https://www.baidu.com') return Promise.resolve('hello') } }) .implement({ object: AuthenticationApi, id: 1, name: 'login', implement: function () { console.log('无参数的 login') }, }) IAuthenticationApi.ensureImplements(AuthenticationApi)
let authenticationService = new AuthenticationApi(axios) // 挂载代理函数到实例上,否则会提示 // Uncaught TypeError: authenticationService.login is not a function IAuthenticationApi.ensureImplements(authenticationService) authenticationService .login('sitdownload', '1498696873') // login(string, string) 会返回一个 Promise 还记得吗 :P .then(str => alert(`${str} world!`)) authenticationService.login()
首先確保在建立介面時開啟了debug 開關({ debug: true }
)。
上面的demo 運作正常的話你將會得到下面的日誌:
// 注册方法 Interface 注册方法: IApi.getName() Interface 注册方法: IApi.getAxios() Interface 注册方法: IApi.get(any) Interface 注册方法: IApi.post(any) Interface 注册方法: IApi.put(any) Interface 注册方法: IApi.delete(any) Interface 注册方法: IAuthentication extends IApi.getName() Interface 注册方法: IAuthentication extends IApi.getAxios() Interface 注册方法: IAuthentication extends IApi.get(any) Interface 注册方法: IAuthentication extends IApi.post(any) Interface 注册方法: IAuthentication extends IApi.put(any) Interface 注册方法: IAuthentication extends IApi.delete(any) Interface 注册方法: [0]IAuthentication.login(username :string, password :string) Interface 注册方法: [1]IAuthentication.login() // 实现方法 Interface 实现方法: 保存 [0]IAuthentication.login(...) 实现: ƒ implement(username, password) Interface 实现方法: 保存 [1]IAuthentication.login(...) 实现: ƒ implement() // 匹配方法 Interface 方法匹配: 精准匹配 IAuthentication.login({ username: "sitdownload" } :string, { password: "1498696873" } :string). // 在控制台这行是可以打开实现的具体位置的 ƒ implement(username, password) // 方法输出 AuthenticationApi.js?7b55:25 带参数的 login // 匹配方法 Interface 方法匹配: 无法精准匹配 IAuthentication.get("https://www.baidu.com"),使用 any 实现匹配: ƒ implement() Interface 方法匹配: 精准匹配 IAuthentication.login(). ƒ implement() // 方法输出 AuthenticationApi.js?7b55:35 无参数的 login // AuthenticationApi.login(username, password) 中请求了 '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/…}
如果要發版了,確認所有的介面方法都正確實作後,就可以把debug 關掉,這樣就不會有Interface
內部的一些入參檢查和偵錯輸出。
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
推薦閱讀:
####################################################################### ###以上是JS中使用介面步驟詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!