Heim > Web-Frontend > js-Tutorial > Hauptteil

Detaillierte Erklärung der Operatorüberladung in Javascript

亚连
Freigeben: 2018-05-26 14:15:47
Original
1669 Leute haben es durchsucht

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

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
}
Nach dem Login kopieren

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)
        })
      }
    })
  }
}
Nach dem Login kopieren

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;)
}
Nach dem Login kopieren

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)
        })
      }
    })
  }
Nach dem Login kopieren

Für Code in Nicht-Klassen benötigen wir einen Container, hier habe ich zwei Methoden verwendet, eine verwendet ookay-Skript, so

let a = a+b // a, b sind Objektinstanzen

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)()
  }
Nach dem Login kopieren

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!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage