国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開(kāi)發(fā) > JS > 正文

Javascript實(shí)現(xiàn)運(yùn)算符重載詳解

2024-05-06 16:43:36
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

最近要做數(shù)據(jù)處理,自定義了一些數(shù)據(jù)結(jié)構(gòu),比如Mat,Vector,Point之類的,對(duì)于加減乘除之類的四則運(yùn)算還要重復(fù)定義,代碼顯得不是很直觀,javascript沒(méi)有運(yùn)算符重載這個(gè)像C++、C#之類的功能的確令人不爽,于是想“曲線救國(guó)”,自動(dòng)將翻譯代碼實(shí)現(xiàn)運(yùn)算符重載,實(shí)現(xiàn)思路其實(shí)很簡(jiǎn)單,就是編寫(xiě)一個(gè)解釋器,將代碼編譯。例如:

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

翻譯成

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

在replace函數(shù)中我們調(diào)用對(duì)象相應(yīng)的運(yùn)算符函數(shù),replace函數(shù)代碼如下:

/** * 轉(zhuǎn)換方法 * @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 '不同類型的對(duì)象不能使用四則運(yùn)算'  }  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 + '運(yùn)算符無(wú)法識(shí)別'  }}

replace實(shí)現(xiàn)非常簡(jiǎn)單,不做過(guò)多解釋,重要的部分是如何實(shí)現(xiàn)代碼的編譯。大學(xué)學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)時(shí)四則運(yùn)算的實(shí)現(xiàn)就是這翻譯的基礎(chǔ),略微有些差異。簡(jiǎn)單描述一下流程:

1、分割表達(dá)式,提取變量和運(yùn)算符獲得元數(shù)組A
2、遍歷元數(shù)組

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

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

依據(jù)上述流程,實(shí)現(xiàn)代碼:

/** * 表達(dá)式轉(zhuǎn)換工具方法 * @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}

表達(dá)式編譯的方法寫(xiě)好了,接下來(lái)就是如何使編寫(xiě)的代碼被我們的翻譯機(jī)翻譯,也就是需要一個(gè)容器,兩種方法:一種就是類構(gòu)造器重新定義方法屬性,另一種就是將代碼作為參數(shù)傳入我們自定義的方法。接下來(lái)介紹一下類構(gòu)造器中重新定義方法:

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在構(gòu)造器中重新定義了,translate_block是對(duì)整個(gè)代碼塊分割得到進(jìn)行翻譯,代碼如下:

/** * 類代碼塊轉(zhuǎn)換工具 * @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')}

對(duì)于新的類,我們只要繼承OOkay類就可以在該類中使用運(yùn)算符重載。對(duì)于繼承自非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)        })      }    })  }

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

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

這樣就實(shí)現(xiàn)了運(yùn)算符的重載


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JavaScript/Ajax教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 麦盖提县| 江华| 衡东县| 甘孜| 龙州县| 股票| 容城县| 遂平县| 绵阳市| 永川市| 牟定县| 曲靖市| 郸城县| 金平| 高淳县| 个旧市| 衢州市| 迁西县| 屯昌县| 简阳市| 康保县| 博野县| 阳城县| 准格尔旗| 虞城县| 黔东| 轮台县| 廊坊市| 宜川县| 岑巩县| 抚州市| 津南区| 上思县| 招远市| 平凉市| 萝北县| 东光县| 冷水江市| 西吉县| 高邮市| 神农架林区|