Javascript: operatör aşırı yükleme


95

JavaScript ile birkaç gündür çalışıyorum ve tanımlı nesnelerim için operatörleri aşırı yüklemek istediğim bir noktaya geldim.

Google'da bunu aradıktan sonra, bunu resmi olarak yapamayacağınız görülüyor, ancak bu eylemi gerçekleştirmenin uzun soluklu bir yolunu iddia eden birkaç kişi var.

Temel olarak bir Vector2 sınıfı oluşturdum ve aşağıdakileri yapabilmek istiyorum:

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.

Bunun yerine şunu yapmak zorundayım:

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. 

Vector2 sınıfımdaki operatörleri aşırı yüklemek için uygulayabileceğim bir yaklaşım var mı? Bu sadece çirkin görünüyor.



1
Az önce bir operatör aşırı yükleme kitaplığıyla karşılaştım. Henüz denemedim ve ne kadar iyi çalıştığını bilmiyorum: google.com/…
fishinear

Yanıtlar:


105

Bulduğunuz gibi JavaScript, operatör aşırı yüklemesini desteklemiyor. Gelebileceğiniz en yakın şey, toString(örneğin bir dizge olmaya zorlanması gerektiğinde valueOfçağrılacak olan ) uygulamaktır ve (bu, onu bir sayıya zorlamak için çağrılır, örneğin +toplama için kullanırken veya birçok durumda bunu birleştirme için kullanmak çünkü birleştirmeden +önce toplama yapmaya çalışır), ki bu oldukça sınırlıdır. Vector2Sonuç olarak nesne oluşturmanıza da izin vermez.


(Yerine bir sonucu olarak bir dize veya numara istiyorum bu soruya gelen insanlar için Vector2) olsa da, burada örnekleridir valueOfve toString. Bu örnekler yok sadece yararlanarak, operatör aşırı yüklenmesini gösteren JavaScript yerleşik ilkellere dönüştürme işleme:

valueOf

Bu örnek, bir valilkele zorlanmaya yanıt olarak bir nesnenin özelliğinin değerini iki katına çıkarır, örneğin +:

Veya ES2015'ler ile class:

Veya sadece nesnelerle, yapıcı yok:

toString

Bu örnek, bir valilkele zorlanmaya yanıt olarak bir nesnenin özelliğinin değerini büyük harfe dönüştürür, örneğin +:

Veya ES2015'ler ile class:

Veya sadece nesnelerle, yapıcı yok:


1
JS içinde doğru bir şekilde desteklenmese de, bugünlerde JS'yi özel özelliklerle genişletmek ve düz JS'ye geri aktarmak oldukça yaygındır, örneğin, SweetJS tam olarak bu sorunu çözmeyi hedeflemektedir.
Dmitri Zaitsev

1
DateSınıftaki karşılaştırma işleçleri, tarihleri ​​kullanarak örtük olarak tarihleri ​​sayılara dönüştürüyor valueOfmu? Örneğin yapabilirsiniz date2 > date1ve daha date2sonra oluşturulmuşsa bu doğru olacaktır date1.
Sean Letendre

1
@SeanLetendre: Evet. >, <, >=Ve <=(ancak ==, ===, !=, veya !==) kullanmak Özet İlişkisel Karşılaştırma işlemi, kullanımları ToPrimitiveipucu "number" ile. Bir Datenesnede, bu, getTimedöndüren sayıyla sonuçlanır (Epoch'tan beri-milisaniye değeri).
TJ Crowder 08

24

TJ'nin dediği gibi, JavaScript'te operatörleri aşırı yükleyemezsiniz. Bununla birlikte , her seferinde olduğu valueOfgibi işlevleri kullanmaktan daha iyi görünen add, ancak x ve y'nin 0 ile MAX_VALUE arasında olduğu vektöre kısıtlamalar getiren bir hack yazmak için bu işlevden yararlanabilirsiniz . İşte kod:

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);
};

O zaman şöyle denklemler yazabilirsiniz:

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]

7
Temelde OP'nin addyönteminin kodunu yazdınız ... Yapmak istemedikleri bir şey.
Ian Brindley

16
@IanBrindley OP bir operatörü aşırı yüklemek istedi, bu da açıkça böyle bir işlevi yazmayı planladığını gösteriyor. OP'nin kaygısı, doğal olmayan "ekle" demek zorunda kalmaktı; matematiksel olarak, vektör toplamayı bir +işaret ile temsil ediyoruz . Bu, yarı sayısal nesneler için doğal olmayan bir işlev adı çağırmanın nasıl önleneceğini gösteren çok iyi bir cevaptır.
Kittsil

1
@Kittsil Soru zaten bir add fonksiyonu kullandığımı gösteriyor. Yukarıdaki işlev hiç de kötü bir işlev olmasa da soruyu ele almadı, bu yüzden Ian'a katılıyorum.
Lee Brindley

Şimdiye kadar mümkün olan tek yol bu. +Operatörde sahip olduğumuz tek esneklik Number, işlenenlerden birinin yerine a döndürme yeteneğidir . Bu nedenle, Objectörnekleri çalıştıran herhangi bir ekleme işlevselliği , nesneyi her zaman bir olarak kodlamalı Numberve sonunda kodunu çözmelidir.
Gershom

İki vektörü çarparken bunun beklenmeyen bir sonuç (hata vermek yerine) döndüreceğini unutmayın. Ayrıca koordinatlar tam sayı olmalıdır.
user202729

8

Bilginize paper.js bu sorunu, vektörlerin operatör tarafından aşırı yüklenmesi ile kendi kendine yeten, kapsamlı bir javascript olan PaperScript oluşturarak ve daha sonra bunu tekrar javascript olarak işleyerek çözer.

Ancak bildiri dosyalarının özel olarak belirtilmesi ve bu şekilde işlenmesi gerekir.


8

Aslında, JavaScript biri varyantı vardır does destek operatör aşırı yüklenmesini. Photoshop ve Illustrator gibi Adobe uygulamaları tarafından kullanılan komut dosyası dili olan ExtendScript, operatör aşırı yüklemesine sahiptir. İçinde yazabilirsiniz:

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;

Bu, "Adobe Extendscript JavaScript araçları kılavuzu" nda ( buradaki mevcut bağlantı ) daha ayrıntılı olarak açıklanmaktadır . Sözdizimi görünüşe göre ECMAScript standardının (artık uzun süredir terk edilmiş) bir taslağına dayanıyordu.



3
PaperScript yanıtı yükseltilirken neden ExtendScript yanıtı düşürüldü? IMHO bu cevap da iyidir.
xmedeko

5

Bire yerleştirilmiş iki sayıyla vektör matematiği yapmak mümkündür. Nasıl çalıştığını açıklamadan önce önce bir örnek göstermeme izin verin:

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");

İki sayıyı X kez değiştirirseniz ve sonra onları geri kaydırmadan önce ekler veya çıkarırsanız, başlangıçta kaydırmamışsınız gibi aynı sonucu elde edeceğiniz gerçeğini kullanıyorum. Benzer şekilde skaler çarpma ve bölme, kaydırılmış değerler için simetrik olarak çalışır.

Bir JavaScript numarası 52 bit tamsayı hassasiyetine sahiptir (64 bit kayar), bu yüzden bir sayıyı mevcut 26 bitten daha yüksek ve bir sayıyı daha düşük olana paketleyeceğim. İmzalı numaraları desteklemek istediğim için kod biraz daha karmaşık hale getirildi.

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;
    }
}

Bununla görebildiğim tek dezavantaj, x ve y'nin + -33 milyon aralığında olması gerektiğidir, çünkü her biri 26 bit'e sığmaları gerekir.


Vec_pack'nin tanımı nerede?
İğrenç

1
@Disgusting Hmm üzgünüm, bunu eklemeyi unutmuşum gibi görünüyor ... Bu şimdi düzeltildi :)
Stuffe

4

Sorunun tam cevabı olmasa da, bazı python __magic__ yöntemlerini ES6 Sembollerini kullanarak uygulamak mümkündür.

Bir [Symbol.toPrimitive]()yöntem, bir çağrıyı ima etmenize izin vermez Vector.add(), ancak gibi sözdizimini kullanmanıza izin verir Decimal() + 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;
        }
    }
}

3

valueOfHer yinelemede yöntemden farklı değerlerle ok işlevini değerlendirmek için React benzeri Hook'ları kullanabiliriz .

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

Library @ js-basics / vector , Vector3 için aynı fikri kullanır.



1

Ham JS'de yapmak için bir grup kötü hilesinden yararlanan bir kütüphane yazdım. Bunun gibi ifadelere izin verir.

  • Karışık sayılar:

    >> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))

    <- {r: -2, i: 1}

  • Otomatik farklılaşma:

    Let f(x) = x^3 - 5x:

    >> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);

    Şimdi onu bazı değerler üzerinden eşleştirin:

    >> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)

    <- [ 7, -2, -5, -2, 7 ]

    yani f'(x) = 3x^2 - 5.

  • Polinomlar:

    >> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')

    <- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"

Özel probleminiz için, Vector2kütüphaneyi kullanarak bir işlev (veya belki daha kısa bir şey) tanımlar , sonra yazarsınız.x = Vector2()(x + y);

https://gist.github.com/pyrocto/5a068100abd5ff6dfbe69a73bbc510d7

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.