一个键值对在java里是什么类型_将一个键值对添加入一个对象


对象是JavaScript的基础。

JavaScript中的一等公民函数就是对象的一个子类型,可以视为可调用的对象。

数组是对象的一种类型。

基本类型string、number、boolean都有对应的包装对象String、Number、Boolean。

除此之外,JavaScript中还有一些内置对象:Date、Error、Regexp。

...

在JavaScript中,对象可以说是无处不在,就连JavaScript中的“继承”,本质上也是两个对象通过原型链共享信息。

接下来准备用几篇文章介绍一些对象的基本特性。这篇文章,我们先来了解一下对象的属性描述符。

1.属性描述符

在ES5之前,JavaScript语言本身并没有提供可以直接检测属性特性的方法,比如判断属性是否是只读。

从ES5开始,添加了对对象属性描述符的支持。现在JavaScript中支持4种属性描述符:

  • configurable: 当且仅当该属性的configurable键值为true时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
  • enumerable: 当且仅当该属性的enumerable键值为true时,该属性才会出现在对象的枚举属性中。
  • value: 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
  • writable: 当且仅当该属性的writable键值为true时,属性的值,也就是上面的value,才能被赋值运算符改变。

在JavaScript中可以通过Object.defineProperty()来为对象设置属性描述符。

  1. writable
var obj = {};
Object.defineProperty(obj,'foo', {
  value: 1,
  writable: false,
  configurable: true
});
obj; //{foo: 1}
obj.foo = 2;//严格模式会报错
obj; //{foo: 1}


通过属性描述符给对象obj定义了属性foo的value为2,所以obj.foo的值为2。writable被设置为false,我们再尝试修改对象obj.foo的时候,发现对象的值没有变化,这就是属性描述符writable:false起到了作用。

2. configurable

只要configurabletrue,我们就可以用defineProperty修改属性描述符:


...
//通过defineProperty将writable改为true
Object.defineProperty(obj,'foo', {
  writable: true
});

obj.foo = 2;
obj.foo; //2, foo的值成功被修改


但是如果我们将configurable设置成false:


...
Object.defineProperty(obj,'foo', {
  writable: false
});
Object.defineProperty(obj,'foo', {
  configurable: false
});
Object.defineProperty(obj,'foo', {
  writable: true
}); //TypeError


上面代码我们先将writable设置为false,然后将configurable设置为false,然后我们再尝试将writable设置为true,这时产生了一个TypeError。

不管是不是处于严格模式,尝试修改一个不可配置的属性描述符都会出错。

那么如果我们想将configurable重新设置为true呢?


...
Object.defineProperty(obj,'foo', {
  configurable: false
}); //TypeError


上面代码直接报错,由此可见:把configurable修改成false是单向操作,无法撤销!

注意:即便属性是configurable:false,我们还是可以把writable和enumerable的状态由true改为false,但是无法由false改为true。

configurable:false还会禁止删除这个属性:


delete obj.foo; //false  (严格模式下报错)
obj; // {foo: 2}


3. enumerable

这个描述符控制的是属性是否会出现在对象的属性枚举中,比如说for..in循环。如果把enumerable设置成false,这个属性就不会出现在枚举中:


var obj = {foo: 1};
for(var key in obj){//进入一次循环,输出foo
 console.log(key);
}

Object.defineProperty(obj,'foo', {
  enumerable: false
});

for(var key in obj){//没有进入循环
 console.log(key);
}


如果想要查看对象某个属性的属性描述符,可以使用Object.getOwnPropertyDescriptor


var obj = {
  foo: 1
};
Object.getOwnPropertyDescriptor(obj, 'foo');
//输出:{value: 1, writable: true, enumerable: true, configurable: true}


2. 不变性

有时候你会希望属性或者对象是不可改变的,在ES5中可以通过很多种方法来实现。

  1. 对象常量

结合writable:false和configurable:false就可以创建一个真正的常量属性(不可修改、重定义或者删除):


var obj = {};
Object.defineProperty(obj,'foo', {
  value: 1,
  writable:false,
  configurable:false
});


2. 禁止扩展

如果你想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions


var obj = {
  foo: 1
};
Object.preventExtensions(obj)

obj.bar = 2;
obj.bar; //undefined, 无法创建新的属性

Object.getOwnPropertyDescriptor(obj, 'foo');
//输出:{value: 1, writable: true, enumerable: true, configurable: true}
//说明preventExtensions不会修改属性描述符


在非严格模式下,创建属性bar会静默失败。在严格模式下,将会抛出TypeError错误。

3. 密封

使用Object.seal可以创建一个“密封”的对象。

实际上会在一个现有对象上调用Object.preventExtensions并把所有现有属性标记为configurable:false

密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性,但是可以修改属性值。


...
Object.defineProperty(obj,'foo', {
  enumerable: false
});

Object.seal(obj);

Object.defineProperty(obj,'foo', {
  enumerable: true
});//TypeError, 无法修改属性描述符

obj.foo = 2;
obj.foo; //2, 可以修改属性值

Object.getOwnPropertyDescriptor(obj, 'foo');
//输出:{value: 2, writable: true, enumerable: true, configurable: false}
//configurable被设置为false


4.冻结

使用Object.freeze可以创建一个冻结对象。

实际上会在一个现有对象上调用Object.seal并把所有“数据访问”属性标记为writable:false,这样就无法修改它们的值。


...
Object.freeze(obj);
obj.foo = 3;
obj.foo; //2, 冻结之后无法修改属性值
Object.getOwnPropertyDescriptor(obj, 'foo');
//输出:{value: 2, writable: false, enumerable: true, configurable: false}
//writable被设置为false


这个方法是可以应用在对象上的级别最高的不可变性,它会禁止对于对象本身及其任意直接属性的修改。