자바 스크립트에서 [] 연산자를 어떻게 오버로드 하시겠습니까?


85

자바 스크립트에서 [] 연산자를 오버로드하는 방법을 찾을 수없는 것 같습니다. 아는 사람 있어요?

나는 생각하고 있었다 ...

MyClass.operator.lookup(index)
{
     return myArray[index];
}

아니면 내가 옳은 일을보고 있지 않습니까?


3
여기에 대한 대답은 잘못되었습니다. JS의 배열은 키가 uint32 (-1) 값으로 강제 변환되고 프로토 타입에 추가 메서드가있는 객체 일뿐입니다
Benjamin Gruenbaum

MyClass객체를 배열로 만드십시오 . 당신은에서 키와 값을 복사 할 수 있습니다 myArray당신에 var myObj = new MyClass()객체입니다.
jpaugh

이봐 요, {} 연산자를 오버로드하고 싶습니다.
daniel assayag

답변:


81

JavaScript에서는 연산자를 오버로드 할 수 없습니다.

ECMAScript 4 용 으로 제안 되었지만 거부되었습니다.

조만간 볼 수 없을 것 같습니다.


4
이것은 이미 일부 브라우저의 프록시로 가능할 수 있으며 언젠가는 모든 브라우저에 적용될 것입니다. 참조 github.com/DavidBruant/ProxyArray
Tristan

그렇다면 jQuery는 [] 또는 .eq () 사용 여부에 따라 어떻게 다른 것을 반환합니까? stackoverflow.com/a/6993901/829305
Rikki

2
이제 프록시로 할 수 있습니다.
Eyal

"."가 아닌 배열로 액세스하는 한 기호가있는 메소드를 정의 할 수 있지만. 이것이 SmallTalk Object arg1: a arg2: b arg3: cObject["arg1:arg2:arg3:"](a,b,c). 당신은 할 수 있습니다 myObject["[]"](1024): P
드미트리

