zoukankan      html  css  js  c++  java
  • javascript 元编程之 method_missing

    javascript 元编程之 method_missing

    引言

    要说元编程 ruby 中技巧太多了,今天来写的这个技术也来自于 ruby 中的灵感。

    method_missing 这个在 ruby 中对象调用方法如果没有调到就会去调用这个方法。

    这个功能在 javascript 中怎么实现,现在 proxy 出现了,这个就有思路了。

    实现

    class Base {
      constructor () {
        return new Proxy(this, {
          get (target, property) {
            if (Reflect.has(target, property)) {
              return Reflect.get(target, property)
            } else {
              return function () {
                if (target.method_missing) {
                  return target.method_missing(property, ...arguments)
                } else {
                  throw new ReferenceError('Property "' + property + '" does not exist.')
                }
              }
            }
          }
        })
      }
    }
    

    代码解释一下, 用了 class, Proxy 的功能。

    怎样使用呢?

    我说一下应用场景,在前端单页面中,有很多的 ajax 接口,如果你想使用一般情况下会怎么做呢?

    会先把网络库(jquery, axios) 封一层变成 http ,然后再把请求分一层,变成 request,最后对每个接口封一层,把调用方法变成一个一个的方法,放到一个文件夹中管理 api,然后在别的地方引用。

    这样当然能工作。问题在于接口信息太冗余,接口的信息放在三个地方,url 中,参数中,方法名,如果有一个要改,可能三个地方都要改。

    而且是要对每个函数都要做一遍这个工作。

    下面举例:

    
    // http 方法
    
    import $ from 'jquery'
    
    window.jQuery = $
    
    $.ajaxSetup({
      cache: false,
      dataType: 'json'
    })
    
    // request 方法
    export function request (url, setting) {
      return new Promise((resolve, reject) => {
        $.ajax(url, setting)
          .then(resolve)
          .fail(reject)
      })
    }
    
    export function get (url, params) {
      return request(url, {
        method: 'GET',
        data: params
      })
    }
    
    export function post (url, params) {
      return request(url, {
        method: 'POST',
        data: params
      })
    }
    
    // 具体的方法
    import { post } from '@/lib/http'
    
    export function getPermission (params = {}) {
      return post('/passport/permission', params)
    }
    
    
    

    我们可以看到具体的方法中,函数名和 url 其实都在表达一个意思,而且如果再来接口我还要这么干。

    如果20多个接口,都是这种重复代码,不应该这么干。

    实现

    怎么做?

    上面写的 method_missing 方法上场了,我们不封方法了,直接写函数,然后委托 method_missing 方法进行统一的调用,让我们从重复的封方法中解脱出来,干点别的什么事。

    import Base from './base'
    import { request } from './http'
    import * as _ from 'lodash'
    
    let METHOD = {
      GET: ['get', 'show'],
      POST: ['post', 'update', 'change', 'create', 'delete', 'remove']
    }
    
    class ApiService extends Base {
      constructor () {
        super()
        this.baseURL = '/api/web'
      }
    
      method_missing (property, ...args) {
        let [url, setting] = args
    
        if (_.isObject(url)) {
          setting = {
            data: url
          }
    
          // 这里通过方法名生成 url
          const url_parts = property.split('_')
          const method = url_parts[0].toLowerCase()
    
          if (METHOD.GET.includes(method)) {
            setting.method = 'GET'
            url_parts.shift()
          } else if (METHOD.POST.includes(method)) {
            setting.method = 'POST'
            url_parts.shift()
          } else {
            setting.method = 'GET'
          }
    
          url = '/' + url_parts.join('/')
        }
    
        return request(this.baseURL + url, setting)
      }
    }
    
    export default new ApiService()
    
    
    // 执行代码
    import apiService from './api-service'
    
    // 调用,神奇的代码
    apiService.get_passport_permission({
    })
    
    

    从执行代码来看,我并没有定义这个方法,这个方法会落到 apiserver 中的 method_missing 中去。

    拿到方法名和参数列表,我就能干事情,跟据方法名去生成 url,然后参数加上,就能发请求了。

    结论

    这个方法在代码上如果你接口特别多的话,可以节省太多的样板代码,而且是越是通用的代码,越好,如果有特例的代码,可以加到实现 method_missing 的类中。

    这样通用的代码和特例的代码都能兼顾,当然兼容性还是要考虑的。

  • 相关阅读:
    力扣(LeetCode)67. 二进制求和
    力扣(LeetCode) 66. 加一
    力扣(LeetCode)58. 最后一个单词的长度
    力扣(LeetCode)1009. 十进制整数的反码
    力扣(LeetCode) 20. 有效的括号
    力扣(LeetCode)1016. 子串能表示从 1 到 N 数字的二进制串
    数据库索引
    OSI 七层和五层
    ST算法
    F
  • 原文地址:https://www.cnblogs.com/htoooth/p/11367829.html
Copyright © 2011-2022 走看看