一、数据属性
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
通常我们给对象设置一个属性,configurable 、 enumerable和 writable都是true。
在调用 Object.defineProperty() 方法设置属性时,如果不指定, configurable 、 enumerable和 writable 特性的默认值都是 false。
当用Object.defineProperty()设置属性的时候:
- 拥有布尔值的键
configurable
、enumerable
和writable
的默认值都是false
。 - 属性值和函数的键
value
、get
和set
字段的默认值为undefined
。
比如:设置属性值不可以被修改
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
object1.property1 = 77;
// throws an error in strict mode
console.log(object1.property1);
// expected output: 42
但是在调用Object.defineProperty() 方法修改属性时,指定修改属性的哪个描述值,而不会影响未被修改的描述值。
1、configurable
特点:
值为false,不允许删除该属性;不允许修改enumerable;允许writable从true改为false,不允许writable从false改为true;不管理value。(理解为控制力逐渐衰弱)
值为true,允许修改其他值,也可以修改configurable本身为false,当为false时,不可以修改configurable为true。
违反configurable特性的操作,都要报错。修改value值不管成功与否,都不报错,因为configurable不控制value。
当我们使用Object.defineProperty()方法时,依赖于对象的该属性的自身的设置情况。
比如下面实例中,同时修改属性的writable和value值,修改writable值要依赖属性修改前的configurable值;修改value的值,要依赖属性修改前的writable值。
var obj = {name: "obj"};
Object.defineProperty(obj,'age',{
configurable: false,
enumerable: true,
writable: true,
value: "24"
});
Object.defineProperty(obj,'age',{
writable: false,
value: "26"
});
obj.age = 25
console.log(obj.age);--26
上面obj.age = 25修改属性值,无效,但不会报错。但是当writable为false的后,再通过defineProperty设置value,报错
var obj = {name: "obj"};
Object.defineProperty(obj,'age',{
configurable: false,
enumerable: true,
value: "24",
writable: true
});
Object.defineProperty(obj,'age',{
value: "26",
writable: false
});
报错
Object.defineProperty(obj,'age',{
value: "24"
});
2、enumerable
特点:enumerable表明该属性是显式的还是隐式的,值为true表明属性为显式,值为false表明属性为隐式的
var obj = {name: "zhu", age: 24}
Object.defineProperty(obj, "age", {
enumerable: false
})
console.log(obj)
结果:注意看name属性和age属性的颜色
enumerable
定义了对象的属性是否可以在 for...in
循环和 Object.keys()
中被枚举。
该键值为false时,该属性不可被枚举,for...in
或 Object.keys
方法不能枚举到该属性。
for in遍历
var obj = {name: "obj"};
Object.defineProperty(obj,'age',{
configurable: true,
enumerable: false,
value: "24",
writable: true
});
var arr = [];
for(prop in obj){
arr.push(prop)
}
console.log(arr); 结果:["name"]
Object.keys
返回一个所有元素为字符串的数组,其元素来自于从给定的object
上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。
由对象或数组的可枚举的keys值构成的数组(每项都是字符串)。数组的keys值是数组的索引,对象的keys值是对象的属性。
例如:
var obj = {name:"zhu",age: 24,nb:true};
var arr = ["str",1,false,[0]];
console.log(Object.keys(obj)) ["name", "age", "nb"]
console.log(Object.keys(arr)); ["0", "1", "2", "3"]
补充:如果你想获取一个对象的所有属性,,甚至包括不可枚举的,请查看Object.getOwnPropertyNames
。
Object.keys
遍历
var obj = {name: "obj"};
Object.defineProperty(obj,'age',{
configurable: true,
enumerable: false,
value: "24",
writable: true
});
console.log(Object.keys(obj)); ["name"]
3、writable
writable控制value是否可以被修改,如果writable为true,value值可以修改;如果writable为false,value值不可以被修改。
当 writable
属性设置为 false
时,该属性被称为“不可写的”,普通方式 对象.属性 修改无效,defineProperty修改会报错。
注意:不可修改地址,如果引用值,修改地址所指空间的内容,是允许的。
let arr = [1,2,3];
let obj = {
name: "zhu",
hobby: arr
}
Object.defineProperties(obj, {
"name": {
writable: false
},
"hobby": {
writable: false
}
})
obj.name = "yu"; //无法修改
obj.hobby[0] = "a";//修改成功
console.log(obj)
4、value
value值的设置就是属性值。
二、拓展知识:
1、Object.defineProperties() 定义多个属性:
var obj = {}
Object.defineProperties(obj,{
name:{
configurable: true,
enumerable: true,
writable: true,
value:"zhu"
},
age: {
writable: true,
value:24
}
})
console.log(obj);
2、Object.getOwnPropertyDescriptor()
方法返回指定对象上一个自有属性对应属性的描述符对象。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
var obj = {name: "zhu", age: 24}
Object.defineProperty(obj, "age", {
enumerable: false
})
var descriptor = Object.getOwnPropertyDescriptor(obj, "age")
console.log(descriptor);
3、Object.getOwnPropertyDescriptors()
方法用来获取一个对象的所有自身属性的描述符。
使用:Object.getOwnPropertyDescriptors(obj)
var obj = {}
Object.defineProperties(obj,{
name:{
configurable: true,
enumerable: true,
writable: true,
value:"zhu"
},
age: {
writable: true,
value:24
}
})
console.log(Object.getOwnPropertyDescriptors(obj));
二、访问器属性
1、访问器属性:包含getter和setter函数。读取访问器属性时,调用getter函数,返回有效的值;在写入访问器属性时,调用setter函数传入新值。它包含了4个特性:
-
[[Configurable]]:表示是否能通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为访问器属性。
-
[[Enumerable]]:表示能否用for-in循环或Object.keys()返回。
-
[[Get]]:读取属性时调用的函数,默认undefined。
-
[[Set]]:写入属性时调用的函数,默认undefined。
实际上,writable、value和get、set属性不能共存,这正是区分数据属性和访问器属性的根本所在。
注意:get和set函数中使用的变量,要在Object.defineProperty外声明。
var obj = {name: "zhu"};
Object.defineProperty(obj, "age", {
// 使用了方法名称缩写(ES2015 特性)
// 下面两个缩写等价于:
// get : function() { return bValue; },
// set : function(newValue) { bValue = newValue; },
get() {
console.log("getter");打印getter
return bValue;
},
set(newValue) {
console.log("setter");打印setter
bValue = newValue;
},
enumerable: true,
configurable: true
})
obj.age = 24; 这一步走的就是setter
console.log(obj.age); 这一步走的是getter
2、关于继承
如果访问器属性在原型上,通过实例对象,也可以修改原型上的访问器属性。(普通属性和数据属性是不可以通过实例修改原型的属性值。当执行修改操作时,实际上是在实例上添加设置该属性,而非在原型上)
function myclass() {
}
var value;
Object.defineProperty(myclass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1
3、关于this
切记访问器属性中的get和set函数中的this,谁调用了这两个方法,this就指向谁。
function myclass() {}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x;
},
set(x) {
this.stored_x = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1; 在a对象上添加了stored_x属性,且值为x 1
console.log(myclass.prototype.x); 获取的时候,获取的是原型上的stored_x
需要注意的是,实例对象调用原型的访问器属性