亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

Javascript實現運算符重載詳解

2024-05-06 16:43:36
字體:
來源:轉載
供稿:網友

最近要做數據處理,自定義了一些數據結構,比如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/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
色婷婷久久一区二区| 亚洲老头同性xxxxx| 久久久久久久久久亚洲| 久久久爽爽爽美女图片| 国产精品久久久久免费a∨| 国产女人18毛片水18精品| 国产精品精品久久久久久| 亚洲影院污污.| 亚洲国产精品yw在线观看| 国内精品久久久久伊人av| 全球成人中文在线| 亚洲激情自拍图| 日韩欧美国产成人| 日韩视频精品在线| 欧美激情视频在线观看| 国产精品扒开腿做爽爽爽视频| 亚洲欧美国产视频| 青青草原成人在线视频| 亚洲色图第一页| 一本色道久久88综合亚洲精品ⅰ| 另类少妇人与禽zozz0性伦| 国产女精品视频网站免费| 久久精品成人欧美大片| 国产精品igao视频| 国外色69视频在线观看| 日韩动漫免费观看电视剧高清| 日韩欧美一区二区在线| 91成人天堂久久成人| 国产精品在线看| 久久成人在线视频| 国产ts人妖一区二区三区| 操91在线视频| 欧美特级www| 中文字幕日韩综合av| 91啪国产在线| 国产精品视频资源| 欧美午夜激情小视频| 国产日韩在线亚洲字幕中文| 91久久精品美女高潮| 隔壁老王国产在线精品| 亚洲高清色综合| 成人福利视频在线观看| 亚洲男人天天操| 欧洲精品在线视频| 国产精品444| 亚洲韩国日本中文字幕| 亚洲国产精品国自产拍av秋霞| 尤物tv国产一区| 狠狠色香婷婷久久亚洲精品| 欧美最猛黑人xxxx黑人猛叫黄| 日韩动漫免费观看电视剧高清| 亚洲精品乱码久久久久久金桔影视| 91a在线视频| 九九热视频这里只有精品| 欧美性视频网站| 成人字幕网zmw| 欧美小视频在线观看| 久久在精品线影院精品国产| 欧美日韩免费在线| 成人性生交大片免费看小说| 日本精品性网站在线观看| 992tv成人免费影院| 欧美极品第一页| 国产精品美女久久久久av超清| 97免费视频在线播放| 久久久噜噜噜久久| 精品久久久国产精品999| 91九色国产在线| 亚洲欧洲成视频免费观看| 亚洲欧美在线第一页| 日韩最新在线视频| 国产精品一二三视频| 国产一区二区丝袜高跟鞋图片| 国产91在线播放精品91| 91久久国产综合久久91精品网站| 久久免费少妇高潮久久精品99| 一本大道亚洲视频| 日韩精品视频在线播放| xxxxx成人.com| 在线观看视频亚洲| 久久久久五月天| 欧美福利小视频| 欧美成人sm免费视频| 欧美高清视频在线观看| 一级做a爰片久久毛片美女图片| 亚洲国产精久久久久久| 日韩精品中文字幕有码专区| 91久久久久久久久久久| 日本午夜在线亚洲.国产| 91精品国产综合久久香蕉922| 欧美一区二区大胆人体摄影专业网站| www.xxxx欧美| 国产精品偷伦免费视频观看的| 26uuu另类亚洲欧美日本老年| 日韩va亚洲va欧洲va国产| 中文字幕久精品免费视频| 欧美理论电影在线播放| 久久夜色精品国产| 国产精品久久久久一区二区| 久久影视电视剧免费网站| 欧美精品在线免费观看| 精品av在线播放| 亚洲黄页视频免费观看| 91手机视频在线观看| 久久久成人精品视频| 大胆欧美人体视频| 欧美激情中文网| 中文字幕欧美日韩精品| 操人视频在线观看欧美| 亚洲一区二区三区四区视频| 国产日韩精品入口| 久久伊人精品天天| 欧美日本高清一区| 欧美大片欧美激情性色a∨久久| 久久久精品视频成人| 亚洲a级在线播放观看| 久久精视频免费在线久久完整在线看| 亚洲欧美一区二区精品久久久| 久久精品国产免费观看| 精品调教chinesegay| 日韩中文字幕网站| 欧美成人性色生活仑片| 在线性视频日韩欧美| 日韩大胆人体377p| 欧美性受xxxx白人性爽| 欧美日韩在线视频首页| 久久久久久久久久国产精品| 在线色欧美三级视频| 亚洲一区二区日本| 亚洲人成电影网站色xx| 狠狠久久亚洲欧美专区| 欧美大全免费观看电视剧大泉洋| 国产91对白在线播放| 国产欧美精品在线播放| 亚洲女人天堂视频| 亚洲成人精品在线| 欧美亚洲视频在线观看| 欧美极品少妇全裸体| 精品视频中文字幕| 亚洲热线99精品视频| 欧美精品久久久久久久免费观看| 国内揄拍国内精品少妇国语| 国产成人亚洲综合青青| 精品久久久久久中文字幕一区奶水| 亚洲天堂av综合网| 精品亚洲国产成av人片传媒| 国产精品旅馆在线| 国内外成人免费激情在线视频| 欧美性在线观看| 色www亚洲国产张柏芝| 国产亚洲激情视频在线| 精品女厕一区二区三区| 国产一区二区三区在线观看视频| 久久天天躁夜夜躁狠狠躁2022| 日韩精品视频在线观看网址| 国产精品极品美女在线观看免费| 91中文在线视频| 亚洲欧美另类中文字幕| 91老司机精品视频| 国产精品一区二区久久久久| 55夜色66夜色国产精品视频| 欧美在线一级视频| 午夜欧美大片免费观看| 国产成人激情视频|