ホームページ >ウェブフロントエンド >jsチュートリアル >Javascriptの演算子のオーバーロードの詳細な説明
この記事では、JavaScript で演算子のオーバーロードを実装する方法を要約して紹介します。実装のアイデアは非常に簡単です。必要な方はぜひご覧ください。
私は最近、Mat、Vector などのいくつかのデータ構造をカスタマイズしました。 , Point. たとえば、加算、減算、乗算、除算などの四則演算は繰り返し定義する必要があり、コードがあまり直感的ではありません。JavaScript には演算子のオーバーロードがないのが非常に面倒です。 C++ と C# なので、「曲線で国を救いたい」と自動的に変換コードに演算子のオーバーロードを実装する実装アイデアは、実際には非常に簡単です。インタープリターを作成してコードをコンパイルするだけです。例:
S = A + B (B - C.fun())/2 + D
は
`S = replace(replace(A, '+', replace(replace(B) , '',(replace(B,'-',C.fun())))),'/',2),'+',D)`
replace 関数では、次の対応する演算子を呼び出します。オブジェクト関数、置換関数のコードは次のとおりです:
/**
* 转换方法
* @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 + '运算符无法识别'
}
}
置換の実装は非常に簡単で、あまり説明する必要はありません。重要な部分はコードをコンパイルする方法です。大学でデータ構造を学ぶ際の四則演算の実装がこの翻訳の基礎になっていますが、若干の違いがあります。プロセスを簡単に説明します:
1. 式を分割し、変数と演算子を抽出してメタ配列 A を取得します
2. メタ配列を走査します
要素が演算子の加算、減算、乗算、除算の場合は、スタックから前の要素をポップします。要素が ')' の場合、要素はスタックからポップされ、'(' に遭遇するまで結合され、スタックにプッシュされます。 ここで注意する必要があるのは、 '(' 要素の前に関数呼び出しまたは replace があるかどうか。関数呼び出しまたは replace の場合は、引き続きデータを前方にポップし、置換関数を閉じる必要があります。
それが一般的な要素の場合は、前の要素が置き換えられる場合は、')' を結合して置換関数を閉じる必要があります。それ以外の場合は、ステップ 2 で取得したスタック シーケンスを結合して、コンパイルされた式
を取得します。
/**
* 表达式转换工具方法
* @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
}
式のコンパイル メソッドが記述され、次のステップは、記述されたコードをトランスレータによって翻訳する方法です。これは、コンテナが必要であることを意味します。方法は 2 つあります。1 つはクラス コンストラクターでメソッドの属性を再定義する方法で、もう 1 つはカスタム メソッドのパラメーター Pass としてコードを使用する方法です。次に、クラス コンストラクターでの再定義メソッドを紹介します。
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)
})
}
})
}
}
上記からわかるように、コンストラクタ内で再定義するのに Object.defineProperty を使用し、translate_block で全体のコードブロックを分割して翻訳しています。 コードは次のとおりです。 新しいクラスの場合は、OOKay クラスを継承し、クラス内で演算子のオーバーロードを使用するだけです。OOOK 以外のクラスから継承するクラスの場合、メソッドは次のとおりです。
/**
* 类代码块转换工具
* @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')
}
非クラスのコードの場合、コンテナーが必要です。ここでは 2 つのメソッドを使用します。1 つは次のように ookay スクリプトで使用されます。
40fc65e74328a5ea22a3b784f397d3e4let a = a+b // aと b はオブジェクト インスタンスです
2cacc6d41bbb37262a98f745aa00fbf0もう 1 つは、次のようにコードを __$$__ メソッドにパラメータとして渡し、コードをコンパイルして実行します:
/**
* 非继承类的注入方法
* @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)
})
}
})
}
上記は皆さんのためにまとめたものです。今後皆さんのお役に立てれば幸いです。 Django フレームワークの post メソッド
django で jquery ajax post データを使用するときの 403 エラーの解決策
以上がJavascriptの演算子のオーバーロードの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。