Property Descriptor 属性描述符 是一个普通对象,用于描述一个属性的相关信息;
通过Object.getOwnPropertyDescriptor(对象, 属性名)
可以得到一个对象的某个属性的属性描述符;
也可以通过 Object.getOwnPropertyDescriptors(对象)
可以得到某个对象的所有的属性描述符;
value
:属性值configurable
:该属性的描述符是否可以修改(默认值为true)enumerable
:该属性是否可以被枚举(默认值为true)writable
:该属性(的值)是否可以被重新赋值(默认值为true)如果需要为某个对象添加属性时 或 修改属性时, 配置其属性描述符,可以使用下面的代码:
Object.defineProperty(对象, 属性名, 描述符);
Object.defineProperties(对象, 多个属性的描述符)
const obj = {
a: 1,
b: 2
}
//obj.a = 3;
Object.defineProperty(obj, "a", {
value: 3,
configurable: false, //之后就不能在针对该属性的属性描述符进行修改
enumerable: false, // 之后该属性就不可枚举,(会影响for in 循环,Object.keys得到一个对象的所有属性名,Object.value得到一个对象的所有属性值)
writable: false // 之后该属性不能被重新修改赋值
})
// Object.defineProperties(obj, {
// a: {
// value: 3,
// configurable: false,
// enumerable: false,
// writable: false
// }
// })
obj.a = 10;
console.log(obj); // {b: 2, a: 3}
属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。
get 和 set配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行get方法
,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set方法
。
存取器属性最大的意义,在于可以控制属性的读取和赋值。
const obj = {
b: 2
}
Object.defineProperty(obj, "a", {
get() {
console.log("运行了属性a的get函数")
},
set(val){
console.log("运行了属性a的set函数", val)
}
})
obj.a = 20 + 10; // set(20+10), 运行了属性a的set函数", 30
console.log(obj.a); // console.log(get()) , 运行了属性a的get函数 返回 undefined
// obj.a = obj.a + 1; // set(obj.a + 1) set(get() + 1) 运行了属性a的set函数", NaN
// console.log(obj.a); // undefined
const obj = {
b: 2
}
Object.defineProperty(obj, "a", {
get() {
console.log("运行了属性a的get函数")
return obj._a;
},
set(val){
console.log("运行了属性a的set函数", val)
obj._a = val;
}
})
obj.a = 10; // 运行了属性a的set函数 10
console.log(obj.a); // 运行了属性a的get函数
// 10
obj = {
name: "adsf"
}
Object.defineProperty(obj, "age", {
get() {
return obj._age;
},
set(val) {
if (typeof val !== "number") {
throw new TypeError("年龄必须是一个数字")
}
if (val < 0) {
val = 0;
} else if (val > 200) {
val = 200;
}
obj._age = val;
}
})
obj.age = "Asdfasasdf";
console.log(obj.age); //报错 TypeError: 年龄必须是一个数字 at Object.set
实例:
<p>
<span>姓名:span>
<span id="name">span>
p>
<p>
<span>年龄:span>
<span id="age">span>
p>
const spanName = document.getElementById("name")
const spanAge = document.getElementById("age")
const user = {}
Object.defineProperties(user, {
name: {
get() {
return spanName.innerText;
},
set(val) {
spanName.innerText = val;
}
},
age: {
get() {
return +spanAge.innerText;
},
set(val) {
if (typeof val !== "number") {
throw new TypeError("年龄必须是一个数字")
}
if (val < 0) {
val = 0;
} else if (val > 200) {
val = 200;
}
spanAge.innerText = val;
}
}
})
Reflect是什么?
Reflect
是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能
由于它类似于其他语言的反射,因此取名为Reflect
它可以做什么?
使用Reflect
可以实现诸如 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在与对象中 等等功能
这些功能不是已经存在了吗?为什么还需要用Reflect实现一次?
有一个重要的理念,在ES5就被提出:减少魔法、让代码更加纯粹
这种理念很大程度上是受到函数式编程的影响
ES6进一步贯彻了这种理念,它认为,对属性内存的控制、原型链的修改、函数的调用等等,这些都属于底层实现,属于一种魔法,因此,需要将它们提取出来,形成一个正常的API,并高度聚合到某个对象中,于是,就造就了Reflect对象
因此,你可以看到Reflect对象
中有很多的API都可以使用过去的某种语法或其他API实现。
它里面到底提供了哪些API呢?
Reflect.set(target, propertyKey, value)
: 设置对象target的属性propertyKey的值为value,等同于给对象的属性赋值 const obj = {
a: 1,
b: 2
}
// obj.a = 10; // 魔法
Reflect.set(obj, "a", 10); // 使用API 给obj的a属性赋值为10
Reflect.get(target, propertyKey)
: 读取对象target的属性propertyKey,等同于读取对象的属性值const obj = {
a: 1,
b: 2
}
// obj.a = 10;
Reflect.set(obj, "a", 10);
console.log(Reflect.get(obj, "a")); // 使用API 读取obj的a属性的值
Reflect.apply(target, thisArgument, argumentsList)
:调用一个指定的函数,并绑定this和参数列表。等同于函数调用 function method(a, b){
console.log("method", a, b);
}
// method(3, 4); // 魔法
Reflect.apply(method, null, [3, 4]);
Reflect.deleteProperty(target, propertyKey)
:删除一个对象的属性 const obj = {
a: 1,
b: 2
}
// delete obj.a; // 魔法
Reflect.deleteProperty(obj, "a"); // 删除obj对象的a属性
Reflect.defineProperty(target, propertyKey, attributes)
:类似于Object.defineProperty(对象, 属性名, 描述符)
,为某个对象添加属性时或修改属性时, 配置其属性描述符;不同的是如果配置出现问题,返回false而不是报错 const obj = {
a: 1,
b: 2
}
//obj.a = 3;
Reflect.defineProperty(obj, "a", {
value: 3,
configurable: false, //之后就不能在针对该属性的属性描述符进行修改
enumerable: false, // 之后该属性就不可枚举,(会影响for in 循环,Object.keys得到一个对象的所有属性名,Object.value得到一个对象的所有属性值)
writable: false // 之后该属性不能被重新修改赋值
})
Reflect.construct(target, argumentsList)
:用构造函数的方式创建一个对象 function Test(a, b) {
this.a = a;
this.b = b;
}
// const t = new Test(1, 3); // 魔法
const t = Reflect.construct(Test, [1, 3]); //构造函数的方式创建一个对象
console.log(t)
Reflect.has(target, propertyKey)
: 判断一个对象是否拥有一个属性 const obj = {
a: 1,
b: 2
}
// console.log("a" in obj); // 魔法
console.log(Reflect.has(obj, "a")); //判断obj里是否有a属性 true
代理:提供了修改底层实现的方式
Proxy
可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
//代理一个目标对象
//target:目标对象
//handler:是一个普通对象,其中可以重写底层实现
//返回一个代理对象
const proxy = new Proxy(target, handler);
Proxy
对象的所有用法,都是上面这种形式,不同的只是handler
参数的写法。其中,new Proxy()
表示生成一个Proxy实例,target
参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。如果handler
没有设置任何拦截,那就等同于直接通向原对象。
const obj = {
a: 1,
b: 2
}
const proxy = new Proxy(obj, {
set(target, propertyKey, value) {
// console.log(target, propertyKey, value);
// target[propertyKey] = value;
Reflect.set(target, propertyKey, value);
},
get(target, propertyKey) {
if (Reflect.has(target, propertyKey)) {
return Reflect.get(target, propertyKey);
} else {
return -1;
}
},
has(target, propertyKey) {
return false;
}
});
// console.log(proxy);
// proxy.a = 10;
// console.log(proxy.a);
console.log(proxy.d); // -1
console.log("a" in proxy); // false 在代理那里做了修改,全部返回了false
部分内容可以参考阮一峰老师的ES6 教程
有一个对象,是观察者,它用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事。
//创建一个观察者
function observer(target) {
const div = document.getElementById("container");
// const ob = {};
// const props = Object.keys(target); // 得到所有的属性名
// for (const prop of props) {
// Object.defineProperty(ob, prop, {
// get() {
// return target[prop];
// },
// set(val) {
// target[prop] = val;
// render();
// },
// enumerable: true
// })
//}
const proxy = new Proxy(target, {
set(target, prop, value) {
Reflect.set(target, prop, value);
render();
},
get(target, prop){
return Reflect.get(target, prop);
}
})
render();
function render() {
let html = "";
for (const prop of Object.keys(ob)) {
html += `
${prop}:${ob[prop]}
`;
}
div.innerHTML = html;
}
return proxy;
}
const target = {
a: 1,
b: 2
}
const obj = observer(target)
</script>
class User {
}
function ConstructorProxy(Class, ...propNames) {
return new Proxy(Class, {
construct(target, argumentsList) {
const obj = Reflect.construct(target, argumentsList)
propNames.forEach((name, i) => {
obj[name] = argumentsList[i];
})
return obj;
}
})
}
const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age")
const obj = new UserProxy("胖", "虎", 18);
console.log(obj)
class Monster {
}
const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")
const m = new MonsterProxy(10, 20, 100, 30, "怪物")
console.log(m);
function sum(a, b) {
return a + b;
}
function validatorFunction(func, ...types) {
const proxy = new Proxy(func, {
apply(target, thisArgument, argumentsList) {
types.forEach((t, i) => {
const arg = argumentsList[i]
if (typeof arg !== t) {
throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
}
})
return Reflect.apply(target, thisArgument, argumentsList);
}
})
return proxy;
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))
function sum(a, b) {
return a + b;
}
function validatorFunction(func, ...types) {
return function(...argumentsList) {
types.forEach((t, i) => {
const arg = argumentsList[i]
if (typeof arg !== t) {
throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
}
})
return func(...argumentsList)
}
return proxy;
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, 2))