Dieses Mal zeige ich Ihnen, wie Sie Operatorüberladung in JS implementieren und was die Hinweise für die Implementierung von Operatorüberladung in JS sind. Hier ist ein praktischer Fall, nehmen wir einen sehen.
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. Zum Beispiel:
S = A + B (B - C.fun())/2 + D
wird übersetzt in
`S = replace(replace(A , ' +', replace(replace(B,'',(replace(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. Der wichtige Teil ist wie man den Code kompiliert. 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 die Elemente Addition, Subtraktion und Multiplikation sind und Division, dann entferne das vorherige Element vom Stapel und konvertiere es in „replace(last, Operator,
Wenn das Element „)“ ist, entferne das Element vom Stapel, spleiße es, bis es auf „(“ trifft, und drücke es in den Stapel. Beachten Sie hier '(' Gibt es einen Funktionsaufruf oder ein Ersetzen vor dem Element? Wenn es sich um einen Funktionsaufruf oder ein Ersetzen handelt, müssen Sie die Daten weiter nach vorne verschieben und die Ersetzungsfunktion schließen.
Wenn dies der Fall ist Ist ein allgemeines Element, prüfen Sie, ob das vorherige Element ersetzt wird, um die Ersetzungsfunktion zu schließen.
3 Erhalten Sie in Schritt 2 den kompilierten Ausdruck. 🎜>Implementieren Sie den Code gemäß dem obigen Prozess:
/** * 表达式转换工具方法 * @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(/["]+[^"]*["]+|[']+[^']*[']+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={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 < tmp.length; i++){ let item = tmp[i] if(/\*\*|\+|-|\*|\/|%|\.\/|\.\*/.test(tmp[i])) { let top = data.pop() let trans = 'replace(' + top + ',\'' + tmp[i] + '\',' data.push(trans) }else{ if (')' == tmp[i]) { let trans0 = tmp[i] let top0 = data.pop() while (top0 != '(') { 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 + ')' 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 + ')' break; }else{ pre = data.pop() trans1 = pre + trans1 + ')' pre = data[data.length - 1] } } data.push(trans1) } } } let result = '' data.forEach((value, key, own) => { result += value }) return result }
Der nächste Schritt besteht darin, den geschriebenen Code von unserem Übersetzer zu übersetzen Dies bedeutet, dass zwei Methoden erforderlich sind: Eine besteht darin, die Methodenattribute im Klassenkonstruktor neu zu definieren, und die andere besteht darin, den Code als Parameter an unsere benutzerdefinierte Methode zu übergeben Konstruktor:
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) }) } }) } }
Wie Sie oben sehen können, verwenden wir Object.defineProperty, um es im Konstruktor neu zu definieren. Der Code lautet 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('//') != -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('\n') }
Für neue Klassen müssen wir nur die OOOkay-Klasse erben, um
in der Klasse den Operator zu überladen. Für Code, der von Nicht-OOOkay-Klassen erbt, 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)
})
}
})
}
Die andere Möglichkeit besteht darin, den Code als Parameter an die $$-Methode zu übergeben, die den Code kompiliert und ausführt. wie folgt:
static $(fn) { if(!(fn instanceof Function)){ throw '参数错误' } (new Function(translate_block('function',fn.toString()))).call(window)() }
Empfohlen Lesen:
Wie vue-cli domänenübergreifende Anfragen stelltDetaillierte Erläuterung der Schritte zum automatischen Erstellen von rem für das mobile Webpack-TerminalDetaillierte Erläuterung der Schritte zum Rendern der Seite von NodeJS durch Antwort-WritebackDas obige ist der detaillierte Inhalt vonSo implementieren Sie die Operatorüberladung in JS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!