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


95

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



1
운영자 과부하 라이브러리를 발견했습니다. 시도해 보지 않았고 얼마나 잘 작동하는지 모르겠습니다. google.com/…
fishinear

답변:


104

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


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

valueOf

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

또는 ES2015의 경우 class:

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

toString

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

또는 ES2015의 경우 class:

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


1
JS 내에서는 지원되지 않지만, 요즘에는 커스텀 기능으로 JS를 확장하고 일반 JS로 다시 트랜스 파일하는 것이 일반적 입니다. 예를 들어 SweetJS 는이 문제를 정확히 해결하는 것을 목표로하고 있습니다.
드미트리 Zaitsev

1
Date클래스 의 비교 연산자는 valueOf? 를 사용하여 암시 적으로 날짜를 숫자로 변환 합니까? 예를 들어 할 수 date2 > date1있으며 date2이후에 생성 된 경우 true가됩니다 date1.
Sean Letendre

1
@SeanLetendre : 네. >, <, >=,과 <=(그러나 ==, ===, !=, 또는 !==)을 사용 추상 관계 비교 작업, 사용 ToPrimitive힌트 "번호"로한다. A의 Date개체 수에 해당하는 결과를 getTime반환 (밀리 초 - 사람 - 더 - 에포크 값).
TJ Crowder

24

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]

7
기본적으로 OP의 add방법에 대한 코드를 작성했습니다 ... 그들이 원하지 않는 일.
Ian Brindley 2015 년

15
@IanBrindley OP는 연산자에 오버로드를 원했는데, 이는 그가 그러한 함수를 작성할 계획임을 분명히 암시합니다. OP의 우려는 부 자연스러운 "add"를 호출해야한다는 것이었다. 수학적으로 벡터 덧셈을 +부호로 나타냅니다 . 이것은 준 숫자 객체에 대해 부 자연스러운 함수 이름을 호출하는 것을 피하는 방법을 보여주는 매우 좋은 대답입니다.
Kittsil

1
@Kittsil 질문은 내가 이미 추가 기능을 사용하고 있음을 보여줍니다. 위의 기능은 전혀 나쁜 기능은 아니지만 질문을 해결하지 않았으므로 Ian에 동의합니다.
Lee Brindley

현재로서는 이것이 유일한 방법입니다. +연산자에 대한 유일한 유연성 Number은 피연산자 중 하나의 대체물로 a 를 반환하는 기능 입니다. 따라서 Object인스턴스 를 작동하는 모든 추가 기능 은 항상 객체를로 인코딩 Number하고 결국 디코딩해야합니다.
Gershom

두 벡터를 곱하면 오류가 발생하는 대신 예상치 못한 결과가 반환됩니다. 또한 좌표는 정수 여야합니다.
user202729

8

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

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


8

사실, 자바 스크립트의 한 변형이 수행 지원 연산자 오버로딩은. 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 표준의 초안 (현재는 버려진)을 기반으로합니다.


11
수정 ExtendScript = 자바 스크립트!
Andrio Skur

3
ExtendScript 답변이 반대 투표되고 PaperScript 답변이 찬성되는 이유는 무엇입니까? IMHO이 답변도 좋습니다.
xmedeko

5

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

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 만 범위에 있어야한다는 것입니다.


vec_pack의 정의는 어디에 있습니까?
역겨운

1
@Disgusting Hmm 죄송합니다. 내가 추가하는 것을 잊은 것 같습니다 ... 이제 수정되었습니다 :)
Stuffe

4

질문에 대한 정확한 답은 아니지만 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;
        }
    }
}


2

React-like Hooks를 사용 valueOf하여 각 반복마다 메서드 와 다른 값으로 화살표 함수를 평가할 수 있습니다 .

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

라이브러리 @ js-basics / vector 는 Vector3에 대해 동일한 아이디어를 사용합니다.


1

나는 원시 JS에서 수행하기 위해 악의적 인 해킹을 악용하는 라이브러리를 작성했습니다. 이와 같은 표현이 가능합니다.

  • 복소수 :

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

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

  • 자동 차별화 :

    하자 f(x) = x^3 - 5x:

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

    이제 몇 가지 값에 매핑합니다.

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

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

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

  • 다항식 :

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

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

특정 문제에 Vector2대해 라이브러리를 사용하여 함수 (또는 더 짧은 것)를 정의한 다음x = Vector2()(x + y);

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.