前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈JavaScript 数据属性和访问器属性

浅谈JavaScript 数据属性和访问器属性

作者头像
李才哥
发布2019-07-10 09:33:27
1.3K0
发布2019-07-10 09:33:27
举报
文章被收录于专栏:李才哥李才哥
在JavaScript中对象被定义为"无序属性的集合,其属性可以包含基本值、对象或函数。"通俗点讲,我们可以把对象理解为一组一组的名值对,其中值可以是数据或函数。

创建自定义对象通常有两种方法,第一种就是创建一个Object的实例,然后再为其添加属性和方法,例如:

?

代码语言:javascript
复制
var person = new Object();
person.name = "Scott";
person.age = 24;
person.sayName = function(){ 
 alert(person.name);
}

第二种方法即对象字面量法,一般推荐使用这种方法创建对象,例如:

?

代码语言:javascript
复制
var person = {
 name: "Scott",
 age: 24,
 sayName: function(){
    alert(this.name);  
 }
}

在javaScript中,对象的属性分为两种类型:数据属性和访问器属性。

一、数据属性

1.数据属性:它包含的是一个数据值的位置,在这可以对数据值进行读写。

2.数据属性包含四个特性,分别是:

configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为true

enumerable:表示能否通过for-in循环返回属性

writable:表示能否修改属性的值

value:包含该属性的数据值。默认为undefined

如下面这个例子:创建一个对象person,打印出name属性的特性的默认值

执行结果:

对几个特性的测试:

测试结果:

3.修改数据属性的默认特性

修改属性属性的默认特性要用到一个方法:Object.defineProperty()方法,这个方法有三个参数:属性所在的对象,属性名,一个描述符对象。

通过这个方法,我们可以来修改一个属性的这4个特性。

如我们对刚刚上面的penson对象里面的name属性的特性进行修改:

执行结果:

结果中可以看到,person对象的name属性中的四个特性的值都相应改变了。同时后面的报错是对configurable这个特性改为false后的局限的测试。

上面的注释中是分别对各个属性修改后的影响的测试。大家可以自己运行下试试效果。

属性类型

JavaScript中定义了两种不同的属性:数据属性和访问器属性。数据属性一般用于存储数据数值,而访问器属性一般进行get/set操作,不能直接存储数据数值。在ES5中,我们为了描述属性(property)的各种特征,定义了特性(attribute)。在JavaScript中不能直接访问特性,我们把它放在两对方括号中,例如[[Enumerable]]。

•数据属性

数据属性主要有四个特性描述其行为:

1.[[Configurable]]:默认为true。表示能否通过delete删除属性从而重新定义属性,能否修改属性特性,或者能否把属性修改为访问器属性;

2.[[Enumerable]]:默认为true。表示能否通过for-in循环返回属性;

3.[[Writable]]:默认为true。表示能否修改属性的值。

4.[[Value]]:默认值为undefined。表示包含属性的数据值。读写属性值都从这个位置进行。

对于上面直接在person对象上定义的属性,它们的[[Configurable]]、[[Enumerable]]、[[Writable]]特性都被默认设置为true,而[[Value]]特性被设置为特定值。如果想要修改属性默认的特性,可以使用ES5提供的Object.defineProperty()方法,这个方法接收三个参数:属性所在对象、属性的名字和一个描述符对象。描述符对象只能包含上述四个特性的一个或多个。例子如下:

?

代码语言:javascript
复制
var person = {
 name: "Scott"
}
Object.defineProperty(person,"name",{ 
 writable:false;
})
console.log(person.name);  //"Scott" 
person.name = "Evan";
console.log(person.name);  //"Scott"

将person对象name属性的特性writable设置为false,此属性的值为不可修改的,因此对该属性的复制操作会直接忽略。

?

代码语言:javascript
复制
var person = {
 name: "Scott"
}
Object.defineProperty(person,"name",{ 
 configurable:false;
})
console.log(person.name);  //"Scott" 
delete person.name;
console.log(person.name);  //"Scott"

可以看到,当把name属性的特性值configurable设置为false之后,就表示不能从对象中删除属性。但需要注意的是,当把属性定义为不可配置之后,就不能把它变回可配置的了。此时修改除writable之外的其它特性都会报错,例如:

?

代码语言:javascript
复制
var person = {
 name: "Scott"
}
Object.defineProperty(person,"name",{
 configurable:false;
})
Object.defineProperty(person,"name",{
 configurable:true;  //此处会抛出错误 
})

也就是说,在把configurable特性修改为false之后,再修改其它特性就会有限制存在。

二、访问器属性

1.访问器属性:这个属性不包含数据值,包含的是一对get和set方法,在读写访问器属性时,就是通过这两个方法来进行操作处理的。

2.访问器属性包含的四个特性

configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为false

enumerable:表示能否通过for-in循环返回属性,默认为false

Get:在读取属性时调用的函数,默认值为undefined

Set:在写入属性时调用的函数,默认值为undefined

这里要注意下,访问器属性不能直接定义,要通过Object.defineProperty()这个方法来定义。

下面来个例子,创建一个访问器对象book,接着打印出其year访问器属性的特性描述并对其方法进行测试打印:

执行结果:

其他两个特性configurable,enumerable的测试方式可以参照数据属性的。不过在这特别说明下,关于configurable这个特性,因为访问器属性里面这个

特性默认值为false,如果程序后面需要对该属性进行delete操作等,那就在定义访问器属性时,将这个特性设置为true,不然这个会导致后面一些报错的问题。

•访问器属性

访问器属性不包含数据值。它包含一对getter和setter函数。当读取访问器属性时,会调用getter函数并返回有效值;当写入访问器属性时,会调用setter函数并传入新值,setter函数负责处理数据。该属性有四个特性:

1.[[Configurable]]:默认为true。表示能否通过delete删除属性从而重新定义属性,能否修改属性特性,或者能否把属性修改为访问器属性;

2.[[Enumerable]]:默认为true。表示能否通过for-in循环返回属性;

3.[[Get]]:读取属性时调用的函数,默认为undefined;

4.[[Set]]:写入属性时调用的函数,默认为undefined。

访问器属性不能直接定义,必须通过Object.defineProperty()函数定义,例如:

?

代码语言:javascript
复制
var person = {
 _name: "Scott",
 _age: 24,
 _tel: 86247
};
//name属性为只读的
Object.defineProperty(person,"name",{
 get: function(){
   return this._name;
 }
});
//age属性为只写不可读的
Object.defineProperty(person,"age",{
 set: function(p){
    this._age = p;
 }
});
//tel属性为可读可写的
Object.defineProperty(person,"tel",{
 get:function(){
    return this._tel;
 },
 set: function(p){
    this._tel = p;
 }
});
console.log(person.name);  //"Scott"
person.name = "Evan";
console.log(person.name);  //"Scott",对name属性的修改无效 
console.log(person.age);  //undefined,不可读属性
person.age = 25;
console.log(person._age);  //25,已经修改
console.log(person.tel);  //"86247",可读属性
person.tel = "13975";
console.log(person.tel);  //"13975",可以修改

属性前面的下划线表示只能通过对象方法访问的属性。当我们使用person.name时实际上调用的是name属性的getter函数,为person.name赋值时调用的是name属性的setter函数,这样属性和访问器之间的关系就很清晰了。

定义多个属性

实际上ES5为我们提供了为一个对象定义多个属性的方法,即Object.defineProperties(),该函数接收两个参数,属性所在的对象以及需要修改的属性及其描述符对象组成的对象,例如把上边的例子修改为一次性定义多个属性,如下:

?

代码语言:javascript
复制
var person = {
 _name: "Scott",
 _age: 24,
 _tel: 86247
};
Object.defineProperties(person,{ 
 name:{
   get: function(){
     return this._name;
   }
 },
 age:{
   set: function(p){
     this._age = p;
   }
 },
 tel:{
   get:function(){
     return this._tel;
   },
   set: function(p){
     this._tel = p;
   }
 }
});

读取属性的特性

ES5提供了Object.getOwnPropertyDescripter()方法来获取给定属性的描述符。该方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。结果会返回一个对象,如果是访问器属性,返回的对象有configuable、enumerable、get和set;如果是数据属性,这个返回对象的属性包括configuable、enumerable、writable和value。对于上面的例如,使用如下:

?

代码语言:javascript
复制
var person = {
 _name: "Scott",
 _age: 24,
 _tel: 86247
};
Object.defineProperties(person,{
 name:{
   get: function(){
     return this._name;
   }
 },
 age:{
   set: function(p){
     this._age = p;
   }
 },
 tel:{
   get:function(){
     return this._tel;
   },
   set: function(p){
     this._tel = p;
   }
 }
});
var descripter = Object.getOwnPropertyDescripter(person,"tel"); 
console.log(descripter.value);  //undefined
console.log(descripter.enumerable);  //false
console.log(typeof descripter.get);  //"function"

上面的代码中获取了person对象的tel属性,由于其是一个访问器属性,所以其value为undefined,enumerable为false,而get为指向getter函数的一个指针。

理解对象

ECMAScript中有两种属性:数据属性和访问器属性

数据属性

示例1:

Nodejs 中

代码语言:javascript
复制
    console.log(global.undefined);//undefined
   global.undefined = 1;    console.log(global.undefined);//undefined

浏览器中

代码语言:javascript
复制
    console.log(window.undefined);//undefined
   window.undefined = 2;    console.log(global.undefined);//undefined

