中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Javascript實現運算符重載的方法

發布時間:2021-04-12 12:38:40 來源:億速云 閱讀:215 作者:小新 欄目:web開發

這篇文章將為大家詳細講解有關Javascript實現運算符重載的方法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

最近要做數據處理,自定義了一些數據結構,比如Mat,Vector,Point之類的,對于加減乘除之類的四則運算還要重復定義,代碼顯得不是很直觀,javascript沒有運算符重載這個像C++、C#之類的功能的確令人不爽,于是想“曲線救國”,自動將翻譯代碼實現運算符重載,實現思路其實很簡單,就是編寫一個解釋器,將代碼編譯。例如:

S = A + B (B - C.fun())/2 + D

翻譯成

`S = replace(replace(A, '+', replace(replace(B,'',(replace(B,'-',C.fun())))),'/',2),'+',D)`

在replace函數中我們調用對象相應的運算符函數,replace函數代碼如下:

/**
 * 轉換方法
 * @param a
 * @param op
 * @param b
 * @returns {*}
 * @private
 */
export function __replace__(a,op,b){
  if(typeof(a) != 'object' && typeof(b) != 'object'){
    return new Function('a','b','return a' + op + 'b')(a,b)
  }
  if(!Object.getPrototypeOf(a).isPrototypeOf(b)
    && Object.getPrototypeOf(b).isPrototypeOf(a)){
    throw '不同類型的對象不能使用四則運算'
  }
  let target = null
  if (Object.getPrototypeOf(a).isPrototypeOf(b)) {
    target = new Function('return ' + b.__proto__.constructor.name)()
  }
  if (Object.getPrototypeOf(b).isPrototypeOf(a)) {
    target = new Function('return ' + a.__proto__.constructor.name)()
  }
  if (op == '+') {
    if (target.__add__ != undefined) {
      return target.__add__(a, b)
    }else {
      throw target.toString() +'\n未定義__add__方法'
    }
  }else if(op == '-') {
    if (target.__plus__ != undefined) {
      return target.__plus__(a, b)
    }else {
      throw target.toString() + '\n未定義__plus__方法'
    }
  }else if(op == '*') {
    if (target.__multiply__ != undefined) {
      return target.__multiply__(a, b)
    }else {
      throw target.toString() + '\n未定義__multiply__方法'
    }
  } else if (op == '/') {
    if (target.__divide__ != undefined) {
      return target.__divide__(a, b)
    }else {
      throw target.toString() + '\n未定義__divide__方法'
    }
  } else if (op == '%') {
    if (target.__mod__ != undefined) {
      return target.__mod__(a, b)
    }else {
      throw target.toString() + '\n未定義__mod__方法'
    }
  } else if(op == '.*') {
    if (target.__dot_multiply__ != undefined) {
      return target.__dot_multiply__(a, b)
    }else {
      throw target.toString() + '\n未定義__dot_multiply__方法'
    }
  } else if(op == './') {
    if (target.__dot_divide__ != undefined) {
      return target.__dot_divide__(a, b)
    }else {
      throw target.toString() + '\n未定義__dot_divide__方法'
    }
  } else if(op == '**') {
    if (target.__power__ != undefined) {
      return target.__power__(a, b)
    }else {
      throw target.toString() + '\n未定義__power__方法'
    }
  }else {
    throw op + '運算符無法識別'
  }
}

replace實現非常簡單,不做過多解釋,重要的部分是如何實現代碼的編譯。大學學習數據結構時四則運算的實現就是這翻譯的基礎,略微有些差異。簡單描述一下流程:

1、分割表達式,提取變量和運算符獲得元數組A
2、遍歷元數組

如果元素是運算符加減乘除,則從堆棧中彈出上一個元素,轉換為replace(last,操作符,
如果元素是‘)',則從堆棧中彈出元素,拼接直到遇到'(',并壓入堆棧。這里需要注意‘('元素前是否為函數調用或replace,如果是函數調用或replace,則需要繼續向前彈出數據,閉合replace函數的閉合。
如果是一般元素,則查看前一個元素是否replace,如果是,則需要拼接‘)'使得replace函數閉合,否則直接將元素壓入棧。

3、將2步驟中得到的棧順序組合就得到編譯后的表達式。

依據上述流程,實現代碼:

/**
 * 表達式轉換工具方法
 * @param code
 */
export function translate (code) {
  let data = []
  let tmp_code = code.replace(/\s/g,'')
  let tmp = []
  let vari = tmp_code.split(/["]+[^"]*["]+|[']+[^']*[']+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|\}|=|%|\.\/|\.\*|,/g)
  let ops = tmp_code.match(/["]+[^"]*["]+|[']+[^']*[']+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|\}|=|%|\.\/|\.\*|,/g)
  for (let i = 0,len = ops.length; i < len; i++) {
    if (vari[i] != '') {
      tmp.push(vari[i])
    }
    if (ops[i] != '') {
      tmp.push(ops[i])
    }
  }
  tmp.push(vari[ops.length])
  for (let i = 0; i < tmp.length; i++){
    let item = tmp[i]
    if(/\*\*|\+|-|\*|\/|%|\.\/|\.\*/.test(tmp[i])) {
      let top = data.pop()
      let trans = '__replace__(' + top + ',\'' + tmp[i] + '\','
      data.push(trans)
    }else{
      if (')' == tmp[i]) {
        let trans0 = tmp[i]
        let top0 = data.pop()
        while (top0 != '(') {
          trans0 = top0 + trans0
          top0 = data.pop()
        }
        trans0 = top0 + trans0
        let pre = data[data.length - 1]
        while(/[_\w]+[\.]?[_\w]+/.test(pre)
        && !/^__replace__\(/.test(pre)
        && pre != undefined) {
          pre = data.pop()
          trans0 = pre + trans0
          pre = data[data.length - 1]
        }
        pre = data[data.length - 1]
        while(pre != undefined
        && /^__replace__\(/.test(pre)){
          pre = data.pop()
          trans0 = pre + trans0 + ')'
          pre = data[data.length - 1]
        }
        data.push(trans0)
      }else {
        let pre = data[data.length - 1]
        let trans1 = tmp[i]
        while(pre != undefined
        && /^__replace__\(/.test(pre)
        && !/\*\*|\+|-|\*|\/|\(|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|=|\}|%|\.\/|\.\*/.test(item)
        && !/^__replace__\(/.test(item)) {
          if(tmp[i + 1] == undefined){
            pre = data.pop()
            trans1 = pre + trans1 + ')'
            break;
          }else{
            pre = data.pop()
            trans1 = pre + trans1 + ')'
            pre = data[data.length - 1]
          }

        }
        data.push(trans1)

      }
    }
  }
  let result = ''
  data.forEach((value, key, own) => {
    result += value
  })
  return result
}

表達式編譯的方法寫好了,接下來就是如何使編寫的代碼被我們的翻譯機翻譯,也就是需要一個容器,兩種方法:一種就是類構造器重新定義方法屬性,另一種就是將代碼作為參數傳入我們自定義的方法。接下來介紹一下類構造器中重新定義方法:

export default class OOkay {
  constructor () {
    let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(this))
    protos.forEach((proto, key, own) => {
      if(proto != 'constructor'){
        Object.defineProperty(this, proto, {
          value:new Function(translate_block(proto, this[proto].toString())).call(this)
        })
      }
    })
  }
}

由上面可以看出,我們使用Object.defineProperty在構造器中重新定義了,translate_block是對整個代碼塊分割得到進行翻譯,代碼如下:

/**
 * 類代碼塊轉換工具
 * @param name
 * @param block
 * @returns {string}
 */
export function translate_block (name , block) {
  let codes = block.split('\n')
  let reg = new RegExp('^' + name + '$')
  console.log(reg.source)
  codes[0] = codes[0].replace(name,'function')
  for(let i = 1; i < codes.length; i++) {
    if (codes[i].indexOf('//') != -1) {
      codes[i] = codes[i].substring(0,codes[i].indexOf('//'))
    }
    if(/\*\*|\+|-|\*|\/|%|\.\/|\.\*/g.test(codes[i])){
      if (codes[i].indexOf('return ') != -1) {
        let ret_index = codes[i].indexOf('return ') + 7
        codes[i] = codes[i].substring(0,ret_index) + translate(codes[i].substring(ret_index))
      }else {
        let eq_index = codes[i].indexOf('=') + 1
        codes[i] = codes[i].substring(0,eq_index) + translate(codes[i].substring(eq_index))
      }
    }
  }
  return 'return ' + codes.join('\n')
}

對于新的類,我們只要繼承OOkay類就可以在該類中使用運算符重載。對于繼承自非OOkay類的,我們可以采用注入的方式,如下:

/**
   * 非繼承類的注入方法
   * @param target
   */
  static inject (target) {
    let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(target))
    protos.forEach((proto, key, own) => {
      if (proto != 'constructor') {
        Object.defineProperty(target, proto, {
          value:new Function(translate_block(proto, target[proto].toString())).call(target)
        })
      }
    })
  }

對于非類中的代碼,我們需要一個容器,這里我采用了兩種方式,一種以ookay腳本的方式使用,像這樣
<script type='text/ookayscript'>
let a = a+b // a、b為對象實例
</script>
還有就是將代碼作為參數傳入__$$__方法,該方法編譯代碼并執行,如下:

static __$__(fn) {
    if(!(fn instanceof Function)){
      throw '參數錯誤'
    }
    (new Function(translate_block('function',fn.toString()))).call(window)()
  }

關于“Javascript實現運算符重載的方法”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

丹凤县| 连云港市| 盐城市| 河西区| 乌拉特前旗| 六盘水市| 安乡县| 嘉鱼县| 建湖县| 金坛市| 健康| 图木舒克市| 桐城市| 青铜峡市| 阿坝县| 祁门县| 嘉义市| 郎溪县| 三河市| 镇远县| 无极县| 宁阳县| 巴里| 湖北省| 绥滨县| 临漳县| 湖南省| 电白县| 龙胜| 探索| 唐海县| 华池县| 洛川县| 广宁县| 洞头县| 玉树县| 沐川县| 周口市| 乌鲁木齐县| 大英县| 富裕县|