링크가 죽었습니다 :(
Gp2mv3

69

ES6 프록시 ( 모든 최신 브라우저 에서 사용 가능)로이 작업을 수행 할 수 있습니다.

var handler = {
    get: function(target, name) {
        return "Hello, " + name;
    }
};
var proxy = new Proxy({}, handler);

console.log(proxy.world); // output: Hello, world

MDN에 대한 자세한 내용을 확인하십시오 .


2
이를 사용하여 인덱스 접근자가있는 자체 클래스를 만드는 방법은 무엇입니까? 즉, 내 자신의 생성자를 사용하고 싶습니다. 프록시를 생성하고 싶지 않습니다.
mpen

1
이것은 진정한 과부하가 아닙니다. 개체 자체에서 메서드를 호출하는 대신 이제 프록시의 메서드를 호출합니다.
Pacerier 2011

@Pacerier target[name]는 getter에서 반환 할 수 있습니다 . OP는 예제를 보여줍니다
Valen

1
[]운영자와도 함께 작동합니다. btw :var key = 'world'; console.log(proxy[key]);
Klesun

15

간단한 대답은 JavaScript가 대괄호를 통해 객체의 자식에 액세스 할 수 있도록 허용한다는 것입니다.

따라서 클래스를 정의 할 수 있습니다.

MyClass = function(){
    // Set some defaults that belong to the class via dot syntax or array syntax.
    this.some_property = 'my value is a string';
    this['another_property'] = 'i am also a string';
    this[0] = 1;
};

그러면 두 구문 중 하나를 사용하여 클래스의 모든 인스턴스에서 멤버에 액세스 할 수 있습니다.

foo = new MyClass();
foo.some_property;  // Returns 'my value is a string'
foo['some_property'];  // Returns 'my value is a string'
foo.another_property;  // Returns  'i am also a string'
foo['another_property'];  // Also returns 'i am also a string'
foo.0;  // Syntax Error
foo[0];  // Returns 1
foo['0'];  // Returns 1

2
성능상의 이유로 이것을 권장하지는 않지만 여기서 문제에 대한 유일한 실제 해결책입니다. 가능하지 않다는 편집은 이것이 훌륭한 대답이 될 것입니다.
Milimetric

4
이것은 질문이 원하는 것이 아닙니다 . 문제는 foo['random']코드가 할 수없는 것을 잡는 방법을 요구 하는 것입니다.
Pacerier

9

프록시를 사용하십시오. 답변의 다른 곳에서 언급되었지만 이것이 더 나은 예라고 생각합니다.

var handler = {
    get: function(target, name) {
        if (name in target) {
            return target[name];
        }
        if (name == 'length') {
            return Infinity;
        }
        return name * name;
    }
};
var p = new Proxy({}, handler);

p[4]; //returns 16, which is the square of 4.

프록시 따라서 ES6 기능입니다 것을 아마 언급 할만큼 가치는 더 제한이 브라우저 지원을 (그리고 바벨 중 하나를 가짜들을 수 없습니다 ).
jdehesa

8

대괄호 연산자는 실제로 속성 액세스 연산자이므로 getter 및 setter로 연결할 수 있습니다. IE의 경우 대신 Object.defineProperty ()를 사용해야합니다. 예:

var obj = {
    get attr() { alert("Getter called!"); return 1; },
    set attr(value) { alert("Setter called!"); return value; }
};

obj.attr = 123;

IE8 +에서도 동일 :

Object.defineProperty("attr", {
    get: function() { alert("Getter called!"); return 1; },
    set: function(value) { alert("Setter called!"); return value; }
});

IE5-7의 onpropertychange경우 이벤트 만 있으며 DOM 요소에는 작동하지만 다른 개체에는 작동하지 않습니다.

이 메서드의 단점은 미리 정의 된 이름이없는 임의의 속성이 아니라 미리 정의 된 속성 집합에 대한 요청 만 연결할 수 있다는 것입니다.


jsfiddle.net에서 접근 방식을 시연 해 주 시겠습니까? 솔루션이 식의 모든 키에 대해 작동한다고 가정 obj['any_key'] = 123;하지만 코드에서 보는 것은 (아직 알려지지 않은) 키에 대해 setter / getter를 정의해야합니다. 그것은 불가능합니다.
dma_k 2013

3
더하기 1은 IE 전용이 아니기 때문에 빼기 1을 오프셋합니다.
orb

이것은 클래스 함수에 대해 수행 될 수 있습니까? 나는 그것에 대한 구문을 스스로 찾기 위해 고군분투하고 있습니다.
Michael Hoffmann

7

설명대로 Proxy를 사용해야하지만 궁극적으로 클래스 생성자에 통합 될 수 있습니다.

return new Proxy(this, {
    set: function( target, name, value ) {
...}};

'this'와 함께. 그러면 set 및 get (또한 deleteProperty) 함수가 실행됩니다. 비록 당신이 다른 것처럼 보이는 Proxy 객체를 얻더라도 그것은 대부분의 경우 비교 (target.constructor === MyClass)를 요청하기 위해 작동합니다. 그것은 클래스 유형 등입니다. 텍스트 (약간 다르게 작동하는 예를 언급합니다.)]


1
이는 Backbone 컬렉션의 [] 오버로드에 적합하므로 다른 모든 속성에 대한 통과와 함께 []를 사용하여 개별 모델 개체가 반환되도록합니다.
justkt

5

그래서 당신은 var whatever = MyClassInstance [4]; ? 그렇다면 간단한 대답은 Javascript가 현재 연산자 오버로딩을 지원하지 않는다는 것입니다.


1
그래서 jQuery는 어떻게 작동합니까? $ ( '. foo'). html ()과 같은 jQuery 객체에서 메서드를 호출하거나 $ ( '. foo') [0]와 같은 첫 번째 일치하는 dom 요소를 가져올 수 있습니다.
kagronick

1
jQuery는 함수이며 $ 함수에 매개 변수를 전달합니다. 따라서, () 괄호하지 []
제임스 웨스트 게이트

5

이를 수행하는 한 가지 교활한 방법은 언어 자체를 확장하는 것입니다.

1 단계

사용자 지정 인덱싱 규칙을 정의하고 "[]"라고 부르겠습니다.

var MyClass = function MyClass(n) {
    this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
    value: function(index) {
        return this.myArray[index];
    }
});

