使用 JS 代理拒绝数组输入
Rejecting array inputs with JS Proxy
我想创建一个只接受特定实例类型存储的数组。似乎最好的解决方案是使用 Proxies, inspired by this gist and SO thread.
所以我有一个代理在工作,对于基本的数组功能,它按预期工作。 set
属性 确保只有作为 Fruit 实例的对象才能插入到数组中,否则会抛出 TypeError。现在唯一可以设置的 属性 是 length
.
问题出在高级 i/o,例如 splice()
。记录 set
函数显示数组项被移动到 space 以便在 [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);
据我所知,实现此目的的唯一方法是覆盖 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');
}
}
});
我想创建一个只接受特定实例类型存储的数组。似乎最好的解决方案是使用 Proxies, inspired by this gist and SO thread.
所以我有一个代理在工作,对于基本的数组功能,它按预期工作。 set
属性 确保只有作为 Fruit 实例的对象才能插入到数组中,否则会抛出 TypeError。现在唯一可以设置的 属性 是 length
.
问题出在高级 i/o,例如 splice()
。记录 set
函数显示数组项被移动到 space 以便在 [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);
据我所知,实现此目的的唯一方法是覆盖 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');
}
}
});