javascript - 是否可以在 JavaScript 或 TypeScript 的自定义类中实现索引器?

标签 javascript typescript

我可以像在 C# 中一样在自定义类上使用方括号吗? 或者 JavaScript 中的方括号仅限于数组?

C# 中的等效内容如下:

    public class Contact
    {
        private string[] address = new string[3];
        public string this[int index]
        {
            get
            {
                return address[index];
            }
            set
            {
                address[index] = value;
            }
        }
    }

最佳答案

JavaScript 和 TypeScript 都没有您正在寻找的特定功能,但 JavaScript 确实有 Proxy对象,它们非常强大并且可以用于此目的。您需要实现 getset陷阱处理程序(至少),并让 Contact 构造函数返回代理而不是实例本身。 (如果从构造函数返回一个对象,则会覆盖返回新创建的实例的默认行为。)在代理上“读取”的每个属性上都会调用 get 处理程序,并且 >set 处理程序在代理上的每个属性“write”上被调用:

// ==== The implementation
const rexAllDigits = /^\d+$/;
const contactProxyHandlers = {
    get(target, key) {
        if (rexAllDigits.test(key)) {
            return target.address[key];
        }
        return target[key];
    },
    set(target, key, value) {
        if (rexAllDigits.test(key)) {
            return Reflect.set(target.address, key, value);
        }
        return Reflect.set(target, key, value);
    }
};
class Contact {
    constructor() {
        this.address = ["", "", ""];
        return new Proxy(this, contactProxyHandlers);
    }
}

// ==== Usage:
const c = new Contact();
c[0] = "address 0";
console.log(c[0], c.address[0]); // "address 0", "address 0"
c.example = "not an index property";
console.log(c.example);

在该示例中,我将使用全数字属性名称的所有属性获取和设置重定向到 Contact 对象上的 address 数组,但传递请求对于其他属性,直接传递给对象。

您的 C# 版本将地址设为私有(private)。很快您就可以使用 private fields在 JavaScript 中也可以做到这一点。在那之前,如果您想将其设为私有(private),可以使用 WeakMap 来实现,如下所示:

// ==== The implementation -- private parts
// You'd keep these parts private, for instance in a module and not exported

const contactAddresses = new WeakMap();
const proxyTargets = new WeakMap();
const rexAllDigits = /^\d+$/;
const contactProxyHandlers = {
    get(target, key) {
        if (rexAllDigits.test(key)) {
            return contactAddresses.get(target)[key];
        }
        return target[key];
    },
    set(target, key, value) {
        if (rexAllDigits.test(key)) {
            return Reflect.set(contactAddresses.get(target), key, value);
        }
        return Reflect.set(target, key, value);
    }
};

// ==== The implementation -- public parts
// You'd make this available, for instance exporting it from the module

class Contact {
    constructor() {
        // Initialize the entry in the map. If/when this object is ready
        // for garbage collection, the entry will get removed automatically.
        contactAddresses.set(this, ["", "", ""]);
        const p = new Proxy(this, contactProxyHandlers);
        proxyTargets.set(p, this);
        return p;
    }
    showAddresses() {
        const t = proxyTargets.get(this);
        if (!t) {
            throw new Error("Invalid `this` value for Contact");
        }
        console.log(JSON.stringify(contactAddresses.get(t)));
    }
}

// ==== Usage:
const c = new Contact();
c[0] = "address 0";
console.log(c[0]); // "address 0"
c.showAddresses(); // "address 0", "", ""
c.example = "not an index property";
console.log(c.example);

使用私有(private)字段的版本还需要 proxyTargets WeakMap。它看起来像这样:

// ==== The implementation
class Contact {
    static #rexAllDigits = /^\d+$/;
    static #contactProxyHandlers = {
        get(target, key) {
            if (Contact.#rexAllDigits.test(key)) {
                return target.#address[key];
            }
            return target[key];
        },
        set(target, key, value) {
            if (Contact.#rexAllDigits.test(key)) {
                return Reflect.set(target.#address, key, value);
            }
            return Reflect.set(target, key, value);
        }
    };
    static #proxyTargets = new WeakMap();
    #address = ["", "", ""];
    constructor() {
        const p = new Proxy(this, Contact.#contactProxyHandlers);
        Contact.#proxyTargets.set(p, this);
        return p;
    }
    showAddresses() {
        const t = Contact.#proxyTargets.get(this);
        if (!t) {
            throw new Error("Invalid `this` value for Contact");
        }
        console.log(JSON.stringify(t.#address));
    }
}

// ==== Usage:
const c = new Contact();
c[0] = "address 0";
console.log(c[0]); // "address 0"
c.showAddresses(); // "address 0", "", ""
c.example = "not an index property";
console.log(c.example);

关于javascript - 是否可以在 JavaScript 或 TypeScript 的自定义类中实现索引器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63613518/

相关文章:

javascript - TypeScript:在构造函数中使用 private 或 public

typescript 默认导出未定义

javascript - "even last"数组 JavaScript 练习

javascript - ASP.NET MVC Razor <text> 标签不接受小于或等于运算符

javascript - 在 node.js 中,如何使用 child_process.exec 以便所有操作都可以异步发生?

javascript - Json 在循环中替换值

typescript - 样式组件 defaultProps

javascript - fast-csv 抛出异常 : column header mismatch expected

node.js - 两个日期之间的天数 - typescript

javascript - 通过 JavaScript 进行 Android 模拟器日志记录