【问题标题】:Rejecting array inputs with JS Proxy使用 JS 代理拒绝数组输入
【发布时间】:2016-12-17 13:39:03
【问题描述】:

我想创建一个只接受要存储的特定实例类型的数组。似乎最好的解决方案是使用Proxies,灵感来自gistSO thread

所以我有一个代理工作,对于基本的数组功能,它按预期工作。 set 属性确保只有作为 Fruit 实例的对象才能插入到数组中,否则会抛出 TypeError。现在唯一可以设置的其他属性是length

问题在于高级 i/o,例如 splice()。记录set 函数表明,数组项被移动以为要在 [0] 处插入的新项腾出空间,但是当新项被拒绝时,它会使数组变得一团糟。

由于set 被迭代调用,我没有看到一种明确的方法来阻止拼接启动,或者在代理中将数组恢复到以前的荣耀(最好是以前的选项)。还有其他人知道如何实现这些想法,或者有其他建议吗?

"use strict";

class Fruit {
  constructor(name) {
    this._name = name;
  }

  set name(name) {
    this._name = name;
  }

  get name() {
    return this._name;
  }
}

class Vegetable {
  constructor(name) {
    this.name(name);
  }

  set name(name) {
    this._name = name;
  }

  get name() {
    return this._name;
  }
}

// a proxy for our array
var fruitbowl = new Proxy([], {
  apply: function(target, thisArg, argumentsList) {
    return thisArg[target].apply(this, argumentList);
  },
  deleteProperty: function(target, property) {
    console.log("Deleted %s", property);
    return true;
  },
  set: function(target, property, value, receiver) {
    // UNCOMMENT HERE for useful output:
    // console.log("Setting " + property + " to ", value);
    if (property == "length") {
      target.length = value;
      return true;
    } else {
      if (value instanceof Fruit) {
        target[property] = value;
        return true;
      } else {
        return false;
        // throw TypeError("Expected Fruit, got " + typeof(value) + " (" + value + ")");
      }
    }
  }
});

console.log("\n\n=== Putting fruit into the bowl... ===\n\n");

try {
  fruitbowl.push(new Vegetable("potato"));
} catch (e) {
  console.log("Shoudln't allow vegetables: PASSED");
}

fruitbowl.push(new Fruit("apple"));
console.log("Should allow fruit: " + (fruitbowl.length == 1 ? "PASSED" : "FAILED"));

fruitbowl[0] = new Fruit("orange");
console.log("Should replace item specified as long as it's a Fruit: " + (fruitbowl.length == 1 && fruitbowl[0].name == "orange" ? "PASSED" : "FAILED"));

try {
  fruitbowl[0] = "Bananas!!1one";
} catch (e) {

}
console.log("Should not replace with a string: " + (fruitbowl.length == 1 && fruitbowl[0].name == "orange" ? "PASSED" : "FAILED"));

fruitbowl.push(new Fruit("banana"), new Fruit("pear"));
console.log("Should have 3 items [orange, banana, pear]: " + (fruitbowl.length == 3 ? "PASSED" : "FAILED"), fruitbowl);

console.log("\n\n === Cropping the bowl... ===\n\n");

fruitbowl.length = 2;
console.log("Should have 2 items [orange,banana]: " + (fruitbowl.length == 2 ? "PASSED" : "FAILED"));
console.log("Should error at item 2: " + (!fruitbowl[2] ? "PASSED" : "FAILED"), fruitbowl);

console.log("\n\n === Splicing the bowl... ===\n\n");

console.log(fruitbowl.length);

try {
  console.log(fruitbowl.length);
  fruitbowl.splice(0, 0, "pineapples!!1one");
  console.log(fruitbowl.length);
} catch (e) {
  console.log("Shouldn't have inserted string: PASSED");
}
console.log("Should still only have 2 fruit: " + (fruitbowl.length == 2 ? "PASSED" : "FAILED (" + fruitbowl.length + ")"));
console.log(fruitbowl);

【问题讨论】:

  • 数组没有 [[Call]] 方法。你的apply 陷阱没用。
  • 我认为您最好扩展 Array 原型,重新定义新原型上可能会改变数组的所有方法。
  • @trincot 这是我的第一个想法,效果非常好,直到它重载了 [] 速记。
  • 我认为您不想更改[] 的行为,因为您的代码(或第三方代码)在想要使用其他普通数组时可能会遇到意外结果。您的新原型可以定义自己的 '.from()` 方法,MyArray.from([]) 将返回原型的空数组。

标签: javascript arrays ecmascript-6 es6-proxy


【解决方案1】:

据我所知,实现这一点的唯一方法是覆盖splice() 函数。您必须检查所有项目是否都是Fruit 对象,如果不是,则抛出错误。如果都是Fruit对象,则应该调用原函数。

Reflect.defineProperty(fruitbowl, 'splice', {
  configurable: true,
  enumerable: false,
  value: function(start, deleteCount, ...items) {
    if (items.every(item => item instanceof Fruit)) {
      return Reflect.apply(Array.prototype.splice, this, [start, deleteCount, ...items]);
    } else {
      throw new Error('All elements must be Fruit objects');
    }
  }
});

【讨论】:

  • 如果有人[].splice.call(fruitbowl, 0, 1)怎么办?
  • @trincot 然后它不会抛出错误。我认为对此没有什么好的解决方案,因为无法确定调用了哪个方法。
  • 我确实考虑过这个选项,但后来开始想知道我还需要重写多少其他方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多