Cette fois, je vais vous montrer comment utiliser JS pour implémenter la surcharge d'opérateurs, et quelles sont les précautions à prendre pour utiliser JS pour implémenter la surcharge d'opérateurs. Ce qui suit est un cas pratique, jetons un coup d'œil.
J'ai récemment fait du traitement de données et j'ai personnalisé certaines structures de données, telles que Mat, Vecteur, Point, etc. Je dois définir à plusieurs reprises les quatre opérations arithmétiques telles que l'addition, la soustraction, la multiplication et la division. ne semble pas très intuitif, javascript Le manque de surcharge d'opérateurs, une fonctionnalité comme C++ et C#, est vraiment frustrant, j'ai donc voulu "sauver le pays" en implémentant automatiquement la surcharge d'opérateurs dans le code de traduction. L'idée d'implémentation est en fait très simple. , qui consiste à écrire un interpréteur et à compiler le code. Par exemple :
S = A + B (B - C.fun())/2 + D
est traduit par
` S = remplacer(remplacer(A, '+', remplacer(remplacer(B,'',(remplacer(B,'-',C.fun())))),'/',2),' +' ,D)`
Dans la fonction de remplacement, nous appelons la fonction opérateur correspondante de l'objet. Le code de la fonction de remplacement est le suivant :
/** * 转换方法 * @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 + '运算符无法识别' } }
L'implémentation de remplacement est. très simple et n'a pas besoin d'être trop expliqué, la partie importante est de savoir comment implémenter la compilation du code. La mise en œuvre des quatre opérations arithmétiques lors de l’étude de la structure des données au collège constitue la base de cette traduction, avec de légères différences. Décrivez brièvement le processus :
1. Divisez l'expression, extrayez les variables et les opérateurs pour obtenir le méta-tableau A
2 Parcourez le méta-tableau
Si l'élément est une addition et une soustraction d'opérateurs. Pour la multiplication et la division, extrayez l'élément précédent de la pile et convertissez-le en replace(last, Operator,
Si l'élément est ')', extrayez l'élément de la pile, épissez-le jusqu'à ce qu'il rencontre '(', et placez-le dans la pile. Ici, vous devez faire attention à savoir si l'élément '(' est précédé d'un appel de fonction ou d'un remplacement. S'il s'agit d'un appel ou d'un remplacement de fonction, vous devez continuer à faire avancer les données et fermer le remplacement. fonction.
S'il s'agit d'un élément général, vérifiez si l'élément précédent remplace, si c'est le cas, vous devez épisser ')' pour fermer la fonction de remplacement, sinon poussez l'élément directement sur la pile
3. Combinez la séquence de pile obtenue à l'étape 2 pour obtenir l'expression compilée
Selon le processus ci-dessus, implémentez le 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(/["]+[^"]*["]+|[']+[^']*[']+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={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 }
Après le. Lorsque la méthode de compilation d'expression est écrite, l'étape suivante consiste à faire traduire le code écrit par notre traducteur, c'est-à-dire que nous avons besoin d'un conteneur et de deux méthodes : l'une consiste à redéfinir les attributs de la méthode dans le constructeur de classe et l'autre. pour passer le code en paramètre à notre méthode personnalisée. Ensuite, nous introduirons la méthode de redéfinition dans le constructeur de classe :
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) }) } }) } }
Comme le montre ce qui précède, nous utilisons Object.defineProperty pour la redéfinir. dans le constructeur, Translate_block consiste à diviser l'intégralité du bloc de code et à le traduire. Le code est le suivant :
/** * 类代码块转换工具 * @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') }
Pour les nouvelles classes, il suffit d'hériter de la classe OOkay et d'utiliser la surcharge d'opérateurs dans la classe. . Pour ceux qui héritent de classes non-OOOkay, nous pouvons utiliser l'injection, comme suit :
/** * 非继承类的注入方法 * @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) }) } }) }
Pour les non-classes. Pour le code, nous avons besoin d'un conteneur. Ici, j'utilise deux méthodes. ookay script, comme celui-ci
L'autre est pour passer le code en paramètre dans la méthode __$$__, qui compile le code et l'exécute, comme suit :
static __$__(fn) { if(!(fn instanceof Function)){ throw '参数错误' } (new Function(translate_block('function',fn.toString()))).call(window)() }
Cela implémente la surcharge d'opérateurs
Je crois que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web chinois de PHP
Lire recommandée :
Comment utiliser Vue pour implémenter l'effet de commutation de carrousel de composants internes
Comment utiliser Angular5 pour ajouter une classe de style aux étiquettes des composants
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!