属性描述符
js
/*
对象属性修饰符:
{
value:10, 该属性的属性值
writable: false, 该属性是否可写
enumerable: true, 该属性是否可遍历和访问
configurable: true, 是否可以再次修改该属性的修饰符信息
}
*/
let obj = {
a: 10,
b: "hello",
};
//可修改
obj.a = "aaa";
console.log(obj);
//可遍历
for (let k in obj) {
console.log(k);
}
console.log(Object.keys(obj)); //返回属性名组成的数组
/* 获取对象某个属性的修饰信息 */
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
/* 设置对象的属性修饰符 */
Object.defineProperty(obj, "c", {
value: "我是c",
writable: false,
enumerable: true,
configurable: false,
// getter和setter
});
//如果上面设置configurable: false,那么此处再修改属性修饰符的信息就会报错
Object.defineProperty(obj, "c", {
writable: true,
});
obj.c = 20; //writable:false就无法修改
console.log(obj); //enumerable:false就无法访问和遍历
getter 和 setter
js
let obj = {};
//getter和setter
Object.defineProperty(obj, "a", {
get() {
console.log("访问了a属性");
return 111;
},
set(val) {
console.log("设置了a属性,值是:", val);
//此处可以直接将a属性设置为只读的,如果使用者设置了,就直接报错
throw new Error("兄弟,你正在设置a属性,这是不允许的,因为a属性是只读的");
},
});
console.log(obj.a); //等价于console.log(get())
obj.a = 20; //等价于set(20) 20会作为参数传入到set(val)中
对购物车案例的优化
js
// 上节课的购物车数据
let Goods = {
pic: ".",
title: "..",
desc: `...`,
sellNumber: 1,
favorRate: 2,
price: 3,
};
/* 上节课的数据处理 */
class UIData {
constructor() {
this.data = Goods;
this.chooseCount = 0;
}
}
//这样写会出现隐患,就是如果用户直接修改了data和chooseCount,那么程序就变得不稳定了。
const uidata = new UIData();
uidata.data = "123"; //data就被修改为了'123'
uidata.chooseCount = "我就是改着玩";
console.log(uidata);
/* 为了避免上述的问题,我们需要加强对程序的健壮性 */
class UIData2 {
constructor(g) {
g = { ...g }; //防止用户修改元数据
Object.freeze(g); //放置用户再通过data修改g中的属性
//data属性
//法一:缺点是,用户在修改的时候,虽然修改不成功,但是不会报错,会让人产生疑问的
// Object.defineProperty(this, "data", {
// value: Goods,
// writable: false,
// enumerable: true,
// configurable: false,
// });
//法二:使用setter和getter
Object.defineProperty(this, "data", {
configurable: false,
get() {
return g;
},
set(val) {
throw new Error(
"你正在修改data属性为val,这是不允许的,因为data是只读的"
);
},
});
//定义chooseCount
let interval = undefined;
Object.defineProperty(this, "chooseCount", {
configurable: false,
get() {
return interval;
},
set(val) {
// 做设置的限制
if (typeof val !== "number") {
throw new Error("chooseCount属性只能设置为number类型");
}
if (val < 0) {
throw new Error("chooseCount值不能<0");
}
let temp = parseInt(val);
if (temp !== val) {
throw new Error("chooseCount值必须是整数");
}
interval = val;
},
});
//冻结this.防止用户通过实例添加属性
// Object.freeze(this);
//冻结不可取,y允许用户修改,freeze()会将整个this冻结,将this密封起来就好了
this.y = 20;
Object.seal(this);
}
}
Object.freeze(UIData2.prototype);
const uidata2 = new UIData2(Goods);
// uidata2.data = "我这次该不了了";
// uidata2.chooseCount = 13;
// console.log(uidata2);
//上述还会产生漏洞,因为你完全想不到用户会怎么使用
//1.用户直接修改元数据Goods -- 解决办法,深拷贝复制一份元数据
Goods.price = "123";
console.log(uidata2.data); //price被修改为123
//2.但是用户直接访问data的属性修改,解决办法,使用frezee()冷冻对象
uidata2.data.price = "我又修改了,想不到把";
console.log(uidata2.data.price);
//3.但是用户依旧可以通过原型修改或者添加,解决办法:冷冻原型
UIData2.prototype.haha = "我又来了";
console.log(uidata2.haha);
//4. 用户依旧可以通过uidata来新增新的属性,解决办法就是将这个对象this给冻结
uidata2.x = "新增的";
console.log(uidata2);
//5.但是,万一类中有普通属性,this.y=10,这个可以修改呢?freeze(this)之后所有的属性都不可以修改了,这又是问题了
//解决办法: 将freeze()改为seal()密封
/*
结论:
应用方面的是最简单的,而开发一个库或者框架层面却是难的,需要考虑到千变万化的条件
而开发一个东西程序,并不能用代码量来判断好坏,而是程序的稳定性,有的程序代码多,但是健壮。
有的程序代码多,是繁琐。
*/