Maison >interface Web >js tutoriel >Explication détaillée de la surcharge des opérateurs en Javascript

Explication détaillée de la surcharge des opérateurs en Javascript

亚连
亚连original
2018-05-26 14:15:471686parcourir

Cet article résume et présente la méthode d'implémentation de la surcharge d'opérateurs en Javascript. L'idée d'implémentation est très simple. Les amis qui en ont besoin peuvent y jeter un œil

J'ai récemment effectué du traitement de données et personnalisé certaines structures de données. Par exemple, Mat, Vector, Point, etc., les quatre opérations arithmétiques telles que l'addition, la soustraction, la multiplication et la division doivent être définies à plusieurs reprises. Le code n'est pas très intuitif. Il est vraiment ennuyeux que JavaScript n'ait pas de surcharge d'opérateurs comme. C++ et C#, j'ai donc pensé que "Courbe pour sauver le pays" implémente automatiquement la surcharge d'opérateurs dans le code de traduction. L'idée d'implémentation est en fait très simple, elle 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 replace est très simple et n'a pas besoin d'être trop expliquée. La partie importante 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 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, le code d'implémentation est :

<.>

/**
 * 表达式转换工具方法
 * @param code
 */
export function translate (code) {
  let data = []
  let tmp_code = code.replace(/\s/g,&#39;&#39;)
  let tmp = []
  let vari = tmp_code.split(/["]+[^"]*["]+|[&#39;]+[^&#39;]*[&#39;]+|\*\*|\+|-|\*|\/|\(|\)|\?|>[=]|<[=]|={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
}

La méthode de compilation d'expression est écrite. Suivant Voilà comment faire traduire le code écrit par notre traducteur, c'est-à-dire qu'un conteneur est nécessaire, et deux méthodes : l'une consiste à redéfinir la méthode. attributs avec le constructeur de classe, et l'autre consiste à transmettre le code en tant que paramètre dans notre méthode personnalisée. Ensuite, introduisons 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 != &#39;constructor&#39;){
        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 le redéfinir dans le constructeur. Oui, Translate_block consiste à traduire l'intégralité du bloc de code. Le code est le suivant :

.

/**
 * 类代码块转换工具
 * @param name
 * @param block
 * @returns {string}
 */
export function translate_block (name , block) {
  let codes = block.split(&#39;\n&#39;)
  let reg = new RegExp(&#39;^&#39; + name + &#39;$&#39;)
  console.log(reg.source)
  codes[0] = codes[0].replace(name,&#39;function&#39;)
  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;)
}
Pour les nouvelles classes, nous devons uniquement hériter de la classe OOkay. La surcharge d'opérateurs est utilisée dans cette classe. Pour celles 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 != &#39;constructor&#39;) {
        Object.defineProperty(target, proto, {
          value:new Function(translate_block(proto, target[proto].toString())).call(target)
        })
      }
    })
  }
Pour ceux qui ne sont pas en classe Code, nous avons besoin d'un conteneur, ici j'utilise deux méthodes, l'une utilise un script ookay, comme celui-ci

fae67a96cbb1b8047895dfd8d47b32a2

let a = a+b // a et b sont des instances d'objet

2cacc6d41bbb37262a98f745aa00fbf0
L'autre consiste à transmettre le code comme un paramètre dans la méthode __$$__, qui compile le code et l'exécute, comme suit :


static __$__(fn) {
    if(!(fn instanceof Function)){
      throw &#39;参数错误&#39;
    }
    (new Function(translate_block(&#39;function&#39;,fn.toString()))).call(window)()
  }
J'ai compilé ce qui précède pour vous, je j'espère que cela vous sera utile à l'avenir

Articles connexes :

Comment résoudre les caractères chinois tronqués lorsque JQuery ajax renvoie json


Comment utiliser la méthode post d'ajax dans le framework Django


django Solution à l'erreur 403 lors de l'utilisation des données de publication jquery ajax


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