Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erklärung der Operatorüberladung in Javascript

Detaillierte Erklärung der Operatorüberladung in Javascript

亚连
亚连Original
2018-05-26 14:15:471616Durchsuche

Dieser Artikel fasst die Methode zur Implementierung der Operatorüberladung in Javascript zusammen. Die Implementierungsidee ist sehr einfach.

Ich habe kürzlich die Datenverarbeitung durchgeführt und einige Datenstrukturen angepasst. Beispielsweise müssen die vier arithmetischen Operationen wie Addition, Subtraktion, Multiplikation und Division wiederholt definiert werden. Der Code ist nicht sehr intuitiv. Es ist wirklich ärgerlich, dass es in JavaScript keine Operatorüberladung gibt C++ und C#, daher dachte ich, dass „Curve to save the Country“ automatisch eine Operatorüberladung im Übersetzungscode implementiert. Die Implementierungsidee ist eigentlich sehr einfach, nämlich einen Interpreter zu schreiben und den Code zu kompilieren. Beispiel:

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

wird übersetzt in

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

In der Ersetzungsfunktion rufen wir die entsprechende Operatorfunktion des Objekts auf. Der Ersetzungsfunktionscode lautet wie folgt:

/**
 * 转换方法
 * @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 + '运算符无法识别'
  }
}

Die Implementierung des Ersetzens ist sehr einfach und erfordert keine allzu großen Erklärungen. Der wichtige Teil ist, wie der Code kompiliert wird. Die Implementierung der vier arithmetischen Operationen beim Studium der Datenstruktur an der Hochschule ist die Grundlage dieser Übersetzung, mit geringfügigen Unterschieden. Beschreiben Sie kurz den Prozess:

1. Teilen Sie den Ausdruck, extrahieren Sie Variablen und Operatoren, um das Metaarray A zu erhalten.
2 Durchlaufen Sie das Metaarray

Wenn das Element eine Operatoraddition ist oder Subtraktion Für Multiplikation und Division entfernen Sie das vorherige Element vom Stapel und konvertieren Sie es in replace(last, Operator,
Wenn das Element „)“ ist, entfernen Sie das Element vom Stapel und verbinden Sie es, bis es auf „(“ trifft. , und schieben Sie es in den Stapel. Hier müssen Sie darauf achten, ob dem Element „(“ ein Funktionsaufruf oder eine Ersetzung vorangeht. Wenn es sich um einen Funktionsaufruf oder eine Ersetzung handelt, müssen Sie die Daten weiterhin vorwärts und schließen die Ersetzungsfunktion.
Wenn es sich um ein allgemeines Element handelt, prüfen Sie, ob das vorherige Element ersetzt wird. Wenn ja, müssen Sie „)“ zusammenfügen, um die Ersetzungsfunktion zu schließen, andernfalls schieben Sie das Element direkt auf den Stapel

3. Kombinieren Sie die in Schritt 2 erhaltene Stapelsequenz, um den kompilierten Ausdruck zu erhalten.

Gemäß dem obigen Prozess lautet der Implementierungscode:

/**
 * 表达式转换工具方法
 * @param code
 */
export function translate (code) {
  let data = []
  let tmp_code = code.replace(/\s/g,'')
  let tmp = []
  let vari = tmp_code.split(/["]+[^"]*["]+|[&#39;]+[^&#39;]*[&#39;]+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={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
}

Die Ausdruckskompilierungsmethode ist geschrieben und der nächste Schritt besteht darin, wie man den geschriebenen Code von unserem Übersetzer übersetzen lässt. Wir benötigen einen Container und zwei Methoden: Eine besteht darin, die Methodenattribute neu zu definieren mit dem Klassenkonstruktor, und die andere besteht darin, den Code als Parameter an unsere benutzerdefinierte Methode zu übergeben. Als nächstes führen wir die Neudefinitionsmethode im Klassenkonstruktor ein:

export default class OOkay {
  constructor () {
    let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(this))
    protos.forEach((proto, key, own) => {
      if(proto != &#39;constructor&#39;){
        Object.defineProperty(this, proto, {
          value:new Function(translate_block(proto, this[proto].toString())).call(this)
        })
      }
    })
  }
}

Wie aus dem Obigen hervorgeht, verwenden wir Object.defineProperty, um es im Konstruktor neu zu definieren, und translator_block ist Der gesamte Codeblock wird geteilt und übersetzt. Der Code lautet wie folgt:

/**
 * 类代码块转换工具
 * @param name
 * @param block
 * @returns {string}
 */
export function translate_block (name , block) {
  let codes = block.split(&#39;\n&#39;)
  let reg = new RegExp(&#39;^&#39; + name + &#39;$&#39;)
  console.log(reg.source)
  codes[0] = codes[0].replace(name,&#39;function&#39;)
  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;)
}

Für neue Klassen können wir die Operatorüberladung in dieser Klasse verwenden, solange wir die OOKay-Klasse erben. Für diejenigen, die von Nicht-OOOkay-Klassen erben, können wir die Injektion wie folgt verwenden:

/**
   * 非继承类的注入方法
   * @param target
   */
  static inject (target) {
    let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(target))
    protos.forEach((proto, key, own) => {
      if (proto != &#39;constructor&#39;) {
        Object.defineProperty(target, proto, {
          value:new Function(translate_block(proto, target[proto].toString())).call(target)
        })
      }
    })
  }

Für Code in Nicht-Klassen benötigen wir einen Container, hier habe ich zwei Methoden verwendet, eine verwendet ookay-Skript, so
61b58cba80701ff9226e7f7700d4b66c
let a = a+b // a, b sind Objektinstanzen
2cacc6d41bbb37262a98f745aa00fbf0
Die andere besteht darin, den Code als Parameter an __$ zu übergeben $__-Methode, die den Code wie folgt kompiliert und ausführt:

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

Das Obige habe ich für Sie zusammengestellt. Ich hoffe, es wird hilfreich sein Sie in der Zukunft.

Verwandte Artikel:

Was tun, wenn chinesische Zeichen verstümmelt werden, wenn JQuery Ajax JSON zurückgibt

So verwenden Sie die Ajax-Post-Methode im Django-Framework

403-Fehler tritt auf, wenn JQuery-Ajax-Post-Daten in der Django-Lösung verwendet werden

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung der Operatorüberladung in Javascript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn