持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
哈哈哈哈,奶奶的考试周终于过去了,俺又有时间学前端了哈哈。
1、对象的属性
对象的属性分为两种:数据属性和访问器属性。
数据属性
数据属性有4个特性描述他们的行为:
- Configurable:是否可以被
delete删除并重新定义,是否可以修改它的特性,以及是否可以被改为访问器属性, 能否被二次配置。 - Enumerable:表示属性是否可以被
for-in遍历 - Writable:表示属性的值是否可以被修改
- Value:属性值
其中的
Configurable、Enumerable、Writable默认都为true
let user = {
name: "ly",
age: "18",
};
// Object.getOwnPropertyDescriptor(obj,key)可以读取对象里面某个值的属性
// 读取对象的单个值属性(name)
console.log(Object.getOwnPropertyDescriptor(user, "name"));
/*
{
value: "ly" 属性值
writable: true 是否可写
enumerable: true 是否可以被遍历
configurable: true 是否可以被删除获取重新被配置
}
*/
// 读取队形里面所有值得属性
console.log(JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2));
访问器属性
访问器属性不包含数据值,相反,他们包含一个获取(getter)函数和一个设置(setter)函数。不过这两个函数不是必须的。
let person = {
name: 'ly',
_age: 18,
get age() {
return this._age;
},
set age(value) {
if (typeof value === "number" && value > 10 && value < 100) {
this._age = value;
} else {
console.error("设置的age不符合规范");
}
}
}
console.log(person.age); //18
person.age = 111; //设置的age不符合规范
console.log(person.age); //18
person.age = 20;
console.log(person.age); //20
我们可以发现,当我们要控制对象里面的值不能被随意更改,需要一定规则限制的时候可以用对象的访问器来实现。
此外,访问器还可以进行伪造属性,也就是对象本身没有的属性,可以使用访问器进行伪造。
let lesson = {
list: [
{ name: "css", price: 60 },
{ name: "js", price: 70 },
{ name: "html", price: 80 },
{ name: "jquery", price: 90 },
{ name: "vue", price: 100 },
],
// 可以利用访问器实现特定方法
get total() {
return this.list.reduce((v, l) => v + l.price, 0);
},
get namecount() {
return `当前课程数量${this.list.length}`;
},
// 传入参数加到每个对象的name中
set changename(val) {
this.list.forEach((item) => {
item.name = item.name + val;
});
},
get changename() {
return this.list;
},
};
console.log(lesson.total); // 400
console.log(lesson.namecount); // 当前课程数量5
lesson.changename = "_火爆售卖中";
// {"name":"css_火爆售卖中","price":60},{.....}
console.table(lesson.changename);
2、修改属性
我们上面看到了对象的默认属性有四个,可以用Object.getOwnPropertyDescriptor(obj, propertyName) 来进行查询有关某个属性的完整信息。
而要修改属性的默认属性,就必须使用 Object.definedProperty() 方法。
Object.defineProperty(obj, prop, descriptor)
obj:要定义属性的对象。
prop:要定义或修改的属性的名称。
descriptor:要定义或修改的属性描述符。
let person = {}
Object.defineProperty(person, 'name', {
writable: false,
value: 'ly',
configurable: false
})
console.log(person.name); //ly
person.name = 'lyyy'
console.log(person.name); //ly
delete person.name;
console.log(person.name); //ly
我们为person对象赋予了一个只读的、不可配置的属性name, 且值为ly,那么这个属性的值就不能再被修改、这个属性不能从对象删除了。
但是要注意: configurable设置为false后,再调用Object.defineProperty()就会受到限制了。
let person = {}
Object.defineProperty(person, 'name', {
writable: false,
value: 'ly',
configurable: false
})
console.log(person.name); //ly
person.name = 'lyyy'
console.log(person.name); //ly
Object.defineProperty(person, 'name', {
writable: true
})
person.name = 'yyy' // Cannot redefine property: name
console.log(person.name); //ly
当configurable设置为false后,就不能对其属性进行二次配置了。
Object.defineProperty(obj, props)
obj :在其上定义或修改属性的对象。
props:定义其可枚举属性或修改的属性描述符的对象。
这个方法可以在一个对象上同时定义多个属性。
let person = {};
Object.defineProperties(person, {
name: {
value: 'ly',
writable: true,
configurable: true,
enumerable: true
},
_age: {
value: 18
},
age: {
get() {
return this.age;
},
set(value) {
if (typeof value === "number" && value > 10 && value < 100) {
this._age = value;
} else {
console.error("设置的age不符合规范");
}
}
}
})
3、封闭对象
Object.seal(obj)封闭对象,即不能删除已有属性,但可以修改已有属性的值,不能添加新的属性Object.isSealed(obj)查看对象是否被封闭,已封闭返回 true,未封闭返回 false
let user = {
name: "ly",
age: 18,
};
// Object.seal() 封闭对象
Object.seal(user);
// 已封闭返回true,未封闭返回false
console.log(Object.isSealed(user));
封闭对象的本质就是将对象可配置属性configurable变成false
console.log(JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2));
4、冻结对象
Object.freeze(obj)冻结对象,冻结后值不可被修改、属性不可重复修改、可以被遍历Object.isFrozen(obj)查看对象是否被冻结,是返回 true,不是返回 false
let user = {
name: "ly",
age: 18,
};
Object.freeze(user); // true
console.log(Object.isFrozen(user)); //true
封闭对象的本质就是将对象可配置属性configurable和writable变成false
5、Vue中的数据代理与数据响应
总结到这里,就不得不提一下Vue中的数据代理和数据响应。Vue2的数据代理就是使用Object.defineProperty加访问器属性实现的。
在Vue2中,我们在template的data中写的数据,在Vue的实例vm上面其实是绑定在_data上面的,但是我们在使用data中的数据时,却没有{{_data.xxx}},这是因为,Vue自动使用数据代理,将_data中的数据代理到vm上面去了,同时,为每一个添加到vm上的属性,都添加了setter和getter的访问器属性。
let vm = {
_data:{
x:100
}
}
Object.defineProperty(vm, 'x', {
get() {
return vm._data.x;
},
set(value) {
vm._data.x = value;
}
})
但是这有一个问题,就是对引用类型,尤其是数组,再对其进行增删查改,数据不能进行响应,所以在Vue3中,官方使用了Proxy和Reflect对引用数据类型进行了数据代理,而只是保留了对原始数据类型仍然使用Object.defineProperty进行数据代理。