```javascript// 定义一个处理器对象handler,包含get属性的处理方法
const handler = {
get: function (obj, prop) {
// 如果obj中存在prop属性,则返回该属性的值;否则返回37
return obj.hasOwnProperty(prop) ? obj[prop] : 37;
},
};
// 创建一个新的代理对象p,将target对象作为其目标对象,handler作为其处理器
const p = new Proxy(target, handler);
// 为p对象添加属性a和属性b,分别赋值为1和undefined
p.a = 1;
p.b = undefined;
// 输出p对象的属性a和属性b的值,分别为1和undefined
console.log(p.a, p.b);
// 检查字符串"c"是否在p对象中,以及p对象是否有属性c
console.log("c" in p, p.c); // false, 37
// 定义一个空对象target,并使用空处理器创建一个新的代理对象p
let target = {};
let p = new Proxy(target, {});
p.a = 37; // 将操作转发到目标对象target
console.log(target.a); // 输出37,说明操作已经被正确地转发到目标对象上。
```
下面是重构后的代码:
```javascript
class AgeValidator {
setAge(value) {
if (!Number.isInteger(value)) {
throw new TypeError("年龄不是整数");
}
if (value > 200) {
throw new RangeError("年龄似乎无效");
}
}
}
const ageProxy = new AgeValidator();
ageProxy.setAge = Object.defineProperties(ageProxy, {
age: {
get() {
return this._age;
},
set: ageProxy.setAge,
},
});
ageProxy.age = 100;
console.log(ageProxy.age); // 100
ageProxy.age = "young"; // Throws: Uncaught TypeError: The age is not an integer
ageProxy.age = 300; // Throws: Uncaught RangeError: The age seems invalid
```
代码重构
```javascriptfunction extend(sup, base) {
var descriptor = Object.getOwnPropertyDescriptor(base.prototype, "constructor"); // 获取构造函数的描述符
base.prototype = Object.create(sup.prototype); // 将基类原型设置为父类原型的实例
var handler = {
construct: function(target, args) {
var obj = Object.create(base.prototype); // 创建一个新对象,该对象继承自基类的原型
this.apply(target, obj, args);
return obj; // 返回新创建的对象
},
apply: function(target, that, args) {
sup.apply(that, args); // 先调用父类构造函数
base.apply(that, args); // 再调用基类构造函数
},
};
var proxy = new Proxy(base, handler); // 创建一个代理对象
descriptor.value = proxy; // 将构造函数的值设置为代理对象
Object.defineProperty(base.prototype, "constructor", descriptor); // 将修改后的构造函数描述符重新定义到基类原型上
return proxy; // 返回代理对象
}
var Person = function (name) {
this.name = name;
};
var Boy = extend(Person, function (name, age) { // 注意这里的参数顺序是错误的!应该是先传入name和age,再传入thisArg。正确的调用方式如下:
Boy.call(thisArg, name, age);
});
Boy.prototype.sex = "M"; // 在扩展时添加属性或方法,需要使用extend中的handler来确保它们能够正确地应用到基类和派生类上。否则,在扩展时添加的属性或方法会直接覆盖掉基类原有的同名属性或方法。因此,在这里需要将构造函数中的this指向派生类的实例。即:new constructor(...args)。这样可以确保所有传入的参数都被正确地传递给基类和派生类的构造函数。因此,我们需要将proxy作为arguments传递进去:new proxy(...args)。另外要注意的是,这里使用了apply方法而不是construct方法来调用派生类和基类的构造函数。这是因为如果直接调用构造函数会导致this指向错误。而使用apply方法可以正确地将调用上下文设置为派生类的实例。
var Peter = new Boy("Peter", 13); // 注意这里的参数顺序应该是先传入name和age,再传入thisArg。正确的调用方式如下:
console.log(Peter.sex); // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age); // 13
```javascriptlet view = new Proxy({ selected: null }, {
set: function (obj, prop, newval) {
let oldval = obj[prop];
if (prop === "selected") {
if (oldval) {
oldval.setAttribute("aria-selected", "false");
}
if (newval) {
newval.setAttribute("aria-selected", "true");
}
}
// 默认行为是存储被传入 setter 函数的属性值
obj[prop] = newval;
// 表示操作成功
return true;
},
});
let i1 = (view.selected = document.getElementById("item-1"));
console.log(i1.getAttribute("aria-selected")); // 'true'
let i2 = (view.selected = document.getElementById("item-2"));
console.log(i1.getAttribute("aria-selected")); // 'false'
console.log(i2.getAttribute("aria-selected")); // 'true'
```
以下是重构后的内容:
```javascript
let products = new Proxy({
browsers: [
'Internet Explorer',
'Netscape',
],
}, {
get: function (obj, prop) {
// 附加一个属性
if (prop === 'latestBrowser') {
return obj.browsers[obj.browsers.length - 1];
}
// 默认行为是返回属性值
return obj[prop];
},
set: function (obj, prop, value) {
// 附加属性
if (prop === 'latestBrowser') {
obj.browsers.push(value);
return;
}
// 如果不是数组,则进行转换
if (typeof value === 'string') {
value = [value];
}
// 默认行为是保存属性值
obj[prop] = value;
// 表示成功
return true;
},
});
console.log(products.browsers); // ['Internet Explorer', 'Netscape']
products.browsers = ['Firefox']; // 如果不小心传入了一个字符串,也可以正常工作,得到的依旧是一个数组
console.log(products.browsers); // ['Firefox'] <---- 也没问题,得到的依旧是一个数组
products.latestBrowser = 'Chrome';
console.log(products.browsers); // ['Firefox', 'Chrome'] <-- Chrome也被添加到了数组中
console.log(products.latestBrowser); // 'Chrome' <-- latestBrowser被成功设置为Chrome字符串值
```
```javascriptlet products = new Proxy(
[
{ name: "Firefox", type: "browser" },
{ name: "SeaMonkey", type: "browser" },
{ name: "Thunderbird", type: "mailer" },
],
{
get: function (obj, prop) {
// 默认行为是返回属性值,prop 通常是一个整数
if (prop in obj) {
return obj[prop];
}
// 获取 products 的 number; 它是 products.length 的别名
if (prop === "number") {
return obj.length;
}
let result, types = {};
for (let product of obj) {
if (product.name === prop) {
result = product;
}
if (types[product.type]) {
types[product.type].push(product);
} else {
types[product.type] = [product];
}
}
// 通过 name 获取 product
if (result) {
return result;
}
// 通过 type 获取 products
if (prop in types) {
return types[prop];
}
// 获取 product type
if (prop === "types") {
return Object.keys(types);
}
return undefined;
},
},
);
console.log(products[0]); // { name: 'Firefox', type: 'browser' }
console.log(products["Firefox"]); // { name: 'Firefox', type: 'browser' }
console.log(products["Chrome"]); // undefined
console.log(products.browser); // [{ name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }]
console.log(products.types); // ['browser', 'mailer']
console.log(products.number); // 3
```
```javascript// 获取 "docCookies" 对象,参考链接:https://reference.codeproject.com/dom/document/cookie/simple_document.cookie_framework
const docCookies = new Proxy(docCookies, {
get(target, key) {
return target[key] || target.getItem(key) || undefined;
},
set(target, key, value) {
if (key in target) {
return false;
}
return target.setItem(key, value);
},
deleteProperty(target, key) {
if (!(key in target)) {
return false;
}
return target.removeItem(key);
},
ownKeys(target) {
return target.keys();
},
has(target, key) {
return key in target || target.hasItem(key);
},
defineProperty(target, key, descriptor) {
if (descriptor && "value" in descriptor) {
target.setItem(key, descriptor.value);
}
return target;
},
getOwnPropertyDescriptor(target, key) {
const value = target.getItem(key);
return value
? { value, writable: true, enumerable: true, configurable: false, }
: undefined;
},
}); // Cookie测试
console.log((docCookies.myCookie1 = "First value"));
console.log(docCookies.getItem("myCookie1"));
docCookies.setItem("myCookie1", "Changed value");
console.log(docCookies.myCookie1);
```