English 中文(简体)
How would you overload the [] operator in javascript
原标题:

I can t seem to find the way to overload the [] operator in javascript. Anyone out there know?

I was thinking on the lines of ...

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

or am I not looking at the right things.

最佳回答

You can t overload operators in JavaScript.

It was proposed for ECMAScript 4 but rejected.

I don t think you ll see it anytime soon.

问题回答

You can do this with ES6 Proxy (available in all modern browsers)

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

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

Check details on MDN.

The simple answer is that JavaScript allows access to children of an Object via the square brackets.

So you could define your class:

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

You will then be able to access the members on any instances of your class with either syntax.

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

Use a proxy. It was mentioned elsewhere in the answers but I think that this is a better example:

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.

We can proxy get | set methods directly. Inspired by this.

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]

As brackets operator is actually property access operator, you can hook on it with getters and setters. For IE you will have to use Object.defineProperty() instead. Example:

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

obj.attr = 123;

The same for IE8+:

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

For IE5-7 there s onpropertychange event only, which works for DOM elements, but not for other objects.

The drawback of the method is you can only hook on requests to predefined set of properties, not on arbitrary property without any predefined name.

one sneaky way to do this is by extending the language itself.

step 1

define a custom indexing convention, let s call it, "[]".

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

step 2

define a new eval implementation. (don t do this this way, but it s a proof of concept).

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

the above won t work for more complex indexes but it can be with stronger parsing.

alternative:

instead of resorting to creating your own superset language, you can instead compile your notation to the existing language, then eval it. This reduces the parsing overhead to native after the first time you use it.

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

You need to use Proxy as explained, but it can ultimately be integrated into a class constructor

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

with this . Then the set and get (also deleteProperty) functions will fire. Although you get a Proxy object which seems different it for the most part works to ask the compare ( target.constructor === MyClass ) it s class type etc. [even though it s a function where target.constructor.name is the class name in text (just noting an example of things that work slightly different.)]

So you re hoping to do something like var whatever = MyClassInstance[4]; ? If so, simple answer is that Javascript does not currently support operator overloading.

Have a look at Symbol.iterator. You can implement a user-defined @@iterator method to make any object iterable.

The well-known Symbol.iterator symbol specifies the default iterator for an object. Used by for...of.

Example:

class MyClass {

  constructor () {
    this._array = [data]
  }

  *[Symbol.iterator] () {
    for (let i=0, n=this._array.length; i<n; i++) {
      yield this._array[i]
    }
  }
}

const c = new MyClass()

for (const element of [...c]) {
  // do something with element
}




相关问题
selected text in iframe

How to get a selected text inside a iframe. I my page i m having a iframe which is editable true. So how can i get the selected text in that iframe.

How to fire event handlers on the link using javascript

I would like to click a link in my page using javascript. I would like to Fire event handlers on the link without navigating. How can this be done? This has to work both in firefox and Internet ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Clipboard access using Javascript - sans Flash?

Is there a reliable way to access the client machine s clipboard using Javascript? I continue to run into permissions issues when attempting to do this. How does Google Docs do this? Do they use ...

javascript debugging question

I have a large javascript which I didn t write but I need to use it and I m slowely going trough it trying to figure out what does it do and how, I m using alert to print out what it does but now I ...

Parsing date like twitter

I ve made a little forum and I want parse the date on newest posts like twitter, you know "posted 40 minutes ago ","posted 1 hour ago"... What s the best way ? Thanx.

热门标签