JS怎样实现运算符重载

这次给大家带来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() +'未定义add方法'    }  }else if(op == '-') {    if (target.plus != undefined) {      return target.plus(a, b)    }else {      throw target.toString() + '未定义plus方法'    }  }else if(op == '*') {    if (target.multiply != undefined) {      return target.multiply(a, b)    }else {      throw target.toString() + '未定义multiply方法'    }  } else if (op == '/') {    if (target.pide != undefined) {      return target.pide(a, b)    }else {      throw target.toString() + '未定义pide方法'    }  } else if (op == '%') {    if (target.mod != undefined) {      return target.mod(a, b)    }else {      throw target.toString() + '未定义mod方法'    }  } else if(op == '.*') {    if (target.dot_multiply != undefined) {      return target.dot_multiply(a, b)    }else {      throw target.toString() + '未定义dot_multiply方法'    }  } else if(op == './') {    if (target.dot_pide != undefined) {      return target.dot_pide(a, b)    }else {      throw target.toString() + '未定义dot_pide方法'    }  } else if(op == '**') {    if (target.power != undefined) {      return target.power(a, b)    }else {      throw target.toString() + '未定义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)  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 [=]| {    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('')  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('')}

登录后复制

对于新的类,我们只要继承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脚本的方式使用,像这样

还有就是将代码作为参数传入$$方法,该方法编译代码并执行,如下:

static $(fn) {    if(!(fn instanceof Function)){      throw '参数错误'    }    (new Function(translate_block('function',fn.toString()))).call(window)()  }

登录后复制

相信看了本文案例你已经掌握了方法,更多精彩请关注【创想鸟】其它相关文章!

推荐阅读:

vue-cli怎样做出跨域请求

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

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

以上就是JS怎样实现运算符重载的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2765863.html

(0)
上一篇 2025年3月8日 11:04:06
下一篇 2025年3月7日 17:09:48

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • JS实现动态进度条步骤分析

    这次给大家带来JS实现动态进度条步骤分析,JS实现动态进度条的注意事项有哪些,下面就是实战案例,一起来看一下。 本文实例为大家分享了js实现动态进度条效果的具体代码,供大家参考,具体内容如下 1.效果 2.源码 window.onload …

    2025年3月8日
    200
  • js+css实现页面可控速度的打字效果

    这次给大家带来js+css实现页面可控速度的打字效果,js+css实现打字效果的注意事项有哪些,下面就是实战案例,一起来看一下。 本文实例为大家分享了JavaScript实现打字效果的具体代码,供大家参考,具体内容如下 1.效果 2.源码 …

    2025年3月8日
    200
  • vue.js项目nginx部署步骤详解

    这次给大家带来vue.js项目nginx部署步骤详解,vue.js项目nginx部署的js有哪些,下面就是实战案例,一起来看一下。 js完成。 二、编译部署 1、项目路径下demo输入命令npm run build 编译完成后会发现在dem…

    2025年3月8日
    200
  • 常用的6大JS排序算法与比较

    这次给大家带来常用的6大JS排序算法与比较,使用JS排序算法的注意事项有哪些,下面就是实战案例,一起来看一下。 1.冒泡排序 var bubbleSort = function(arr) { for (var i = 0, len = ar…

    编程技术 2025年3月8日
    200
  • JS有哪些属性可以判断页面是否存在滚动条

    这次给大家带来JS有哪些属性可以判断页面是否存在滚动条,JS判断页面是否存在滚动条的注意事项有哪些,下面就是实战案例,一起来看一下。 前言 最近在写插件的过程中,需要使用 JS 判断是否有滚动条,搜了一下,大致方法都差不多,但都有些啰嗦,代…

    2025年3月8日
    200
  • Nuxt.js实现服务端渲染步骤详解

    这次给大家带来Nuxt.js实现服务端渲染步骤详解,Nuxt.js实现服务端渲染的注意事项有哪些,下面就是实战案例,一起来看一下。 2016 年 10 月 25 日,zeit.co 背后的团队对外发布了一个 React 的服务端渲染应用框架…

    2025年3月8日 编程技术
    200
  • 热模块替换有哪几种实现方法

    这次给大家带来热模块替换有哪几种实现方法,热模块替换的注意事项有哪些,下面就是实战案例,一起来看一下。 稍微研究了一下,发现在angular/cli创建的项目中,实现这个不算太难,步骤如下: 1、首先创建一个src/environments…

    编程技术 2025年3月8日
    200
  • vue实现三级联动

    这次给大家带来vue实现三级联动,vue实现三级联动的注意事项有哪些,下面就是实战案例,一起来看一下。 1、实际效果 地址三级联动 mint-ui picker.png 2、首先你需要去下载一个包含中国省份,城市,区县的数据 如下:(这个地…

    2025年3月8日
    200
  • JS访问DOM对象选中节点方法

    这次给大家带来JS访问DOM对象选中节点方法,JS访问DOM对象选中节点的注意事项有哪些,下面就是实战案例,一起来看一下。 一 介绍 使用getElementById()方法来访问指定id的节点,并用nodeName属性、nodeType属…

    2025年3月8日
    200
  • JS操作DOM删除节点

    这次给大家带来JS操作DOM删除节点,JS操作DOM删除节点的注意事项有哪些,下面就是实战案例,一起来看一下。 一 介绍 删除节点通过使用removeChild()方法来实现。 removeChild()方法用来删除一个子节点。 obj. …

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论