定义对象有两种方式:
var obj = {"a":1,"b":2}
或者
var obj = new Object();
obj.a = 1;
obj.b = 2;
我们一般都会用第一种方式吧?第二个还要逐个添加呢,太麻烦了。
有没有想过定义一个属性a,难道这个a就是光杆司令,只有一个值吗?
其实吧,每一个属性都有一群小兵呢,它们叫做属性描述符,就是详细描述这个属性的。看代码:
var obj = {"a":1};
Object.getOwnPropertyDescriptor(obj,"a");
//{
// value: 1
// writable: true
// enumerable: true
// configurable: true
//}
哇!一个属性都这么多东西呢。不着急,下面我们一个一个来研究它们都是干嘛的。
writable
writable决定是否可以修改属性的值。
var obj = {};
Object.defineProperty(obj,"a",{
value: 1,
writable: false, //注意!不可写
configurable: true,
enumerable: true
})
obj.a; //1
obj.a = 2;
obj.a; //1
看到没有,我们通过Object.defineProperty()方法定义了属性a以及它的属性描述符。当属性描述符writable为false的时候,对该属性重新赋值是没有效果的。
上面代码是在非严格模式下的,如果在严格模式下面呢:
'use strict';
var obj = {};
Object.defineProperty(obj,"a",{
value: 1,
writable: false, //注意!不可写
configurable: true,
enumerable: true
})
obj.a = 2; //Uncaught TypeError: Cannot assign to read only property 'a' of object
语法错误,不可以修改只读的属性。
configurable
只要属性是可配置的,就可以使用defineProperty()方法修改属性描述符。
var obj = {a:2};
obj.a = 3;
obj.a; //3
Object.defineProperty(obj,"a",{
value: 4,
writable: true,
configurable: false, //不可配置
enumerable: true
});
obj.a; //4
obj.a = 5;
obj.a; //5(赋值是可以的,有writable控制)
Object.defineProperty(obj,"a",{
value: 6,
writable: true,
configurable: true,
enumerable: true
}); //TypeError: Cannot redefine property: a
注意:
- 即使属性是configurable: false,也可以把writable的状态由true改为false,但是无法由false改为true。
- 除了无法修改,configurable: false还会禁止删除这个属性:
var obj = {a:2};
obj.a; //2
delete obj.a;
obj.a; //undefined
Object.defineProperty(obj,"a",{
value: 2,
writable:true,
configurable: false,
enumerable: true
});
obj.a; //2
delete obj.a;
obj.a; //2,没有删除该属性
enumerable
控制属性是否会出现在对象的属性枚举中。
var obj = {};
Object.defineProperty(obj,"a",{
value: 2,
enumerable: true
});
Object.defineProperty(obj,"b",{
value: 3,
enumerable: false
});
obj.b; //3,虽然不可枚举,但是可以访问
("b" in obj); //true
obj.hasOwnProperty("b"); //true
for(var k in obj) {
console.log(k, obj[k]); //"a" 2
}
注意:for..in不要用于数组。因为in可以检查是否有某个属性值,如果一个数组含有其他属性(当然给数组添加属性不常见),遍历数组的话,和你想象的不一样:
var arr = [1,2,3];
arr.a = 1;
for(var k in arr)
console.log(k, arr[k]);
//0 1
//1 2
//2 3
//a 1
我们也可以通过另一种方式来区分属性是否可以枚举:
var obj = {};
Object.defineProperty(obj,"a",{
value: 2,
enumerable: true
});
Object.defineProperty(obj,"b",{
value: 2,
enumerable: false
});
obj.propertyIsEnumerable("a"); //true
obj.propertyIsEnumerable("b"); //false
Object.keys(obj); //["a"]
Object.getOwnPropertyNames(obj); //["a", "b"]
- propertyIsEnumerable()会检查给定的属性名是否直接存在于对象中(而不是在原型链上),并满足enumerable: true。
- Object.keys()和Object.getOwnPropertyNames()都只会查找对象直接包含的属性。
- Object.keys()会返回一个数组,包含所有可枚举属性。
- Object.getOwnPropertyNames()会返回一个数组,包含所有属性,无论它们是否可枚举。
- in和hasOwnProperty()的区别在于是否查找原型链
Object.prototype.a = 1;
var obj = {b: 2};
for(var k in obj) {
console.log(k); //b, a
}
可以看到,in在查找对象属性时遍历原型链上的所有属性。