深入了解JavaScript中的对象

大家都知道循环一个对象,可以用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()方法时,如果不指定, configurableenumerablewritable特性的默认值都是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'); //false
myObject.propertyIsEnumerable('age');//true
复制代码

另外介绍一个方法:

  • Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。是不是和Object.keys(obj)有点像呢?

最后,我们再来总结一下,有四个操作会忽略enumerablefalse的属性。

  • for...in循环:只遍历对象自身的和继承的可枚举的属性。
  • Object.keys():返回对象自身的所有可枚举的属性的键名。
  • JSON.stringify():只串行化对象自身的可枚举的属性。
  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值