...

var foo = new MyClass(1024);
console.log(foo["[]"](0));

2 단계

새로운 평가 구현을 정의합니다. (이렇게하지 마십시오. 그러나 이것은 개념 증명입니다).

var MyClass = function MyClass(length, defaultValue) {
    this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
    value: function(index) {
        return this.myArray[index];
    }
});

var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));

var mini_eval = function(program) {
    var esprima = require("esprima");
    var tokens = esprima.tokenize(program);
    
    if (tokens.length == 4) {    
        var types = tokens.map(a => a.type);
        var values = tokens.map(a => a.value);
        if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
            if (values[1] == '[' && values[3] == ']') {
                var target = eval(values[0]);
                var i = eval(values[2]);
                // higher priority than []                
                if (target.hasOwnProperty('[]')) {
                    return target['[]'](i);
                } else {
                    return target[i];
                }
                return eval(values[0])();
            } else {
                return undefined;
            }
        } else {
            return undefined;
        }
    } else {
        return undefined;
    }    
};

mini_eval("foo[33]");

위의 내용은 더 복잡한 인덱스에서는 작동하지 않지만 더 강력한 구문 분석을 사용할 수 있습니다.

대안 :

고유 한 상위 집합 언어를 만드는 대신 기존 언어로 표기법을 컴파일 한 다음 평가할 수 있습니다. 이렇게하면 처음 사용한 후 구문 분석 오버 헤드가 네이티브로 줄어 듭니다.

var compile = function(program) {
    var esprima = require("esprima");
    var tokens = esprima.tokenize(program);
    
    if (tokens.length == 4) {    
        var types = tokens.map(a => a.type);
        var values = tokens.map(a => a.value);
        if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
            if (values[1] == '[' && values[3] == ']') {
                var target = values[0];
                var i = values[2];
                // higher priority than []                
                return `
                    (${target}['[]']) 
                        ? ${target}['[]'](${i}) 
                        : ${target}[${i}]`
            } else {
                return 'undefined';
            }
        } else {
            return 'undefined';
        }
    } else {
        return 'undefined';
    }    
};

var result = compile("foo[0]");
console.log(result);
console.log(eval(result));

1
비뚤어진 상태입니다 +1
Jus

정말 역겨워. 나는 그것을 좋아한다.
SliceThePi

영리함은 일반적으로 어떤 식 으로든 있습니다. 충분한 자원으로 갚을 수있는 가치있는 운동이 아니라는 의미는 아닙니다. 가장 게으른 사람들은 사용할 수없는 경우에도 더 친숙한 환경에서 작업 할 수 있도록 자체 컴파일러와 번역기를 작성하는 사람들입니다. 즉, 해당 지역에서 더 많은 경험을 가진 누군가가 서두르지 않고 작성하면 덜 역 겨울 것입니다. 사소하지 않은 모든 솔루션은 어떤 식 으로든 역겨운 일이며 우리의 일은 장단점을 알고 결과에 대처하는 것입니다.
Dmitry

3

우리는 얻을 수 있습니다 | 방법을 직접 설정하십시오 . 영감을받은 .

class Foo {
    constructor(v) {
        this.data = v
        return new Proxy(this, {
            get: (obj, key) => {
                if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
                    return obj.data[key]
                else 
                    return obj[key]
            },
            set: (obj, key, value) => {
                if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
                    return obj.data[key] = value
                else 
                    return obj[key] = value
            }
        })
    }
}

var foo = new Foo([])

foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.