2025-01-30 日报 Day82

2025-01-30 日报 Day82

Yuyang 前端小白🥬

今日的鸡汤

容言,则日有所进;容事,则事无不成;容人,则人无不和。宽容待人,也会被世界温柔相待。

今日学习内容

1、JS 红皮书 P283-286 第九章:代理与反射

今日笔记

1、代理模式:

  • 跟踪属性访问: 通过捕获 get、set 和 has 等操作,可以知道对象属性什么时候被访问、被查询。把实现相应捕获器的某个对象代理放到应用中,可以监控这个对象何时在何处被访问过:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
  • 隐藏属性: 代理的内部实现对外部代码是不可见的,因此要隐藏目标对象上的属性也轻而易举。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
  • 属性验证: 因为所有赋值操作都会触发 set()捕获器,所以可以根据所赋的值决定是允许还是拒绝赋值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
  • 函数与构造函数参数验证: 跟保护和验证对象属性类似,也可对函数和构造函数参数进行审查。比如,可以让函数只接收某种类型的值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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

2、数据绑定与可观察对象: 通过代理可以把运行时中原本不相关的部分联系到一起。这样就可以实现各种模式,从而让不同的代码互操作。
比如,可以将被代理的类绑定到一个全局实例集合,让所有创建的实例都被添加到这个集合中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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{}]

另外,还可以把集合绑定到一个事件分派程序,每次插入新实例时都会发送消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

3、小结:
代理是 ECMAScript 6 新增的令人兴奋和动态十足的新特性。尽管不支持向后兼容,但它开辟出了一片前所未有的 JavaScript 元编程及抽象的新天地。
从宏观上看,代理是真实 JavaScript 对象的透明抽象层。代理可以定义包含捕获器的处理程序对象,而这些捕获器可以拦截绝大部分 JavaScript 的基本操作和方法。在这个捕获器处理程序中,可以修改任何基本操作的行为,当然前提是遵从捕获器不变式。
与代理如影随形的反射 API,则封装了一整套与捕获器拦截的操作相对应的方法。可以把反射 API看作一套基本操作,这些操作是绝大部分 JavaScript 对象 API 的基础。
代理的应用场景是不可限量的。开发者使用它可以创建出各种编码模式,比如(但远远不限于)跟踪属性访问、隐藏属性、阻止修改或删除属性、函数参数验证、构造函数参数验证、数据绑定,以及可观察对象。

此页目录
2025-01-30 日报 Day82