如何在 javascript 中重载 [] 运算符

发布于 2024-08-10 13:00:29 字数 180 浏览 5 评论 0原文

我似乎找不到在 javascript 中重载 [] 运算符的方法。有谁知道吗?

我在想……

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

或者我没有看到正确的事情。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

苏辞 2024-08-17 13:00:29

您可以使用 ES6 代理(在所有现代浏览器中可用)来执行此操作,

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

查看< a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noreferrer">MDN。

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.

小姐丶请自重 2024-08-17 13:00:29

此答案已过时,从 ES6 开始。请参阅平均 Joe 的答案,了解使用 ES6 新功能的答案。然而,对于缺乏 ES6 支持的浏览器来说,这个答案仍然成立。

您不能在 JavaScript 中重载运算符。

它被提议用于 ECMAScript 4 但被拒绝。

我认为你不会很快看到它。

This answer is outdated as of ES6. See average Joe's answer for an answer using new features in ES6. However, this answer is still true for browsers that lack ES6 support.

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.

酷炫老祖宗 2024-08-17 13:00:29

我们可以代理get |直接设置方法。受到这个的启发。

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]

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]
愚人国度 2024-08-17 13:00:29

简单的答案是 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

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
饮湿 2024-08-17 13:00:29

使用代理。在答案的其他地方提到过,但我认为这是一个更好的例子:

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.

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.
苏大泽ㄣ 2024-08-17 13:00:29

由于括号运算符实际上是属性访问运算符,因此您可以使用 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 元素,但不适用于其他对象。

该方法的缺点是您只能挂钩对预定义属性集的请求,而不能挂钩没有任何预定义名称的任意属性。

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.

冷心人i 2024-08-17 13:00:29

做到这一点的一种偷偷摸摸的方法是扩展语言本身。

第 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

定义一个新的 eval 实现。 (不要这样做,但这是一个概念证明)。

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

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));
べ繥欢鉨o。 2024-08-17 13:00:29

您需要按照说明使用 Proxy,但它最终可以

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

通过“this”集成到类构造函数中。然后 set 和 get(也称为 deleteProperty)函数将触发。虽然你得到一个看起来不同的 Proxy 对象,但它在很大程度上可以询问比较( target.constructor === MyClass )它的类类型等。 [即使它是一个函数,其中 target.constructor.name 是类名文本(只是注意到工作原理略有不同的示例。)]

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.)]

夜夜流光相皎洁 2024-08-17 13:00:29

所以你希望做类似的事情
var 不管怎样 = MyClassInstance[4];

如果是这样,简单的答案是 Javascript 目前不支持运算符重载。

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.

翻身的咸鱼 2024-08-17 13:00:29

看看 Symbol.iterator。您可以实现用户定义的 @@iterator 方法来使任何对象可迭代。

众所周知的Symbol.iterator符号指定对象的默认迭代器。由 for...of 使用。

例子:

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
}

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
}
烟柳画桥 2024-08-17 13:00:29

类和代理可以组合:

class Overload {
    #data = {};
    constructor () {
        return new Proxy(this, this);
    }
    get (_, p) {
        return this.#data[p];
    }
}

记住代理处理所有 prop 访问。

class and proxy can be combined:

class Overload {
    #data = {};
    constructor () {
        return new Proxy(this, this);
    }
    get (_, p) {
        return this.#data[p];
    }
}

remember that proxy handles all prop access.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文