javascript - 如何使用 `defineProperty` 创建只读数组属性?

标签 javascript arrays properties native-methods

我将以下内容作为模块的一部分(出于问题的目的简化了名称):

在“module.js”中:

var _arr;

_arr = [];

function ClassName () {
    var props = {};

    // ... other properties ...

    props.arr = {
        enumerable: true,
        get: function () {
            return _arr;
        }
    };

    Object.defineProperties(this, props); 

    Object.seal(this);
};

ClassName.prototype.addArrValue = function addArrValue(value) {

    // ... some code here to validate `value` ...

    _arr.push(value);
}

在“其他文件.js”中:

var x = new ClassName();

通过上面的实现和下面的示例代码,可以通过两种方式向 arr 添加值。

// No thank you.
x.arr.push("newValue"); // x.arr = ["newValue"];

// Yes please!
x.addArrValue("newValue"); // Only this route is desired.

有谁知道如何实现只读数组属性?

注意:writeable 默认为 false,如果我明确设置它,则不会观察到任何差异。

最佳答案

Object.freeze()将按照您的要求进行操作(在正确实现规范的浏览器上)。在严格模式下尝试修改数组将无声地失败或抛出 TypeError

最简单的解决方案是返回一个新的卡住副本(卡住是破坏性的):

return Object.freeze(_arr.slice());

但是,如果预期读取多于写入,则延迟缓存最近访问的卡住副本并在写入时清除(因为 addArrValue 控制写入)

使用修改后的原始示例延迟缓存只读副本:

"use strict";
const mutable = [];
let cache;

function ClassName () {
    const props = {};

    // ... other properties ...

    props.arr = {
        enumerable: true,
        get: function () {
            return cache || (cache = Object.freeze(mutable.slice());
        }
    };

    Object.defineProperties(this, props); 

    Object.seal(this);
};

ClassName.prototype.addArrValue = function addArrValue(value) {

    // ... some code here to validate `value` ...

    mutable.push(value);
    cache = undefined;
}

使用 ES2015 类的延迟缓存只读副本:

class ClassName {
    constructor() {
        this.mutable = [];
        this.cache = undefined;
        Object.seal(this);
    }

    get arr() {
        return this.cache || (this.cache = Object.freeze(this.mutable.slice());
    }

    function addArrValue(value) {
        this.mutable.push(value);
        this.cache = undefined;
    }
}

“透明”可重用类 hack(很少需要):

class ReadOnlyArray extends Array {
    constructor(mutable) {
        // `this` is now a frozen mutable.slice() and NOT a ReadOnlyArray
        return Object.freeze(mutable.slice()); 
    }
}

const array1 = ['a', 'b', 'c'];
const array2 = new ReadOnlyArray(array1);

console.log(array1); // Array ["a", "b", "c"]
console.log(array2); // Array ["a", "b", "c"]
array1.push("d");
console.log(array1); // Array ["a", "b", "c", "d"]
console.log(array2); // Array ["a", "b", "c"]
//array2.push("e"); // throws

console.log(array2.constructor.name); // "Array"
console.log(Array.isArray(array2));   // true
console.log(array2 instanceof Array); // true
console.log(array2 instanceof ReadOnlyArray); // false

适当的可重用类:

class ReadOnlyArray extends Array {
    constructor(mutable) {
        super(0);
        this.push(...mutable);
        Object.freeze(this);
    }
    static get [Symbol.species]() { return Array; }
}

const array1 = ['a', 'b', 'c'];
const array2 = new ReadOnlyArray(array1);

console.log(array1); // Array ["a", "b", "c"]
console.log(array2); // Array ["a", "b", "c"]
array1.push("d");
console.log(array1); // Array ["a", "b", "c", "d"]
console.log(array2); // Array ["a", "b", "c"]
//array2.push("e"); // throws

console.log(array2.constructor.name); // "ReadOnlyArray"
console.log(Array.isArray(array2));   // true
console.log(array2 instanceof Array); // true
console.log(array2 instanceof ReadOnlyArray); // true

关于javascript - 如何使用 `defineProperty` 创建只读数组属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22515847/

相关文章:

javascript - 从值为零的对象中删除属性

javascript - 按值对对象属性进行排序

java - java中的过滤属性

c# - 内部类和属性同名

javascript - 在 React Redux 中使用嵌套角色和权限

javascript - 在父级宽度的 70% 之后将 Span 中的内容替换为 "..."

html - 如何用 HTML 和 JavaScript/JQuery 替换数据库以创建动态相关的下拉选择列表?

javascript - 我如何找到服务 worker 响应属于哪个缓存?

javascript - 如何在不更改原始参数数组的情况下操作传递给函数的 JavaScript 数组?

arrays - Typescript array.from(iterator) 类型不正确,无法转换?