前情提要
众所周知,JavaScript是一门弱类型语言,但它同样是一门面向对象的语言,准确点说是一门基于原型的面向对象的语言。
什么是对象 ?
ECMA-262规范把对象定义为:无序属性的集合,其属性值可以包含基本类型值、对象或者方法。通俗点讲,对象是一组键值对的集合。键表示属性名称,值表示属性的值。
对象的属性
对象有两个属性:数据属性和访问器属性
数据属性
数据属性具有4个描述行为的特性,因为这些特性是内部值,所以ECMA-262规范将其放在了两对方括号中。
- [[Configurable]]: 表示属性能否删除而重新定义,或者是否可以修改为访问器属性,默认值为true。
- [[Enumerable]]: 表示属性是否可枚举,可枚举的属性能够通过for...in循环返回,默认值为true。
- [[Writable]]: 表示属性值能否被修改,默认值为true。
- [[Value]]: 表示属性的真实值,属性的读取和写入均通过此属性完成,默认值为undefined。 如果需要修改数据属性默认的特性,则必须使用Object.defineProperty()方法。比如:
// 此时name和age均具有Configurable、Enumerable、Writable和Value属性。其中Configurable,Enumerable,Writable默认为true。
let mag = {
name: "张三",
age: 18
}
msg.name = "李四";
console.log(msg.name); // 李四
Object.defineProperty(msg, 'name', {
writable: false
})
msg.name = "王二麻子";
console.log(msg.name); // 李四
Object.defineProperty(msg, 'name', {
enumerable: false
})
for(let key in msg) {
console.log(key);
} // age
访问器属性
访问器属性同样具有4个特性:
- [[Configurable]]: 表示属性能否删除而重新定义,或者是否可以修改为访问器属性,默认值为true。
- [[Enumerable]]: 表示属性是否可枚举,可枚举的属性能够通过for...in循环返回,默认值为true。
- [[Get]]: 在读取属性值时调用的函数(一般称为getter()函数),负责返回有效的值,默认值为undefined。
- [[Set]]: 在写入属性值时调用的函数(一般称为setter()函数),负责处理数据,默认值为undefined。 同样的,如果要修改访问器属性的特性,也必须使用Object.defineProperty()函数。 getter()方法和setter()方法的存在在一定程度上可以实现对象的私有属性,私有属性不对外暴露。如果想要读取和写入私有属性的值,则需要通过设置额外属性的getter()方法和setter()方法来实现。比如:
let obj = {
_age: 10
}
Object.defineProperty(obj, "age", {
get() {
return this._age;
},
set(val) {
if (val > 10) {
this._age = val;
console.log('更新成功');
}
}
})
console.log(obj.age); // 10
obj.age = 4;
console.log(obj.age); // 10
obj.age = 100; // 更新成功
console.log(obj.age); // 100
Object.defineProperty()的其他兄弟
- Object.defineProperties() 可以同时修改对象里多个属性的数据属性和访问器属性
let obj = {
name: "王小二",
age: 18
}
Object.defineProperties(obj, {
// 数据属性
name: {
value: "改头换面",
writable: false
},
age: {
value: 25,
writable: true
},
// 访问器属性
name: {
get() {
return this.name;
},
set(val) {
this.name = val;
}
}
})
- Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
let obj = {
name: '小明'
}
let attr = Object.getOwnPropertyDescriptor(obj, "name");
console.log(attr);
// 正常情况下是:
{
configurable: true
enumerable: true
value: "小明"
writable: true
}
如果没有getter,或者setter,获取时会返回undefined
兼容性
并不是所有的浏览器都支持Object.defineProperty()或者Object.defineProperties();以及Object.getOwnPropertyDescriptor()
Object.defineProperty()不受支持时,可以通过defineGetter() 和 **defineSetter()**来构造访问器属性的get和set特性。
同时Object.getOwnPropertyDescriptor()可以用 lookupGetter 和 lookupSetter来获取getter和setter
var obj = {
_num: 2004,
age: 18
};
obj.__defineGetter__('num', function() {
return this._num;
});
obj.__defineSetter__('num', function(newValue) {
if (newValue > 2004) {
this._num = newValue;
this.age += newValue - 2004;
}
})
obj.num = 2010; // 写入属性
console.log(obj.age, obj.num); // -> 24 2010