>  기사  >  웹 프론트엔드  >  JS에서 연산자 오버로딩을 구현하는 방법

JS에서 연산자 오버로딩을 구현하는 방법

php中世界最好的语言
php中世界最好的语言원래의
2018-05-02 10:31:491812검색

이번에는 JS에서 연산자 오버로딩을 구현하는 방법과 JS에서 연산자 오버로딩을 구현하기 위한 노트가 무엇인지 보여드리겠습니다. 다음은 실제 사례입니다.

최근에 데이터 처리를 하다가 Mat, Vector, Point 등 일부 데이터 구조를 사용자 정의했습니다. 덧셈, 뺄셈, 곱셈, 나눗셈 등 4가지 산술 연산을 반복적으로 정의해야 하는 코드입니다. JavaScript에는 이에 대한 연산자 오버로딩이 없습니다. C++ 및 C#과 같은 기능은 정말 불쾌하므로 번역 코드에서 자동으로 연산자를 오버로드하여 "국가를 구하고 싶습니다". 구현 아이디어는 실제로 매우 간단합니다. , 인터프리터를 작성하고 코드를 컴파일하는 것입니다. 예:

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

`S = replacement(replace(A, '+', replacement(replace(B,')로 번역됩니다. ', (replace(B,'-',C.fun())))),'/',2),'+',D)`

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 + '运算符无法识别'
  }
}

교체 구현은 너무 많은 설명 없이 매우 간단합니다. 중요한 부분은 코드를 컴파일하는 방법입니다. 대학에서 데이터 구조를 공부할 때 네 가지 산술 연산을 구현하는 것이 이 번역의 기초이지만 약간의 차이가 있습니다. 프로세스를 간략하게 설명하세요.

1. 표현식을 분할하고 변수와 연산자를 추출하여 메타배열 A를 얻습니다.
2. 메타배열을 탐색합니다.

요소가 연산자 덧셈, 뺄셈, 곱셈 및 나눗셈인 경우 스택에서 이전 요소를 팝하고
replace(last, 연산자로 변환,
요소가 ')'인 경우 스택에서 요소를 팝하고 '('를 만날 때까지 연결한 다음 스택에 푸시합니다. 여기서 요소가 있는지 여부에 주의해야 합니다. '(' 앞에는 함수 호출 또는 교체가 있습니다. 함수 호출 또는 교체인 경우 계속해서 데이터를 앞으로 팝하고 교체 함수를 닫아야 합니다.

일반 요소인 경우 이전 요소가 있는지 확인하세요. 그렇다면 ')'를 연결하여 교체 기능을 닫아야 합니다.

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(/["]+[^"]*["]+|[&#39;]+[^&#39;]*[&#39;]+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={2}|:|&{2}|\|{2}|\{|\}|=|%|\.\/|\.\*|,/g)
  for (let i = 0,len = ops.length; i < len; i++) {
    if (vari[i] != &#39;&#39;) {
      tmp.push(vari[i])
    }
    if (ops[i] != &#39;&#39;) {
      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 = &#39;replace(&#39; + top + &#39;,\&#39;&#39; + tmp[i] + &#39;\&#39;,&#39;
      data.push(trans)
    }else{
      if (&#39;)&#39; == tmp[i]) {
        let trans0 = tmp[i]
        let top0 = data.pop()
        while (top0 != &#39;(&#39;) {
          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 + &#39;)&#39;
          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 + &#39;)&#39;
            break;
          }else{
            pre = data.pop()
            trans1 = pre + trans1 + &#39;)&#39;
            pre = data[data.length - 1]
          }
        }
        data.push(trans1)
      }
    }
  }
  let result = &#39;&#39;
  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를 사용합니다. 생성자에서 이를 재정의하는 것은 전체 코드 블록을 번역하는 것입니다. 코드는 다음과 같습니다:

/**
 * 类代码块转换工具
 * @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(&#39;//&#39;) != -1) {
      codes[i] = codes[i].substring(0,codes[i].indexOf(&#39;//&#39;))
    }
    if(/\*\*|\+|-|\*|\/|%|\.\/|\.\*/g.test(codes[i])){
      if (codes[i].indexOf(&#39;return &#39;) != -1) {
        let ret_index = codes[i].indexOf(&#39;return &#39;) + 7
        codes[i] = codes[i].substring(0,ret_index) + translate(codes[i].substring(ret_index))
      }else {
        let eq_index = codes[i].indexOf(&#39;=&#39;) + 1
        codes[i] = codes[i].substring(0,eq_index) + translate(codes[i].substring(eq_index))
      }
    }
  }
  return &#39;return &#39; + codes.join(&#39;\n&#39;)
}
새 클래스의 경우 OOkay 클래스를 상속한 다음 클래스에서 연산자오버로딩을 사용하면 됩니다. OOOkay가 아닌 클래스에서 상속하려면 다음과 같이 주입을 사용할 수 있습니다.

/**
   * 非继承类的注入方法
   * @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)()
  }

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 자세한 내용은 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 도서:

How vue -cli가 도메인 간 요청을 합니다

웹팩 모바일 터미널 자동화 구성 렘 단계에 대한 자세한 설명

응답 쓰기 저장을 통한 nodejs 렌더링 이 페이지의 단계에 대한 자세한 설명

위 내용은 JS에서 연산자 오버로딩을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.