PROXIES AND REFLECT
Proxy Fundamentals
Creating a Passthrough Proxy
语法格式
const target = {
id: "target",
};
const handler = {};
const proxy = new Proxy(target, handler);
Defining Traps
定义 Traps
const target = {
foo: "bar",
};
const handler = {
// Traps are keyed by method name inside the handler object
get() {
return "handler override";
},
};
const proxy = new Proxy(target, handler);
调用机制
- 每当 proxy 调用 get() 函数时,
- 会使用 handler 中的 get() 代替调用,
- 使用 target 调用 get() 函数时一切正常。
console.log(target.foo); // bar
console.log(proxy.foo); // handler override
console.log(target["foo"]); // bar
console.log(proxy["foo"]); // handler override
console.log(Object.create(target)["foo"]); // bar
console.log(Object.create(proxy)["foo"]); // handler override
作用
- 提供代码编写者改变几乎任何原生方法的能力。
Trap Parameters and the Reflect API
Reflect object
- target 上的同名原生方法定义在 Reflect object 上,
- 任何可以在 handle 中定义为 trap 的方法在 Reflect object 都有对应的 API,
- 具有相同的名字和参数。
const target = {
foo: "bar",
};
const handler = {
get: Reflect.get,
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo); // bar
console.log(target.foo); // bar
定义 passthrough proxy
const target = {
foo: "bar",
};
const proxy = new Proxy(target, Reflect);
console.log(proxy.foo); // bar
console.log(target.foo); // bar
Trap Invariants
Trap Invariants
- 约束 trap 的准则。
Revocable Proxies
Proxy.revocable() 方法
- Proxy.revocable(target,handler);
- 创建一个可销毁的 proxy;
- 返回值:object;
- proxy 属性:等同于 new Proxy(target,handler) 创建的 proxy;
- revoke 属性:revoke() 函数,用于销毁 proxy。
Utility of the Reflect AP
Reflect API vs. Object API
- reflect 对于 trap handler 没有限制;
Status Flags
- 一些 Reflect 方法返回 boolean,
- Reflect.defineProperty
- Reflect.preventExtensions
- Reflect.setPrototypeOf
- Reflect.set
- Reflect.deleteProperty
- 表示该方法是否执行成功,
- 在一些场景极其有用。
// Initial code
const o = {};
try {
Object.defineProperty(o, "foo", "bar");
console.log("success");
} catch (e) {
console.log("failure");
}
// Refactored code
const o = {};
if (Object.defineProperty(o, "foo", "bar")) {
console.log("success");
} else {
console.log("failure");
}
Supplanting Operators with First-Class Functions
Safe Function Application
Function.prototype.apply.call(myFunc, thisVal, argumentList);
Reflect.apply(myFunc, thisVal, argumentsList);
Proxying a Proxy
const target = {
foo: "bar",
};
const firstProxy = new Proxy(target, {
get() {
console.log("first proxy");
return Reflect.get(...arguments);
},
});
const secondProxy = new Proxy(firstProxy, {
get() {
console.log("second proxy");
return Reflect.get(...arguments);
},
});
console.log(secondProxy.foo);
// second proxy
// first proxy
// bar
Proxy Considerations and Shortcomings
' this' Inside a Proxy
Proxies and Internal Slots
Proxy Traps and Reflect Methods
get()
handler.get() 方法
- handler.get(target,property,receiver)。
- 检索某个属性值。
- 返回值:任何值。
Interceptions
- 略。
Trap invariants
- 略。
set()
handler.set() 方法
- handler.set(target,property,value,receiver)。
- 设置某个属性值。
- 返回值。
- true:设置成功;
- TypeError:设置失败;
Interceptions
- 略。
Trap invariants
- 略。
has()
handler.has() 方法
- handler.has(target,prop)。
- in 操作符的 trap 形式。
- 返回值:boolean。
Interceptions
- 略。
Trap invariants
- 略。
defineProperty()
handler.defineProperty() 方法
- handler.defineProperty(target,property,descriptor)。
- Object.defineProperty() 的 trap 形式。
- 返回值:boolean。
Interceptions
- 略。
Trap invariants
- 略。
getOwnPropertyDescriptor()
handler.getOwnPropertyDescriptor() 方法
- handler.getOwnPropertyDescriptor(target,prop)。
- Object.getOwnPropertyDescriptor() 的 trap 形式。
- 返回值:object/undefined。
Interceptions
- 略。
Trap invariants
- 略。
deleteProperty()
handler.deleteProperty() 方法
- handler.deleteProperty(target,property)。
- delete 操作符的 trap 形式。
- 返回值:boolean。
Interceptions
- 略。
Trap invariants
- 略。
ownKeys()
handler.ownKeys() 方法
- handler.ownKeys(target)。
- Reflect.ownKeys() 的 trap 形式 。
- 返回值:enumerable object。
Interceptions
- 略。
Trap invariants
- 略。
getPrototypeOf()
handler.getPrototypeOf() 方法
- handler.getPrototypeOf(target)。
- [[GetPrototypeOf]] 的 trap 形式。
- 返回值:object/null。
Interceptions
- 略。
Trap invariants
- 略。
setPrototypeOf()
handler.setPrototypeOf() 方法
- handler.setPrototypeOf(target,prototype)。
- Object.setPrototypeOf() 的 trap 形式。
- 返回值:boolean。
Interceptions
- 略。
Trap invariants
- 略。
isExtensible()
handler.isExtensible() 方法
- handler.isExtensible(target)。
- Object.isExtensible() 的 trap 形式。
- 返回值:boolean。
Interceptions
- 略。
Trap invariants
- 略。
preventExtensions()
handler.preventExtensions() 方法
- handler.set(target,property,value,receiver)。
- Object.preventExtensions() 的 trap 形式。
- 返回值:boolean。
Interceptions
- 略。
Trap invariants
- 略。
apply()
handler.apply() 方法
- handler.apply(target,thisArg,argumentsList)。
- function call 的 trap 形式。
- 返回值:任何值。
Interceptions
- 略。
Trap invariants
- 略。
construct()
handler.construct() 方法
- handler.construct(target,argumentsList,newTarget)。
- new 关键字的 trap 形式。
- 返回值:object。
Interceptions
- 略。
Trap invariants
- 略。
Proxy Patterns
Tracking Property Access
const user = {
name: "Jake",
};
const proxy = new Proxy(user, {
get(target, property, receiver) {
console.log("Getting ${property}");
return Reflect.get(...arguments);
},
set(target, property, value, receiver) {
console.log("Setting ${property}=${value}");
return Reflect.set(...arguments);
},
});
proxy.name; // Getting name
proxy.age = 27; // Setting age=27
Hidden Properties
const hiddenProperties = ["foo", "bar"];
const targetObject = {
foo: 1,
bar: 2,
baz: 3,
};
const proxy = new Proxy(targetObject, {
get(target, property) {
if (hiddenProperties.includes(property)) {
return undefined;
} else {
return Reflect.get(...arguments);
}
},
has(target, property) {
if (hiddenProperties.includes(property)) {
return false;
} else {
return Reflect.has(...arguments);
}
},
});
// get()
console.log(proxy.foo); // undefined
console.log(proxy.bar); // undefined
console.log(proxy.baz); // 3
// has()
console.log("foo" in proxy); // false
console.log("bar" in proxy); // false
console.log("baz" in proxy); // true
Property Validation
const target = {
onlyNumbersGoHere: 0,
};
const proxy = new Proxy(target, {
set(target, property, value) {
if (typeof value !== "Number") {
return false;
} else {
return Reflect.set(...arguments);
}
},
});
proxy.onlyNumbersGoHere = 1;
console.log(proxy.onlyNumbersGoHere); // 1
proxy.onlyNumbersGoHere = "2";
console.log(proxy.onlyNumbersGoHere); // 1
Function and Constructor Parameter Validation
function median(...nums) {
return nums.sort()[Math.floor(nums.length / 2)];
}
const proxy = new Proxy(median, {
apply(target, thisArg, ...argumentsList) {
for (const arg of argumentsList) {
if (typeof arg !== "number") {
throw "Non-number argument provided";
}
}
return Reflect.apply(...arguments);
},
});
console.log(proxy(4, 7, 1)); // 4
console.log(proxy(4, "7", 1));
// Error: Non-number argument provided
class User {
constructor(id) {
this.id_ = id;
}
}
const proxy = new Proxy(User, {
construct(target, argumentsList, newTarget) {
if (argumentsList[0] === undefined) {
throw "User cannot be instantiated without id";
} else {
return Reflect.construct(...arguments);
}
},
});
new proxy(1);
new proxy();
// Error: User cannot be instantiated without id
Data Binding and Observables
const userList = [];
class User {
constructor(name) {
this.name_ = name;
}
}
const proxy = new Proxy(User, {
construct() {
const newUser = Reflect.construct(...arguments);
userList.push(newUser);
return newUser;
},
});
new proxy("John");
new proxy("Jacob");
new proxy("Jingleheimerschmidt");
console.log(userList); // [User {}, User {}, User{}]
const userList = [];
function emit(newValue) {
console.log(newValue);
}
const proxy = new Proxy(userList, {
set(target, property, value, receiver) {
const result = Reflect.set(...arguments);
if (result) {
emit(Reflect.get(target, property, receiver));
}
return result;
},
});
proxy.push("John");
// John
proxy.push("Jacob");
// Jacob