【问题标题】:How can I use `defineProperty` to create a readonly array property?如何使用 `defineProperty` 创建只读数组属性?
【发布时间】:2014-04-26 06:53:48
【问题描述】:

我将以下内容作为模块的一部分(为提问而简化名称):

在“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);
}

在“otherfile.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.seal()
  • Object.seal() on props.arr 可以工作,但我认为当我在“x.addArrValue”fn 中对其进行变异时,我无法“解封”它。

标签: javascript arrays properties native-methods


【解决方案1】:

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

【讨论】:

  • 缓存的想法很不错;在这种情况下没有想到它?? 更喜欢使用切片每次访问,因为某些框架可以多次访问属性,例如在 Angular JS 的摘要循环中。
【解决方案2】:

回顾这个,2 年后,一个可能的解决方案是通过属性访问器返回数组的副本。是否是最佳方法取决于各种因素(例如预期的数组大小等)

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

这意味着在数组上调用.push 不会影响原始_arr 数组,而addArrValue 方法将是改变“私有”_arr 变量的唯一方法。

var x = new ClassName();

x.arr.push("newValue"); // Silently fails as it mutates a copy of _arr

console.log(x.arr); // []

x.addArrValue("hi");

console.log(x.arr); // ["hi"];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多