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

问题描述 投票:0回答:11

我似乎找不到在javascript中重载

[]
运算符的方法。有谁知道吗?

我在想……

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

还是我看的东西不对。

javascript operator-overloading
11个回答
106
投票

您可以使用 ES6 Proxy 来完成此操作(在所有现代浏览器中可用)

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

查看MDN详情。


84
投票

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

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

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


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

14
投票

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

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.

14
投票

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

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]

9
投票

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

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


7
投票

实现此目的的一个偷偷摸摸的方法是扩展语言本身。

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

7
投票

你需要按照解释使用Proxy,但它最终可以集成到一个类中构造函数

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

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


4
投票

所以你希望做类似的事情 var 不管怎样 = MyClassInstance[4]; ? 如果是这样,简单的答案是 Javascript 目前不支持运算符重载。


1
投票

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

众所周知的 Symbol.iterator 符号指定对象的默认迭代器。用于…的。

示例:

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
}

0
投票

类和代理可以组合:

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

请记住,代理处理所有道具访问。

© www.soinside.com 2019 - 2024. All rights reserved.