数据属性我们可以理解为我们平时定义对象时赋予的属性,它可以进行读和写。但是,ES5中定义了一些特性,这些特性是用来描述属性的各种特征,特性是内部值,不能直接访问到。特性通过用两对方括号表示,比如[[Enumerable]]。属性的特性会有一些默认值,要修改特性的默认值,必须使用ES5定义的新方法Object.defineProperty方法来修改。
数据属性有4个描述其特征的特性,下面将依次说明每一个特性:
(1)[[Configurable]]:该特性表示是否可以通过delete操作符来删除属性,默认值是true。
var obj = {}; obj.name = "myname"; delete obj.name; console.log(obj.name);//undefined
这段代码很明显,通过delete删除了obj的name属性后,我们再访问name属性就访问不到了。
我们通过Object.defineProperty方法来修改[[Configurable]]特性。
var obj = {}; obj.name = "myname"; Object.defineProperty(obj, "name", { configurable: false }) delete obj.name; console.log(obj.name);//myname
通过将configurable特性设置成false之后,delete就无法删除name属性了,如果在严格模式下,使用delete去删除就会报错。
(2)[[Enumerable]]:表示是否能够通过for…in语句来枚举出属性,默认是true
我们来看看前面的例子:
var obj = { name: "objName" } for (var i in obj) { console.log(i);//name }
这段代码只输出了name属性,我们来将constructor属性的[[Enumerable]]设置为true试试。
var obj = { name: "objName" } Object.defineProperty(obj, "constructor", { enumerable: true }) for (var i in obj) { console.log(i);//name,constructor } console.log(obj.propertyIsEnumerable("constructor"));//true
这段代码中,for…in循环得到了name和constructor两个属性,而通过propertyIsEnumerable方法来判断constructor也返回了true。
(3)[[Writable]]:表示属性值是否可以修改,默认为true
如果[[Writable]]被设置成false,尝试修改时将没有效果,在严格模式下会报错
(4)[[Value]]:表示属性的值,默认为undefined
我们通过一个简单的例子来看看这两个特性:
var obj = { name: "name" }; console.log(obj.name);//name Object.defineProperty(obj, "name", { value: "newValue", writable: false }) console.log(obj.name);//newValue obj.name = "oldValue"; console.log(obj.name);//newValue
我们首先定义了obj对象的name属性值为“name”,然后通过defineProperty方法来修改值,并且将其设置为不可修改的。接着我们再修改name属性的值,可以发现修改无效。
如果我们通过defineProperty来修改name属性的值,是否可以修改呢?答案是可以的:
Object.defineProperty(obj, "name", { value: "oldValue" }) console.log(obj.name); //oldValue
访问器属性
访问器属性有点类似于C#中的属性,和数据属性的区别在于,它没有数据属性的[[Writable]]和[[Value]]两个特性,而是拥有一对getter和setter函数。
[[Get]]:读取属性时调用的函数,默认是undefined
[[Set]]:设置属性时调用的函数,默认是undefined
getter和setter是一个很有用的东西,假设有两个属性,其中第二个属性值会随着第一个属性值的变化而变化。这种场景在我们平时的编码中起始是非常常见的。在之前的做法中,我们往往要去手动修改第二个属性的值,那现在我们就可以通过get和set函数来解决这个问题。看下面这个例子:
var person = { age: 10 } Object.defineProperty(person, "type", { get: function () { if (person.age > 17) { return "成人"; } return "小孩"; } }) console.log(person.type);//小孩 person.age = 18; console.log(person.type);//成人
通过修改age的值,type的值也会相应的修改,这样我们就不用再手动的去修改type的值了。
下面这种方式也是可以实现同样的效果:
var person = { _age: 10, type: "小孩" } Object.defineProperty(person, "age", { get: function () { return this._age; }, set: function (newValue) { this._age = newValue; this.type = newValue > 17 ? "成人" : "小孩"; } }) console.log(person.type); person.age = 18; console.log(person.type);
关于访问器属性,有几点要注意:
1、严格模式下,必须同时设置get和set
2、非严格模式下,可以只设置其中一个,如果只设置get,则属性是只读的,如果只设置set,属性则无法读取
3、Object.defineProperty是ES5中的新方法,IE9(IE8部分实现,只有dom对象才支持)以下浏览器不支持,一些旧的浏览器可以通过非标准方法defineGetter()和defineSetter()来设置,这里就不说明了,有兴趣的同学可以查找相关资料。
特性操作的相关方法
ES5提供了一些读取或操作属性特性的方法,前面用到的Object.defineProperty就是其中之一。我总结了一些比较常用的方法如下:
(1)Object.defineProperty
定义一个对象的属性,这个方法前面我们已经用到多次,简单说说其用法。
复制代码 代码如下:
Object.defineProperty(obj,propName,descriptor);