Saya telah bekerja dengan JavaScript selama beberapa hari sekarang dan sampai pada titik di mana saya ingin membebani operator untuk objek yang saya tentukan.
Setelah menjalankan tugas di google mencari ini, tampaknya Anda tidak bisa secara resmi melakukan ini, namun ada beberapa orang di luar sana yang mengklaim cara bertele-tele untuk melakukan tindakan ini.
Pada dasarnya saya telah membuat kelas Vector2 dan ingin dapat melakukan hal berikut:
var x = new Vector2(10,10);
var y = new Vector2(10,10);
x += y; //This does not result in x being a vector with 20,20 as its x & y values.
Sebaliknya saya harus melakukan ini:
var x = new Vector2(10,10);
var y = new Vector2(10,10);
x = x.add(y); //This results in x being a vector with 20,20 as its x & y values.
Apakah ada pendekatan yang dapat saya lakukan untuk membebani operator di kelas Vector2 saya? Karena ini hanya terlihat jelek.
javascript
operators
operator-overloading
Lee Brindley
sumber
sumber
Jawaban:
Seperti yang Anda temukan, JavaScript tidak mendukung kelebihan beban operator. Yang paling dekat yang bisa Anda lakukan adalah mengimplementasikan
toString
(yang akan dipanggil saat instance perlu dipaksa menjadi string) danvalueOf
(yang akan dipanggil untuk memaksanya menjadi angka, misalnya saat menggunakan+
untuk penambahan, atau dalam banyak kasus saat menggunakannya untuk penggabungan karena+
mencoba melakukan penambahan sebelum penggabungan), yang sangat terbatas. Keduanya tidak memungkinkan Anda membuatVector2
objek sebagai hasilnya.Untuk orang yang datang ke pertanyaan ini yang menginginkan string atau angka sebagai hasil (bukan a
Vector2
), berikut adalah contoh darivalueOf
dantoString
. Contoh-contoh ini tidak menunjukkan kelebihan beban operator, hanya memanfaatkan penanganan bawaan JavaScript yang dikonversi ke primitif:valueOf
Contoh ini menggandakan nilai
val
properti objek sebagai respons untuk dipaksa menjadi primitif, misalnya melalui+
:Tampilkan cuplikan kode
function Thing(val) { this.val = val; } Thing.prototype.valueOf = function() { // Here I'm just doubling it; you'd actually do your longAdd thing return this.val * 2; }; var a = new Thing(1); var b = new Thing(2); console.log(a + b); // 6 (1 * 2 + 2 * 2)
Atau dengan ES2015
class
:Tampilkan cuplikan kode
class Thing { constructor(val) { this.val = val; } valueOf() { return this.val * 2; } } const a = new Thing(1); const b = new Thing(2); console.log(a + b); // 6 (1 * 2 + 2 * 2)
Atau hanya dengan objek, tanpa konstruktor:
Tampilkan cuplikan kode
var thingPrototype = { valueOf: function() { return this.val * 2; } }; var a = Object.create(thingPrototype); a.val = 1; var b = Object.create(thingPrototype); b.val = 2; console.log(a + b); // 6 (1 * 2 + 2 * 2)
toString
Contoh ini mengonversi nilai
val
properti objek menjadi huruf besar sebagai respons untuk dipaksa menjadi primitif, misalnya melalui+
:Tampilkan cuplikan kode
function Thing(val) { this.val = val; } Thing.prototype.toString = function() { return this.val.toUpperCase(); }; var a = new Thing("a"); var b = new Thing("b"); console.log(a + b); // AB
Atau dengan ES2015
class
:Tampilkan cuplikan kode
class Thing { constructor(val) { this.val = val; } toString() { return this.val.toUpperCase(); } } const a = new Thing("a"); const b = new Thing("b"); console.log(a + b); // AB
Atau hanya dengan objek, tanpa konstruktor:
Tampilkan cuplikan kode
var thingPrototype = { toString: function() { return this.val.toUpperCase(); } }; var a = Object.create(thingPrototype); a.val = "a"; var b = Object.create(thingPrototype); b.val = "b"; console.log(a + b); // AB
sumber
Date
kelas secara implisit mengonversi tanggal menjadi angka menggunakanvalueOf
? Misalnya bisa Anda lakukandate2 > date1
dan akan benar jikadate2
dibuat setelahnyadate1
.>
,<
,>=
, Dan<=
(tapi tidak==
,===
,!=
, atau!==
) menggunakan Abstrak Relational Perbandingan operasi, yang menggunakanToPrimitive
dengan "nomor" petunjuk. Pada sebuahDate
objek, yang menghasilkan angka yanggetTime
kembali (nilai milidetik-sejak-The-Epoch).Seperti yang dikatakan TJ, Anda tidak dapat membebani operator di JavaScript. Namun Anda dapat memanfaatkan
valueOf
fungsi untuk menulis peretasan yang terlihat lebih baik daripada menggunakan fungsi sepertiadd
setiap saat, tetapi memberikan batasan pada vektor bahwa x dan y berada di antara 0 dan MAX_VALUE. Ini kodenya:var MAX_VALUE = 1000000; var Vector = function(a, b) { var self = this; //initialize the vector based on parameters if (typeof(b) == "undefined") { //if the b value is not passed in, assume a is the hash of a vector self.y = a % MAX_VALUE; self.x = (a - self.y) / MAX_VALUE; } else { //if b value is passed in, assume the x and the y coordinates are the constructors self.x = a; self.y = b; } //return a hash of the vector this.valueOf = function() { return self.x * MAX_VALUE + self.y; }; }; var V = function(a, b) { return new Vector(a, b); };
Kemudian Anda bisa menulis persamaan seperti ini:
var a = V(1, 2); //a -> [1, 2] var b = V(2, 4); //b -> [2, 4] var c = V((2 * a + b) / 2); //c -> [2, 4]
sumber
add
... Sesuatu yang tidak ingin mereka lakukan.+
tanda. Ini adalah jawaban yang sangat bagus yang menunjukkan bagaimana menghindari pemanggilan nama fungsi yang tidak wajar untuk objek kuasi-numerik.+
operator adalah kemampuan mengembalikanNumber
sebagai pengganti salah satu operan. Oleh karena itu, setiap fungsionalitas penambahan yang berfungsi sebagaiObject
contoh harus selalu menyandikan objek sebagaiNumber
, dan akhirnya mendekodekannya.FYI paper.js memecahkan masalah ini dengan membuat PaperScript, javascript cakupan dan mandiri dengan operator overloading vektor, yang kemudian diproses kembali menjadi javascript.
Tetapi file skrip makalah perlu secara khusus ditentukan dan diproses seperti itu.
sumber
Sebenarnya, ada satu varian dari JavaScript yang tidak operator overloading dukungan. ExtendScript, bahasa skrip yang digunakan oleh aplikasi Adobe seperti Photoshop dan Illustrator, memang memiliki kelebihan beban operator. Di dalamnya, Anda bisa menulis:
Vector2.prototype["+"] = function( b ) { return new Vector2( this.x + b.x, this.y + b.y ); } var a = new Vector2(1,1); var b = new Vector2(2,2); var c = a + b;
Ini dijelaskan secara lebih rinci dalam "Panduan alat JavaScript Adobe Extendscript" ( tautan terkini di sini ). Sintaksnya tampaknya didasarkan pada draf standar ECMAScript (sekarang sudah lama ditinggalkan).
sumber
Dimungkinkan untuk melakukan matematika vektor dengan dua angka yang dikemas menjadi satu. Izinkan saya menunjukkan contoh terlebih dahulu sebelum saya menjelaskan cara kerjanya:
let a = vec_pack([2,4]); let b = vec_pack([1,2]); let c = a+b; // Vector addition let d = c-b; // Vector subtraction let e = d*2; // Scalar multiplication let f = e/2; // Scalar division console.log(vec_unpack(c)); // [3, 6] console.log(vec_unpack(d)); // [2, 4] console.log(vec_unpack(e)); // [4, 8] console.log(vec_unpack(f)); // [2, 4] if(a === f) console.log("Equality works"); if(a > b) console.log("Y value takes priority");
Saya menggunakan fakta bahwa jika Anda menggeser dua angka X kali dan kemudian menambahkan atau menguranginya sebelum menggesernya kembali, Anda akan mendapatkan hasil yang sama seolah-olah Anda tidak menggesernya untuk memulai. Demikian pula perkalian dan pembagian skalar bekerja secara simetris untuk nilai-nilai yang bergeser.
Nomor JavaScript memiliki presisi integer 52 bit (64 bit float), jadi saya akan mengemas satu angka menjadi 26 bit yang tersedia lebih tinggi, dan satu ke lebih rendah. Kode dibuat sedikit lebih berantakan karena saya ingin mendukung nomor yang ditandatangani.
function vec_pack(vec){ return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]); } function vec_unpack(number){ switch(((number & 33554432) !== 0) * 1 + (number < 0) * 2){ case(0): return [(number % 33554432),Math.trunc(number / 67108864)]; break; case(1): return [(number % 33554432)-33554432,Math.trunc(number / 67108864)+1]; break; case(2): return [(((number+33554432) % 33554432) + 33554432) % 33554432,Math.round(number / 67108864)]; break; case(3): return [(number % 33554432),Math.trunc(number / 67108864)]; break; } }
Satu-satunya downside yang dapat saya lihat dengan ini adalah bahwa x dan y harus berada dalam kisaran + -33 juta, karena masing-masing harus muat dalam 26 bit.
sumber
Meskipun bukan jawaban pasti untuk pertanyaan tersebut, dimungkinkan untuk menerapkan beberapa metode python __magic__ menggunakan Simbol ES6
Sebuah
[Symbol.toPrimitive]()
metode tidak membiarkan Anda menyiratkan panggilanVector.add()
, tetapi akan membiarkan Anda menggunakan sintaks sepertiDecimal() + int
.class AnswerToLifeAndUniverseAndEverything { [Symbol.toPrimitive](hint) { if (hint === 'string') { return 'Like, 42, man'; } else if (hint === 'number') { return 42; } else { // when pushed, most classes (except Date) // default to returning a number primitive return 42; } } }
sumber
Kita dapat menggunakan React-like Hooks untuk mengevaluasi fungsi panah dengan nilai yang berbeda dari
valueOf
metode pada setiap iterasi.const a = Vector2(1, 2) // [1, 2] const b = Vector2(2, 4) // [2, 4] const c = Vector2(() => (2 * a + b) / 2) // [2, 4] // There arrow function will iterate twice // 1 iteration: method valueOf return X component // 2 iteration: method valueOf return Y component
Tampilkan cuplikan kode
const Vector2 = (function() { let index = -1 return function(x, y) { if (typeof x === 'function') { const calc = x index = 0, x = calc() index = 1, y = calc() index = -1 } return Object.assign([x, y], { valueOf() { return index == -1 ? this.toString() : this[index] }, toString() { return `[${this[0]}, ${this[1]}]` }, len() { return Math.sqrt(this[0] ** 2 + this[1] ** 2) } }) } })() const a = Vector2(1, 2) const b = Vector2(2, 4) console.log('a = ' + a) // a = [1, 2] console.log(`b = ${b}`) // b = [2, 4] const c = Vector2(() => (2 * a + b) / 2) // [2, 4] a[0] = 12 const d = Vector2(() => (2 * a + b) / 2) // [13, 4] const normalized = Vector2(() => d / d.len()) // [0.955..., 0.294...] console.log(c, d, normalized)
Library @ js-basics / vector menggunakan ide yang sama untuk Vector3.
sumber
Menarik juga eksperimental operator perpustakaan -overloading-js . Itu melakukan overloading dalam konteks yang ditentukan (fungsi panggilan balik) saja.
sumber
Saya menulis perpustakaan yang mengeksploitasi banyak peretasan jahat untuk melakukannya dalam JS mentah. Ini memungkinkan ekspresi seperti ini.
Bilangan kompleks:
>> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))
<- {r: -2, i: 1}
Diferensiasi otomatis:
Biarkan
f(x) = x^3 - 5x
:>> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);
Sekarang petakan pada beberapa nilai:
>> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)
<- [ 7, -2, -5, -2, 7 ]
yaitu
f'(x) = 3x^2 - 5.
Polinomial:
>> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')
<- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"
Untuk masalah khusus Anda, Anda akan menentukan
Vector2
fungsi (atau mungkin sesuatu yang lebih pendek) menggunakan pustaka, lalu tulisx = Vector2()(x + y);
https://gist.github.com/pyrocto/5a068100abd5ff6dfbe69a73bbc510d7
sumber