您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

JS怎样实现运算符重载

2025/5/5 10:33:07发布21次查看
这次给大家带来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函数闭合,否则直接将元素压入栈。
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)()   }
相信看了本文案例你已经掌握了方法,更多精彩请关注其它相关文章!
推荐阅读:
vue-cli怎样做出跨域请求
webpack移动端自动化构建rem步骤详解
nodejs通过响应回写渲染页面步骤详解
以上就是js怎样实现运算符重载的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product