Maison > interface Web > js tutoriel > le corps du texte

Comment implémenter la surcharge d'opérateur à l'aide de JS

php中世界最好的语言
Libérer: 2018-05-26 15:36:00
original
1628 Les gens l'ont consulté

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 + '运算符无法识别'
  }
}
Copier après la connexion

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(/["]+[^"]*["]+|[&#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
}
Copier après la connexion

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)
        })
      }
    })
  }
}
Copier après la connexion

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(&#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;)
}
Copier après la connexion

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)
        })
      }
    })
  }
Copier après la connexion

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)()
  }
Copier après la connexion

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!

source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal