首頁  >  文章  >  web前端  >  JS怎麼實作運算子重載

JS怎麼實作運算子重載

php中世界最好的语言
php中世界最好的语言原創
2018-05-02 10:31:491814瀏覽

這次帶給大家JS怎麼實作運算子重載,JS實作運算子重載的注意事項有哪些,下面就是實戰案例,一起來看一下。

最近要做資料處理,自訂了一些資料結構,像是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.pide != undefined) {
      return target.pide(a, b)
    }else {
      throw target.toString() + '\n未定义pide方法'
    }
  } 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_pide != undefined) {
      return target.dot_pide(a, b)
    }else {
      throw target.toString() + '\n未定义dot_pide方法'
    }
  } 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函數閉合,否則直接將元素壓入堆疊。 #依據上述流程,實作程式碼:

/**
 * 表达式转换工具方法
 * @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(/["]+[^"]*["]+|[&#39;]+[^&#39;]*[&#39;]+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|\}|=|%|\.\/|\.\*|,/g)
  for (let i = 0,len = ops.length; i < len; i++) {
    if (vari[i] != &#39;&#39;) {
      tmp.push(vari[i])
    }
    if (ops[i] != &#39;&#39;) {
      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 = &#39;replace(&#39; + top + &#39;,\&#39;&#39; + tmp[i] + &#39;\&#39;,&#39;
      data.push(trans)
    }else{
      if (&#39;)&#39; == tmp[i]) {
        let trans0 = tmp[i]
        let top0 = data.pop()
        while (top0 != &#39;(&#39;) {
          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 + &#39;)&#39;
          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 + &#39;)&#39;
            break;
          }else{
            pre = data.pop()
            trans1 = pre + trans1 + &#39;)&#39;
            pre = data[data.length - 1]
          }
        }
        data.push(trans1)
      }
    }
  }
  let result = &#39;&#39;
  data.forEach((value, key, own) => {
    result += value
  })
  return result
}

表達式編譯的方法寫好了,接下來就是如何使編寫的程式碼被我們的翻譯機翻譯,也就是需要一個容器,兩種方法:一種就是類別建構器重新定義方法屬性,另一種就是將程式碼作為參數傳入我們自訂的方法。 ,我們使用Object.defineProperty在建構器中重新定義了,translate_block是對整個程式碼區塊分割得到進行翻譯,程式碼如下:

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

對於新的類,我們只要繼承OOkay類別就可以在該類中

使用運算子

重載。我採用了兩種方式,一種以ookay腳本的方式使用,像這樣

還有就是將程式碼作為參數傳入$$方法,該方法編譯程式碼並執行,如下:

/**
 * 类代码块转换工具
 * @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')
}
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章! webpack行動端自動化建置rem步驟詳解




nodejs透過回應回寫渲染頁面步驟詳解

以上是JS怎麼實作運算子重載的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn