前言
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo');
显示结果如下图所示:
描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。
目前,有两种遍历对象属性的方法对于enumerable值不做过滤,即可遍历对象自身的所有属性(可枚举属性和不可枚举属性)其分别是:
- Object.getOwnPropertyNames(obj)【遍历对象自身的所有属性,不包括Symbol属性】
- Reflect.ownKeys(obj)【遍历对象自身的所有属性,包括对象的Symbol属性】
有四种遍历对象属性的方法会忽略enumerable为false的属性,即仅对对象所包含的可枚举类型的属性进行遍历,其分别为:
- for…in【遍历对象自身的和继承的可枚举属性,不遍历对象的Symbol属性】
- Object.keys()【遍历对象自身的可枚举的属性,不遍历对象的Symbol属性】
- JSON.stringify()【遍历对象自身的可枚举的属性,不遍历对象的Symbol属性】
- Object.assign()【遍历对象自身的可枚举的属性,包括Symbol属性】
对需要学习遍历对象所有属性(对enumerable值不做过滤)的方法,几种遍历一个对象包含的所有属性的方法详解 一文可以帮你答疑解惑。
一、for…in循环【自身的和继承的可枚举属性,不包括Symbol】
只遍历对象自身的和继承的可枚举的属性(不遍历对象的Symbol属性)。
案例一
function Obj(){
this.foo = 123;
this.get = function(){
console.log(this.foo);
}
};
Obj.prototype.org = "Obj-prototype-org";//org为Obj原型对象的属性
console.log("Obj.org="+Obj.org+" Obj.prototype.org="+Obj.prototype.org);
var objSon = new Obj();
objSon.name = "objSon";
Object.defineProperty(objSon, 'name', { //设置objSon对象的name 不可枚举
enumerable: false
});
console.log("objSon自身的所有属性(包括可枚举和不可枚举属性)为:"+Reflect.ownKeys(objSon)+"\n"+"objSon自身的和继承的可枚举属性如下所示:");
(function(){
for(key in objSon){
console.log("---------------------------------"+key+"---------------------------------");
if(objSon.hasOwnProperty(key)){//判断当前属性是否为objSon的自身属性,只有自身属性才能获取对应的枚举类型
console.log("objSon的"+key+"属性是自身的吗?"+objSon.hasOwnProperty(key));
console.log("objSon."+key+".enumerable="+Object.getOwnPropertyDescriptor(objSon, key).enumerable);
}else if(Obj.hasOwnProperty(key)){
console.log("Obj的"+key+"属性是自身的吗?"+Obj.hasOwnProperty(key));
console.log("Obj."+key+".enumerable="+Object.getOwnPropertyDescriptor(Obj, key).enumerable);
}else if(Obj.prototype.hasOwnProperty(key)){
console.log("objSon继承Obj.prototype的属性。-----Obj.prototype的"+key+"属性是自身的吗?"+Obj.prototype.hasOwnProperty(key));
console.log("Obj.prototype."+key+".enumerable="+Object.getOwnPropertyDescriptor(Obj.prototype, key).enumerable);
}
}
})();
显示结果如下图所示:
由上图可知,for…in方法只能遍历自身的和继承的可枚举属性,对于不可枚举属性不能遍历。
案例二
var obj = {
name: 'aaa',
age: 'bbb',
say: 'ccc',
[Symbol("id")]: 'ddd' //设置Symbol属性
}
Object.defineProperty(obj, 'say', { //设置obj对象的say不可枚举
enumerable: false
});
(function(){
for(key in obj){
console.log(key+"\t\t\t\t"+key+".enumerable="+Object.getOwnPropertyDescriptor(obj, key).enumerable+"\t\t\t\t"+'result='+obj[key]);
}
})();
显示结果如下图所示:
由上图可知,for…in可遍历自身和继承而来的可枚举属性,对于Symbol类型的属性遍历不到。
二、Object.keys()【自身的可枚举属性,不包括Symbol】
Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object自身可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。即返回对象自身的所有可枚举的属性。
案例一
var obj = {
name: 'aaa',
age: 'bbb',
say: 'ccc',
[Symbol("id")]: 'ddd' //设置Symbol属性
}
Object.defineProperty(obj, 'say', { //设置obj对象的say不可枚举
enumerable: false
});
console.log("obj所有的自身属性有:");
console.log(Reflect.ownKeys(obj));
console.log("Object.keys(obj)可遍历的自身属性有:");
Object.keys(obj).forEach(function(key){
console.log(key+"\t\t\t\t"+key+".enumerable="+Object.getOwnPropertyDescriptor(obj, key).enumerable+"\t\t\t\t"+"result="+obj[key]);
});
显示结果如下图所示:
由上图结果可知:Object.keys(obj)仅可遍历对象自身的可枚举属性,对于Symbol属性遍历不到。
三、JSON.stringify()【自身的可枚举属性,不包括Symbol】
JSON.stringify() 方法将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串,其只串行化对象自身的可枚举的属性。
案例一
var obj = {
name: 'aaa',
age: 'bbb',
say: 'ccc',
[Symbol("id")]: 'ddd' //设置Symbol属性
}
Object.defineProperty(obj, 'say', { //设置obj对象的say不可枚举
enumerable: false
});
console.log("obj所有的自身属性有:");
console.log(Reflect.ownKeys(obj));
console.log("Object.keys(obj)可遍历的自身属性有:"+JSON.stringify(obj));
显示结果如下图所示:
由上图可知,JSON.stringify(obj)是将一个对象的可枚举属性以及属性对应的值按照属性顺序以json字符串的形式输出。
与for…in以及Object.keys(obj)方法输出结果形式不同的是其输出结果为字符串,而非数组,且同时输出了对应可枚举属性的值。
四、Object.assign()【ES6新增】【自身的可枚举属性和Symbol属性】
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
它将返回目标对象。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。即会忽略enumerable为false的属性,只拷贝每个对象自身的可枚举的属性。
String类型和 Symbol 类型的属性都会被拷贝。
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
案例一
var obj = {//源对象
name: 'aaa',
age: 'bbb',
say: 'ccc',
[Symbol("id")]: 'ddd' //设置Symbol属性
}
Object.defineProperty(obj, 'say', { //设置obj对象的say不可枚举
enumerable: false
});
var target = { a: 1, b: 2 };//目标对象
var source = { b: 4, c: 5 };//源对象
console.log(Object.assign(target,source,obj),target);
显示结果如下图所示:
由上图结果可知:Object.assign(target,source)方法会直接对第一个参数(目标数组)进行修改,将第一个参数后边的所有参数对象(可以有一个或多个原对象)中的可枚举属性或者Symbol属性拷贝到目标数组中,最后返回目标数组。【对于元素属性为基本类型的属于深拷贝,对于属性为引用类型的属于浅拷贝,js中的深拷贝和浅拷贝详解 一文详细介绍了深拷贝和浅拷贝】
五、总结
Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
对需要学习遍历对象所有属性(对enumerable值不做过滤)的方法,几种遍历一个对象包含的所有属性的方法详解 一文可以帮你答疑解惑。
以上7种方法遍历对象的属性时遵循同样的属性遍历次序规则。
- 首先遍历所有属性名为数值的属性,按照数字排序。
- 其次遍历所有属性名为字符串的属性,按照生成时间排序。
- 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
另外,ES6 规定,所有 Class 的原型的方法都是不可枚举的。
总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for…in循环,而用Object.keys()代替。
初出茅庐,若稍有差池,还望不吝赐教!