在js中一切皆对象,我们都知道全局属性undefined的值为undefined.那么属性就应该是可以修改,上边对全局属性undefined进行修改却没有修改成功,这是为什么呢?

示例2:

Nodejs 中

代码语言:javascript
复制
    var des = Object.getOwnPropertyDescriptor(global,'undefined');    console.log(des);

输出

代码语言:javascript
复制
    {      value: undefined,      writable: false,      enumerable: false,      configurable: false
   }

在浏览器中输出也应该一样.上面可以看到有四个属性,这就是javascript中的属性特性,它能设置属性的值,可编辑,可删除,可迭代特性。

  • Configurable

表示能够通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认值true。表示能否删除,如果是false,delete不起作用。在严格模式下报异常

  • Enumerable

表示能否通过for-in 循环返回属性。默认值true。false不可迭代

  • Writable

表示能否修改属性的值,默认是true。false不可修改

  • Value

包含这个属性的数据值,读取属性值的时候,从这个位置读;写入属性的时候把新值保存在这个位置,默认是undefined

要修改属性默认的特性,必须用ECMAScript5的Object.defineProperty(),接受三个参数,属性所在对象,属性名称,一个描述符对象。

示例3:

代码语言:javascript
复制
    var obj = {};    Object.defineProperty(obj,'name',{            'value' : 'name',            'writable' : false
   });    console.log(obj.name);//name
   obj.name = 'newname';    console.log(obj.name);//name

访问器属性

示例4:

代码语言:javascript
复制
    var book = {        _year: 2004,        edition: 1
   };    Object.defineProperty(book, "year", {        get: function () {            return this._year;
       },        set: function (newValue) {            if (newValue > 2004) {                this._year = newValue;                this.edition += newValue - 2004;
           }
       }
   });
   book.year = 2005;    console.log(book.edition);//2

访问器属性不包含数据值,它们包含一对getter和setter函数,(不过这两个函数都不是必须的),默认都是undefined.一般情况不用去定义setter和getter方法.找了很久也不知道在哪种场景适合用。上面这一种就是给一个属性赋值,导致另外一个属性变化,其实在代码中直接实现就可以.

上面代码需要注意的地方是,year用_year代替.前面加下划线是常用的用法,表示只能用对象方法访问的属性.如果用year回导致死循环赋值.

其他

另外还有 Object.definePropties可以批量为属性设置特性

在javascript中,对象的属性分为数据属性和存储器属性两种:

两种属性的区别

我们使用Object.defineProperty()先来直观的感受一下这两者的不同。

使用Object.defineProperty()对数据属性进行设置的方法如下

?

代码语言:javascript
复制
var obj = {};
Object.defineProperty(obj, "prop", {
value: 1,
writable: true, //可写性
enumerable: true, //可枚举性
configurable: true //设置该属性是否能被删除,以及enumerable属性是否可以被修改
})

使用Object.defineProperty()对存储器属性的设置方法如下

?

代码语言:javascript
复制
var obj = {};
Object.defineProperty(obj, "prop", {
get
set
enumerable: true, //可枚举性
configurable: true //设置该属性是否能被删除,以及enumerable属性是否可以被修改
})

从上面的例子中,我们观察到存储器属性并没有value和writable两个属性,取而代之的是set和get属性。

存储器属性

看完了数据属性和存储器属性直观上的差异,我们详细看一看存储器属性这个容易会被忽视(说的就是我TT)的属性。

存储器属性与数据属性最大的不同就是增加了getter/setter,通过它们可以对属性的值进行操作,可以实现一些实用的功能。

?

代码语言:javascript
复制
//example1
function serialnum() {
var n =1;
var prop = null;
Object.defineProperty(this, "n", {
get: function() {
return n;
},
set: function(value) {
if(value > n) n = value;
else throw '请输入一个大于n的值';
}
})
}
var obj = new serialnum();
obj.n = 2;
//2
obj.n = 0;
//Uncaught 请输入一个大于n的值

上面的例子中,使用set函数对n的取值范围进行控制。

代码语言:javascript
复制
function Emp(id,ename,salary,age){
    this.id=id;
  this.ename=ename;
  this.salary=salary;
  Object.defineProperties(this,{
    id:{writable:false},
    salary:{enumerable:false},
    _age:{
      writable:true,
      enumerable:false
    },
    age:{
     get:function(){return this._age;},
     set:function(val){
       if(val<18||val>65)
         throw new Error(...)
       this._age=val;   
     },
     enumerable:true
    }
  });
  this.age=age;
  //防篡改:
  Object.seal(this);//密封
} 
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 李才哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、数据属性
  • 二、访问器属性
  • 理解对象
    • 数据属性
      • 访问器属性
        • 其他
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档