Heim > Web-Frontend > js-Tutorial > So implementieren Sie die Operatorüberladung mit JS

So implementieren Sie die Operatorüberladung mit JS

php中世界最好的语言
Freigeben: 2018-05-26 15:36:00
Original
1666 Leute haben es durchsucht

Dieses Mal zeige ich Ihnen, wie Sie JS zum Implementieren einer Operatorüberladung verwenden und welche Vorsichtsmaßnahmen für die Verwendung von JS zum Implementieren einer Operatorüberladung gelten. Das Folgende ist ein praktischer Fall, schauen wir uns das an.

Ich habe kürzlich Daten verarbeitet und einige Datenstrukturen wie Mat, Vektor, Punkt usw. angepasst. Ich muss den Code wiederholt für vier arithmetische Operationen wie Addition, Subtraktion, Multiplikation und Division definieren Scheint nicht sehr intuitiv zu sein, Javascript Das Fehlen einer Operatorüberladung, eine Funktion wie C++ und C#, ist wirklich frustrierend, deshalb wollte ich „das Land retten“, indem ich die Operatorüberladung automatisch in den Übersetzungscode implementierte. Die Implementierungsidee ist eigentlich sehr einfach , das heißt, 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 Es ist einfach und muss nicht zu viel erklärt werden. Der Teil besteht darin, wie die Kompilierung des Codes implementiert 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 Metaarray A zu erhalten.
2 Durchlaufen Sie das Metaarray

Wenn das Element Operatoraddition und -subtraktion ist Nehmen Sie für Multiplikation und Division das vorherige Element vom Stapel und konvertieren Sie es in „replace(last, Operator,
Wenn das Element „)“ ist, entfernen Sie das Element vom Stapel, 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 weiter nach vorne verschieben und die Ersetzung schließen Funktion.
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.

Implementieren Sie gemäß dem obigen Prozess den Code:

/**
 * 表达式转换工具方法
 * @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
}
Nach dem Login kopieren

Die Ausdruckskompilierungsmethode ist geschrieben, und der nächste Schritt besteht darin, den geschriebenen Code von unserem Übersetzer übersetzen zu lassen, d. h. Wir benötigen einen Container und zwei Methoden: Eine besteht darin, die Methodenattribute im Klassenkonstruktor neu zu definieren, und die andere besteht darin, den Code als zu übergeben Als nächstes führen wir die neu definierte Methode im Klassenkonstruktor ein:

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

Wie oben zu sehen ist, verwenden wir Object.defineProperty, um sie im Konstruktor neu zu definieren Teilen Sie den gesamten Codeblock und übersetzen Sie ihn wie folgt:

/**
 * 类代码块转换工具
 * @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;)
}
Nach dem Login kopieren

Für die neue Klasse können wir die Operatorüberladung in dieser Klasse verwenden Aus Nicht-OOOkay-Klassen 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 != 'constructor') {
        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. Ich habe hier zwei Methoden verwendet, eine verwendet Ookay-Skript, wie folgt

Die andere besteht darin, den Code als Parameter zu übergeben in die __$$__-Methode, die den Code kompiliert und wie folgt ausführt:

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

Auf diese Weise wird die Operation realisiert. Überladen von Symbolen

Ich glaube, Sie beherrschen die Methode Nachdem Sie den Fall in diesem Artikel gelesen haben, lesen Sie bitte andere verwandte Artikel auf der chinesischen PHP-Website.

Empfohlene Lektüre:

Wie man mit Vue erreicht interner Komponentenkarussell-Umschalteffekt

So verwenden Sie Angular5, um Stilklassen zu Komponentenbeschriftungen hinzuzufügen

Das obige ist der detaillierte Inhalt vonSo implementieren Sie die Operatorüberladung mit JS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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