属性的分类与获取
可枚举属性
由属性的内部特性[[Enumerable]]决定。它表示属性是否可以通过for-in循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是true。如:
let obj = {name:"test",age:23}
for(property in obj){
console.log(property,obj[property]);
}
//name test
//age 23
1. 用方括号或者点运算符
obj.name; //直接用属性名
obj['age']; //注意方括号内是字符串类型的属性名,可以是一个字符串变量
2. for-in 如上面的代码
for-in遍历的是对象的属性名,与for-of不一样,后者是遍历数组。
let arr = ['javascript','css','html']
for(let k in arr){
console.log(k);
}
//0,1,2
for(let v of arr){
console.log(v);
}
//'javascript','css','html'
这跟一般对象的结构很相似,但是不能这样访问arr.0。
3. Object方法
for(let k of Object.keys(obj)){
console.log(k);
}
//name
//age
for(let v of Object.values(obj){
console.log(v);
}
//test
//123
for(let [k,v] of Object.entries(obj)){
console.log(k,v);
}
//name test
//age 123
Object.keys(),Object.values(),Object.entries()返回的都是数组,所以要用数组解构。
4. 知道属性名可以进行解构获取属性值
5. Object.getOwnPropertyNames()
这个函数返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
于是可以迭代出所有的属性值。
Object.defineProperties(obj, {
'name': {
value: 'test',
writable: true
},
'age': {
value:123,
writable: false
},
'sex': {
value: 'male',
enumerable:false
},
'sayHello': {
value: ()=>console.log('Hello'),
writable: false,
}
});
console.log(Object.getOwnPropertyNames(obj));
//['name', 'age', 'sex', 'sayHello']
既然能获得键名,自然也能获取到属性值了。
不可枚举属性
需要用Object.defineProperty()或Object.defineProperties()
去定义。
let person = {};
Object.defineProperty(person, "name", {
enumerable:false,
value: "Nicholas"
});
console.log(person.name); // "Nicholas"
for( p in person){
console.log(p,person[p]);
}
//无输出
知道属性名
person['name']
person.name
let {name} = person; //解构
不知道属性名:Object.getOwnPropertyNames()
function getOwnNonEnumerableProperties(obj){
if(!obj || typeof obj !== 'object') return null;
//获取自身的不可枚举的属性
let nonEnumerableKeys = Object.getOwnPropertyNames(obj);
let result = [];
for(let key of nonEnumerableKeys){
let descriptor = Object.getOwnPropertyDescriptor(obj,key);
if(descriptor && !descriptor.enumerable){
let value = descriptor.value;
result.push({key,value});
}
}
return result;
}
o = {};
Object.defineProperty(o, "baz", {
value: 8675309,
writable: false,
enumerable: false
});
console.log(getOwnNonEnumerableProperties(o));
//[ { key: 'baz', value: 8675309 } ]
继承属性
即属性不是实例本身定义的,而是从其原型链上获取的。
如何定义属性
Object.defineProperty()
//Object.defineProperty()
var o = {}; // 创建一个新对象
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象 o 拥有了属性 a,值为 37
// 在对象中添加一个设置了存取描述符属性的示例
var bValue = 38;
Object.defineProperty(o, "b", {
// 使用了方法名称缩写(ES2015 特性)
// 下面两个缩写等价于:
// get : function() { return bValue; },
// set : function(newValue) { bValue = newValue; },
get() { return bValue; },
set(newValue) { bValue = newValue; },
enumerable : true,
configurable : true
});
o.b; // 38
// 对象 o 拥有了属性 b,值为 38
// 现在,除非重新定义 o.b,o.b 的值总是与 bValue 相同
// 数据描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 0x9f91102,
get() { return 0xdeadbeef; }
});
// 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
Object.defineProperties()
let book = {};
Object.defineProperties(book, {
year_: {
value: 2017
},
edition: {
value: 1
},
year: {
get() {
return this.year_;
},
set(newValue) {
if (newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
}
});
直接赋值
obj.newProperty = "blue";
obj['newPropertyplus'] = "tall"
获取属性的方法封装
获取所有属性名,自身的和原型链上的
function getAllPropertyNames( obj ) {
var props = [];
do {
Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {
if ( props.indexOf( prop ) === -1 ) {
props.push( prop );
}
});
} while ( obj = Object.getPrototypeOf( obj ) );
return props;
}
console.log(getAllPropertyNames(['a','b','c']));
//打印结果
[
'0', '1', '2',
'length', 'constructor', 'concat',
'copyWithin', 'fill', 'find',
'findIndex', 'lastIndexOf', 'pop',
'push', 'reverse', 'shift',
'unshift', 'slice', 'sort',
'splice', 'includes', 'indexOf',
'join', 'keys', 'entries',
'values', 'forEach', 'filter',
'flat', 'flatMap', 'map',
'every', 'some', 'reduce',
'reduceRight', 'toLocaleString', 'toString',
'at', '__defineGetter__', '__defineSetter__',
'hasOwnProperty', '__lookupGetter__', '__lookupSetter__',
'isPrototypeOf', 'propertyIsEnumerable', 'valueOf',
'__proto__'
]
获取自身的不可枚举的属性
function getOwnNonEnumerableProperties(obj){
if(!obj || typeof obj !== 'object') return null;
let nonEnumerableKeys = Object.getOwnPropertyNames(obj);
let result = [];
for(let key of nonEnumerableKeys){
let descriptor = Object.getOwnPropertyDescriptor(obj,key);
if(descriptor && !descriptor.enumerable){
let value = descriptor.value;
result.push({key,value});
}
}
return result;
}
o = {};
Object.defineProperty(o, "baz", {
value: 8675309,
writable: false,
enumerable: false
});
console.log(getOwnNonEnumerableProperties(o));
获取自身的所有属性
function getOwnAllProperties(obj){
if(!obj || typeof obj !== 'object') return null;
let nonEnumerableKeys = Object.getOwnPropertyNames(obj);
let result = [];
for(let key of nonEnumerableKeys){
let descriptor = Object.getOwnPropertyDescriptor(obj,key);
let value = descriptor.value;
result.push({key,value});
}
return result;
}
console.log(getOwnAllProperties(['a','b','c']));
//打印结果
[
{ key: '0', value: 'a' },
{ key: '1', value: 'b' },
{ key: '2', value: 'c' },
{ key: 'length', value: 3 }
]
按内部特性分类
数据属性
含有的内部特性为:
[[Configurable]]
:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这特性都是 true。[[Enumerable]]
:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。[[Writable]]
:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。[[Value]]
:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为 undefined 。
访问器属性
含有的内部特性为:
[[Configurable]]
:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。[[Enumerable]]
:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。[[Get]]
:获取函数,在读取属性时调用。默认值为 undefined 。[[Set]]
:设置函数,在写入属性时调用。默认值为 undefined 。
数据属性和访问器属性的内部属性不能混在一起,比如既设置了[[Value]]又设置[[Get]]。