大家都知道循环一个对象,可以用for ..in..
来做,比如:
var myObject = {foo: 'bar'};
for(var name in myObject) {
// 这里能得到你想要的属性名
// 也可以得到对应属性值: myObject[propertyName]
}
这是显而易见的,但是如果有人在这个对象的原型上添加了一些属性或方法呢?
Object.prototype.baz = 'quux';
for (var name in myObject) {
alert(name); //foo baz
}
myObject
也会跟着改,那么别人改动原型,对自己的代码造成很大的影响,结果肯定是不能接受的。
既然对象存在继承,那么我们必须考虑哪些是我自己写的(私有的),哪些是从原型上继承的,单纯的for ..in..
循环已经不能满足需求 了。
hasOwnProperty
就是用来判断是否是私有属性,而不是继承过来的
myObject.hasOwnProperty('foo'); // true
myObject.hasOwnProperty('baz'); // false
如果仅仅想要私有的属性或方法,我们就可以这么写了
for(var name in myObject) {
if(myObject.hasOwnProperty(name)){
alert(name);// 'foo'
}
}
for...in..
循环,中间还要做判断,是不是很麻烦,不过我们有专门的方法来解决这些麻烦事。
Object.keys(myObject);//['foo'] 返回一个对象,是一个私有属性的集合
说到这里,肯定还有一些人会疑惑,既然for...in
能遍历出对象自身的属性或方法,也能遍历出继承过来的属性或方法,那么Object
类上还有很多的属性和方法的,比如说toString
,这是被myObject
继承的,但是没有被for...in
遍历到啊。
'toString' in myObject; //true
myObject.toString();//"[object Object]"
这里引入一个概念“可枚举”enumerable
,这个概念的最初目的,就是让某些属性可以规避掉for...in
操作,不然所有内部属性和方法都被遍历到,就可以放肆的更改了。
写到这里,你应该会明白了,上面提到的toString
都是不可枚举的。你会不会有这样的问题?如果我在对象里面写了一些属性,但是不想被遍历出来,是不是也可以把这些属性变成不可枚举的呢?当然是可以的了。
Object.defineProperty(myObject, 'name', {
value : 'cover',
enumerable: false//不可枚举
})
Object.prototype.baz = 'quux';
// myObject:{foo: "bar", name: "cover"}
for (var name in myObject) {
alert(name); //foo baz
}
通过这种方法定义出来的属性或方法就可以变成不可枚举。
新的知识点:Object.defineProperty
,顾名思义,就是为对象定义属性。
定义:直接在一个对象上定义一个新的属性,或者是修改已存在的属性。最终这个方法会返回该对象。
参数
-
object
必需。 要在其上添加或修改属性的对象。 这可能是一个本机JavaScript
对象(即用户定义的对象或内置对象)或DOM
对象。 -
propertyname
必需。 一个包含属性名称的字符串。 -
descriptor
必需。 属性描述符。 它可以针对数据属性或访问器属性。
其中descriptor
的参数值是我们需要重点关注的,该属性可设置的值有:
- [
value
] 属性的值,默认为undefined - [
writable
] 该属性是否可写,如果设置成 false,则任何对该属性改写的操作都无效(但不会报错),对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为true
。
Object.defineProperty(myObject, 'age', {
value : 18,
writable:false
});
myObject.age =19;
console.log(myObject.age);//18;
- [
configurable
]如果为false
,则任何尝试删除目标属性或修改属性的行为将被无效化,对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为true
。
Object.defineProperty(myObject, "name", {
value:"gogo" ,
configurable: false
});
delete myObject.name;
console.log(myObject.name);// 输出 gogo
myObject.name = "gg";
console.log(myObject.name); // 输出gogo
- [
enumerable
] 是否能在for-in循环中遍历出来或在Object.keys中列举出来。对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true。
在调用
Object.defineProperty()
方法时,如果不指定,configurable
,enumerable
,writable
特性的默认值都是false
,这跟之前所 说的对于像前面例子中直接在对象上定义的属性,这个特性默认值为为true
。并不冲突,如下代码所示:
//调用Object.defineProperty()方法时,如果不指定
var someOne = { };
someOne.name = 'coverguo';
console.log(Object.getOwnPropertyDescriptor(someOne, 'name'));
//输出 Object {value: "coverguo", writable: true, enumerable: true, configurable: true}
//直接在对象上定义的属性,这个特性默认值为为 true
var otherOne = {};
Object.defineProperty(otherOne, "name", {
value:"coverguo"
});
console.log(Object.getOwnPropertyDescriptor(otherOne, 'name'));
//输出 Object {value: "coverguo", writable: false, enumerable: false, configurable: false}
Object.getOwnPropertyDescriptor
方法会返回某个对象属性的描述对象(descriptor
)。ES2017 引入了Object.getOwnPropertyDescriptors
方法,返回指定对象所有自身属性(非继承属性)的描述对象。
- [
get
] 修改器,一旦目标对象访问该属性,就会调用这个方法, 并返回结果。默认为undefined
。 - [
set
] 获取器,一旦目标对象设置该属性,就对调用这个方法。默认为undefined
其实,在vue
中就用到了Object.defineProperty
方法,主要用于双向数据绑定,下一篇文章用讲到,(请勿拍砖。。)
扯得有点远了,我们再回来看,如何判断对象上的属性是否可枚举?
obj.propertyIsEnumerable(prop)
Object.defineProperty(myObject, "name", {
value:"gogo" ,
configurable: false ,
enumerable:false,
});
myObject.age =18
myObject.propertyIsEnumerable('name'); //true
myObject.propertyIsEnumerable('age');//true
另外介绍一个方法:
Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。是不是和Object.keys(obj)
有点像呢?
最后,我们再来总结一下,有四个操作会忽略enumerable
为false
的属性。
-
for...in
循环:只遍历对象自身的和继承的可枚举的属性。 -
Object.keys()
:返回对象自身的所有可枚举的属性的键名。 -
JSON.stringify()
:只串行化对象自身的可枚举的属性。 -
Object.assign()
: 忽略enumerable
为false
的属性,只拷贝对象自身的可枚举的属性。