>  기사  >  웹 프론트엔드  >  JS에서 인터페이스를 사용하는 단계에 대한 자세한 설명

JS에서 인터페이스를 사용하는 단계에 대한 자세한 설명

php中世界最好的语言
php中世界最好的语言원래의
2018-05-24 13:38:107933검색

이번에는 JS에서 인터페이스를 사용하는 단계에 대해 자세히 설명하겠습니다. JS에서 인터페이스를 사용할 때 주의 사항은 무엇입니까?

이 글은 js-interface의 README입니다. 비록 아주 복잡한 내용은 아니지만, 누구나 읽을 수 있다면 클래스 관리 방법에 대해 적어보겠습니다.

JavaScript

Design Patterns

", 2장에서는 다양한 디자인 패턴을 쉽게 사용할 수 있도록 JavaScript에서 인터페이스(interface)를 시뮬레이션하는 개념을 언급했으므로 인터페이스를 시뮬레이션해 보세요. 저는 백엔드 Java 개발자이기 때문에 이 시뮬레이션 레이어에 인터페이스 기본 구현, 인터페이스 상속, 메서드 오버로딩 및 기타 기능을 추가하고 싶습니다. 그러나 이러한 기능은 추가한 후에는 불가피합니다. 성능 면에서는 약간의 희생이 따르겠지만 저에게는 개발 경험을 어느 정도 향상시킬 수 있습니다(TypeScript를 알고 있습니다. 그냥 직접 사용해 보고 싶습니다 :P). Use원래 의도는 Api 관리를 용이하게 하는 것이므로 Api에 대한 데모를 만들어 보겠습니다.

인터페이스 만들기

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 })

IApi 接口含有一个 getName 方法,它没有任何参数,也没有默认实现,这就要求在后面任何 IApi 的子接口或实现类必须实现该方法,否则会抛出一个异常。


如果想指定方法的参数列表:

// 声明一个空参方法
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'}
] })

注意!

argsnull 时表示该方法可以接受任意数量的任意参数,如果重载了一个方法(详细的请参阅后面关于重载的说明):

IApi.method({ 
    name: 'getName', 
    // 默认实现,不能为箭头函数!
    implement: function() {
        return "IApi"   
    }
})

注意!

在参数描述中,type 属性只是一个字符串值,它并不真的代表参数的实际类型。它跟 name 属性一样只是提供用于调试的信息,因此你可以填入任何你认为合适的、足以标记该参数一些信息的字符串值。

真正决定方法是否接受该参数的是 support 属性,当它返回 true 时会检查下一个参数直到所有参数检查完毕或某个位置的参数不被接受。

如果需要,可以在 support 中对实际入参进行特殊处理,比如转换对象、特定属性检查等等。


如果想为方法提供默认实现:

// 声明两个方法,它们都没有参数,也不需要重载因此这样就可以了
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)
    }
  })
})

回到我们的 demo,综合运用一下:

const A = new Interface({
    name: 'A',
    debug: true
}).extends([B, C, D, E])

继承接口

假定我们要创建接口 A,要继承 B、C、D、E 等接口,使用如下语句:

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 = [...] })

extends 方法会将传入的接口所持有的所有方法声明(即通过 Interface.method(config) 所声明的那些方法 )拷贝至接口 A,包括那些方法声明的默认实现。

注意!

一般来说,不会在多个接口中重载同一个方法签名,但如果真的有这样的需求,可以自行设置 id 的规则,比如:

// 注意!如果一个方法被重载,则不能在 class 中声明该方法。
class AImpl { ... } 
const AInstance = new AImpl(...)
B.implement({
    object: AInstance,
    id: 'B00', // 指定要实现的方法声明
    name: 'getName'
})

然后实现该方法时指定要实现哪一个声明:

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'
  })

再次回到我们的 demo,综合运用一下:

// 编写一个实现类
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()

使用接口实现类

// 注册方法
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 开关({ debug: true })。

上面的 demo 运行正常的话你将会得到下面的日志:

rrreee

后续

如果要发版了,确认所有的接口方法都正确实现后,就可以把 debug 关掉,这样就不会有 Interface주의!

🎜🎜 argsnull인 경우 메서드가 오버로드된 경우 메서드가 원하는 수의 매개변수를 허용할 수 있음을 의미합니다(자세한 내용은 오버로딩 설명을 참조하세요). 나중에) :🎜rrreee🎜🎜주의! 🎜🎜🎜매개변수 설명에서 type 속성 🎜은 문자열입니다. 🎜 값은 실제로 매개변수의 실제 유형을 나타내지 않습니다. 🎜name 속성과 마찬가지로 디버깅을 위한 정보만 제공하므로 적절하다고 생각되는 문자열 값을 입력하고 매개변수에 일부 정보를 표시하는 데 충분합니다. 🎜🎜메서드가 이 매개변수를 허용하는지 여부를 실제로 결정하는 것은 support 속성입니다. true를 반환하면 모든 매개변수가 확인될 때까지 다음 매개변수가 확인되거나 특정 직위는 허용되지 않습니다. 🎜🎜필요한 경우 지원에서 개체 변환, 특정 속성 확인 등 실제 입력 매개변수에 대한 특수 처리를 수행할 수 있습니다. 🎜
🎜메소드에 대한 기본 구현을 제공하려는 경우: 🎜rrreee
🎜데모로 돌아가서 포괄적으로 사용하십시오. 🎜rrreee🎜인터페이스 상속🎜🎜인터페이스 A를 생성한다고 가정합니다. B, C, D, E 및 기타 인터페이스를 상속하려면 다음 문을 사용하세요. 🎜rrreee🎜extends 메서드는 수신 인터페이스가 보유한 모든 메서드를 선언합니다(즉, Interface.method를 통해). (config) code>)는 해당 메서드 선언의 기본 구현을 포함하여 인터페이스 A에 복사됩니다. 🎜🎜🎜주의! 🎜🎜🎜일반적으로 동일한 메서드 서명은 여러 인터페이스에서 재정의되지 않지만 실제로 그러한 필요가 있는 경우 다음과 같이 id에 대한 규칙을 직접 설정할 수 있습니다. 🎜rrreee🎜그런 다음 이 방법을 구현할 때 구현할 문을 지정하세요. 🎜rrreee
🎜다시 데모로 돌아가서 포괄적으로 사용하세요. 🎜rrreee🎜인터페이스 구현🎜rrreee🎜인터페이스를 사용하여 클래스 구현🎜rrreee🎜정보 log🎜🎜먼저 인터페이스를 생성할 때 디버그 스위치가 켜져 있는지 확인하세요({ debug: true }). 🎜🎜위 데모가 정상적으로 실행되면 다음과 같은 로그가 나옵니다. 🎜rrreee🎜Follow-up🎜🎜버전을 출시하려면 모든 인터페이스 메소드가 올바르게 구현되었는지 확인한 후 디버그를 꺼야 합니다. 인터페이스 내부에는 일부 입력 매개변수 확인 및 디버깅 출력이 없습니다. 🎜

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 도서:

실제 프론트엔드 면접 질문 분석

React와 TypeScript 및 Mobx를 결합하는 단계에 대한 자세한 설명

위 내용은 JS에서 인터페이스를 사용하는 단계에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.