Maison  >  Article  >  interface Web  >  Comment implémenter la surcharge d'opérateur dans JS

Comment implémenter la surcharge d'opérateur dans JS

php中世界最好的语言
php中世界最好的语言original
2018-05-02 10:31:491769parcourir

Cette fois, je vais vous montrer comment implémenter la surcharge d'opérateur en JS, et quelles sont les notes pour implémenter la surcharge d'opérateur en JS Voici un cas pratique, prenons un. regarder.

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

se traduit par

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

Dans la fonction replace, on appelle la fonction opérateur correspondante de l'objet. Le code de la fonction replace 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 + '运算符无法识别'
  }
}

La mise en œuvre du remplacement est très simple, sans trop d'explications, l'important. une partie est de savoir comment compiler le 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 les éléments sont des opérateurs d'addition, de soustraction, de multiplication. et division, puis extrayez l'élément précédent de la pile et convertissez-le en remplacement (dernier, opérateur,
Si l'élément est ')', extrayez l'élément de la pile, épissez-le jusqu'à ce qu'il rencontre '(' et appuyez sur dans la pile. Notez ici '(' Y a-t-il un appel de fonction ou un remplacement avant l'élément ? S'il s'agit d'un appel ou d'un remplacement de fonction, vous devez continuer à faire avancer les données et fermer la fonction de remplacement.
Si c'est le cas. est un élément général, vérifiez si l'élément précédent est remplacé. Si oui, vous devez épisser ')' pour fermer la fonction de remplacement, sinon poussez l'élément directement sur la pile

3. obtenu à l'étape 2 pour obtenir l'expression compilée. 🎜>Selon le processus ci-dessus, implémentez le code :

La méthode de compilation de l'expression a été écrite. L'étape suivante consiste à traduire le code écrit. par notre traducteur, c'est-à-dire qu'un conteneur est nécessaire, deux types Méthode : L'une consiste à redéfinir les attributs de la méthode dans le constructeur de classe et l'autre consiste à transmettre le code en tant que paramètre à notre méthode personnalisée. Ensuite, nous présenterons le. méthode redéfinie dans le constructeur de classe :
/**
 * 表达式转换工具方法
 * @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
}

Comme le montre ce qui précède, nous utilisons Object.defineProperty pour le redéfinir dans le constructeur Translate_block, c'est-à-dire diviser le bloc de code entier et le traduire. est la suivante :
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)
        })
      }
    })
  }
}

Pour les nouvelles classes, nous n'avons besoin que d'hériter. La classe OOkay peut utiliser la surcharge de l'opérateur
/**
 * 类代码块转换工具
 * @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;)
}
dans cette classe. Pour celles qui héritent de classes non OK, nous pouvons utiliser l'injection. , comme suit :

Pour les non-classes, nous avons besoin d'un conteneur Ici, j'utilise deux méthodes. L'une utilise un script ookay, comme ceci


L'autre consiste à passer le code en tant que paramètre dans la méthode $$, qui compile le code et l'exécute comme suit :


Je pense 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 chinois PHP. site Web

static $(fn) {
    if(!(fn instanceof Function)){
      throw '参数错误'
    }
    (new Function(translate_block('function',fn.toString()))).call(window)()
  }
Lecture recommandée :

Comment créer du cross-domain avec vue-cli ! Demande

Explication détaillée du terminal mobile webpack automatisé étapes de construction de rem


Explication détaillée de la page de rendu nodejs via l'écriture de réponse

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn