[javascript] 자바 스크립트 : 연산자 오버로딩

저는 며칠 동안 JavaScript로 작업 해 왔으며 정의 된 개체에 대해 연산자를 오버로드하고 싶은 시점에 도달했습니다.

Google에서 이것을 검색 한 후 공식적으로 이것을 할 수없는 것 같지만,이 작업을 수행하는 오랜 방법을 주장하는 사람들이 있습니다.

기본적으로 Vector2 클래스를 만들고 다음을 수행 할 수 있기를 원합니다.

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.

대신 다음을 수행해야합니다.

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 클래스에서 연산자를 오버로드 할 수있는 방법이 있습니까? 이것은 단지 추악한 것처럼 보입니다.



답변

아시다시피 JavaScript는 연산자 오버로딩을 지원하지 않습니다. 가장 가까운 방법은 구현 toString(인스턴스가 문자열이되도록 강제해야 할 때 호출 됨) 및 valueOf(예를 들어 +덧셈 을 위해 사용할 때 또는 많은 경우에 숫자로 강제하기 위해 호출 됩니다. 연결하기 +전에 더하기를 시도하기 때문에 연결에 사용), 이는 매우 제한적입니다. Vector2결과적 으로 개체 를 만들 수 없습니다 .


결과로 (a 대신) 문자열이나 숫자를 원하는이 질문에 오는 사람들을 위해 다음 Vector2valueOf및의 예입니다 toString. 이 예제 연산자 오버로딩을 보여 주지 않고 기본으로 변환하는 JavaScript의 내장 처리를 활용합니다.

valueOf

이 예제는 예 val를 들어 다음을 통해 프리미티브로 강제 변환되는 것에 대한 응답으로 객체의 속성 값을 두 배로 늘립니다 +.

또는 ES2015의 경우 class:

코드 스 니펫 표시

또는 객체 만 있으면 생성자가 없습니다.

toString

이 예제는 다음 val을 통해 기본 요소로 강제 변환되는 것에 대한 응답으로 객체의 속성 값 을 대문자로 변환합니다 +.

또는 ES2015의 경우 class:

또는 객체 만 있으면 생성자가 없습니다.


답변

TJ가 말했듯이 JavaScript에서는 연산자를 오버로드 할 수 없습니다. 그러나 매번 valueOf같은 함수를 사용하는 것보다 더 좋아 보이는 해킹을 작성하는 함수를 활용할 수 add있지만 x와 y가 0과 MAX_VALUE 사이에 있다는 제약을 벡터에 부과합니다. 다음은 코드입니다.

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

그런 다음 다음과 같은 방정식을 작성할 수 있습니다.

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]


답변

참고로 paper.js는 벡터의 연산자 오버로딩이 포함 된 자체 포함 범위의 자바 스크립트 인 PaperScript를 만든 다음 자바 스크립트로 다시 처리함으로써이 문제를 해결합니다.

그러나 종이 스크립트 파일은 구체적으로 지정하고 처리해야합니다.


답변

사실, 자바 스크립트의 한 변형이 수행 지원 연산자 오버로딩은. Photoshop 및 Illustrator와 같은 Adobe 응용 프로그램에서 사용하는 스크립팅 언어 인 ExtendScript에는 연산자 오버로딩이 있습니다. 그 안에 다음과 같이 작성할 수 있습니다.

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;

자세한 내용은 “Adobe Extendscript JavaScript 도구 가이드”(현재 링크 )에 설명되어 있습니다. 구문은 분명히 ECMAScript 표준의 초안 (현재는 버려진)을 기반으로합니다.


답변

두 개의 숫자를 하나로 묶어 벡터 수학을 할 수 있습니다. 작동 원리를 설명하기 전에 먼저 예를 보여 드리겠습니다.

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

두 숫자를 X 번 비트 시프트 한 다음 다시 시프트하기 전에 더하거나 빼면 처음부터 시프트하지 않은 것처럼 동일한 결과를 얻을 수 있다는 사실을 사용하고 있습니다. 마찬가지로 스칼라 곱셈과 나눗셈은 시프트 된 값에 대해 대칭 적으로 작동합니다.

JavaScript 숫자는 정수 정밀도가 52 비트 (64 비트 부동 소수점)이므로 하나의 숫자를 더 높은 26 비트에, 하나는 더 낮은 숫자에 압축합니다. 부호있는 숫자를 지원하고 싶었 기 때문에 코드가 좀 더 복잡해졌습니다.

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

내가 볼 수있는 유일한 단점은 x와 y가 각각 26 비트 내에 들어가야하기 때문에 + -3300 만 범위에 있어야한다는 것입니다.


답변

질문에 대한 정확한 답은 아니지만 ES6 기호를 사용하여 파이썬 __magic__ 메서드 중 일부를 구현할 수 있습니다.

[Symbol.toPrimitive]()방법은 전화를 의미하지 않습니다 Vector.add(),하지만 당신과 같은 구문을 사용하게됩니다 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;
        }
    }
}


답변

흥미로운 것은 실험적인 라이브러리 operator-overloading-js 입니다. 정의 된 컨텍스트 (콜백 함수)에서만 오버로딩을 수행합니다.