javascript - 禁用 JS 中的属性突变

标签 javascript abstraction

我正在创建一个组件并试图破坏我的实现。这个想法是不允许用户操纵公开的属性。

实现是这样的:

function MyClass(){
  var data = [];
  Object.defineProperty(this, 'data', {
    get: function(){ return data; },
    set: function(){ throw new Error('This operation is not allowed'); },
    configurable: false,
  });
}

var obj = new MyClass();

try {
  obj.data = [];
} catch(ex) {
  console.log('mutation handled');
}

obj.data.push('Found a way to mutate');

console.log(obj.data)

如您所见,设置属性已处理,但用户仍然可以使用 .push 对其进行更改。这是因为我要返回一个引用。

我处理过这个案例:

function MyClass(){
  var data = [];
  Object.defineProperty(this, 'data', {
    get: function(){ return data.slice(); },
    set: function(){ throw new Error('This operation is not allowed'); },
    configurable: false,
  });
}

var obj = new MyClass();

try {
  obj.data = [];
} catch(ex) {
  console.log('mutation handled');
}

obj.data.push('Found a way to mutate');

console.log(obj.data)

如您所见,我返回一个新数组来解决这个问题。不确定它将如何影响性能。

问题:是否有其他方法不允许用户改变对象类型的属性?

我尝试过使用writable: false,但是当我将它与get一起使用时,它给了我错误。


注意:我希望该数组在类内可变,但不能从外部可变。

最佳答案

这里的问题是您有效地阻止了修改MyClass的尝试。但是,MyClass 的其他对象成员仍然是 JavaScript 对象。你这样做的方式(每次调用 get 返回一个新的数组)是最好的方法之一,当然,这取决于你调用 get 的频率或者数组的长度可能会带来性能缺陷。

当然,如果您可以使用 ES6,您可以扩展原生 Array 来创建一个 ReadOnlyArray 类。实际上,您也可以在 ES5 中执行此操作,但您无法使用方括号从数组中的特定索引检索值。

如果您可以避免使用 Internet Explorer,另一个选择是使用代理 ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy )。

使用代理,您可以捕获调用以获取对象的属性,并决定返回什么或执行什么操作。

在下面的示例中,我们为数组创建一个代理。正如您在处理程序中看到的,我们定义了一个 get 函数。每当访问目标对象的属性值时都会调用此函数。这包括访问索引或方法,因为调用方法基本上就是检索属性(函数)的值,然后调用它。

如您所见,如果属性是整数,我们将返回数组中的该位置。如果属性是“length”,那么我们返回数组的长度。在任何其他情况下,我们都会返回一个 void 函数。

这样做的优点是 proxyArray 的行为仍然像一个数组。您可以使用方括号来获取其索引并使用属性长度。但是,如果您尝试执行诸如 proxyArray.push(23) 之类的操作,则什么也不会发生。

当然,在最终的解决方案中,您可能希望根据哪些内容来决定要做什么 方法正在被调用。您可能希望 mapfilter 等方法起作用。

最后,这种方法的最后一个优点是您保留对原始数组的引用,因此您仍然可以修改它并且可以通过代理访问它的值。

var handler = {
    get: function(target, property, receiver) {
      var regexp = /[\d]+/;
      if (regexp.exec(property)) { // indexes:
        return target[property];
      }
      
      if (property === 'length') {
        return target.length;
      }
      
      if (typeof (target[property]) === 'function') {
        // return a function that does nothing:
        return function() {};
      }
    }
};

// this is the original array that we keep private
var array = [1, 2, 3];

// this is the 'visible' array:
var proxyArray = new Proxy(array, handler);

console.log(proxyArray[1]);
console.log(proxyArray.length);


console.log(proxyArray.push(32)); // does nothing
console.log(proxyArray[3]); // undefined


// but if we modify the old array:
array.push(23);
console.log(array);

// the proxy is modified
console.log(proxyArray[3]); // 32

当然,问题在于 proxyArray 并不是真正的数组,因此,根据您计划如何使用它,这可能是一个问题。

关于javascript - 禁用 JS 中的属性突变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49427270/

相关文章:

javascript - react-rte 给出 TypeError : r. getEditorState is not a function in next js

spring - 是否有提供者不可知的方式来获取 Spring 框架中的最新缓存统计信息?

java - 没有接口(interface)的java中的完全抽象

javascript - 如何在 JavaScript 中继承私有(private)成员?

javascript - 使用 Angular 循环遍历数组中的数组

c - 为其他类型重新利用相同的 c ADT

c++ - 无法将父类(super class)转换为子类

android - 如何将抽象与 ViewBinding 与基本 Activity 一起使用?

javascript - 如何使用 jQuery 触发同一容器内的元素

javascript - 使用 Node 运行 express 时出错