最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

JS怎样实现运算符重载

来源:动视网 责编:小采 时间:2020-11-27 19:51:04
文档

JS怎样实现运算符重载

JS怎样实现运算符重载:这次给大家带来JS怎样实现运算符重载,JS实现运算符重载的注意事项有哪些,下面就是实战案例,一起来看一下。最近要做数据处理,自定义了一些数据结构,比如Mat,Vector,Point之类的,对于加减乘除之类的四则运算还要重复定义,代码显得不是很直观,javas
推荐度:
导读JS怎样实现运算符重载:这次给大家带来JS怎样实现运算符重载,JS实现运算符重载的注意事项有哪些,下面就是实战案例,一起来看一下。最近要做数据处理,自定义了一些数据结构,比如Mat,Vector,Point之类的,对于加减乘除之类的四则运算还要重复定义,代码显得不是很直观,javas


这次给大家带来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)()
 }

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

vue-cli怎样做出跨域请求

webpack移动端自动化构建rem步骤详解

nodejs通过响应回写渲染页面步骤详解

文档

JS怎样实现运算符重载

JS怎样实现运算符重载:这次给大家带来JS怎样实现运算符重载,JS实现运算符重载的注意事项有哪些,下面就是实战案例,一起来看一下。最近要做数据处理,自定义了一些数据结构,比如Mat,Vector,Point之类的,对于加减乘除之类的四则运算还要重复定义,代码显得不是很直观,javas
推荐度:
标签: 如何 实现 js
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top