详解JavaScript中的属性和特性


Posted in Javascript onDecember 08, 2016

JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性。

主要内容如下:

  • 理解JavaScript中对象的本质、对象与类的关系、对象与引用类型的关系
  • 对象属性如何进行分类
  • 属性中特性的理解 

第一部分:理解JavaScript中对象的本质、对象与类的关系、对象与引用类型的关系

对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。即对象是一组没有特定顺序的值,对象的每个属性或方法都有一个名字,而这个名字都映射到一个值。故对象的本质是一个散列表:其中是一组名值对,值可以是数据或函数。

对象和类的关系:在JavaScript中,对象和类没有任何关系。这是因为ECMAScript中根本就没有类的概念,它的对象与其他基于类的语言中的对象是不同的。

对象和引用类型的关系:对象和引用类型并不是等价的,因为每个对象都是基于一个引用类型创建的。

第二部分:对象属性如何进行分类

由构造函数或对象字面量方法创建的对象中具有属性和方法(只要提到属性和方法,它们一定是属于对象的;只要提到对象,它一定是具有属性和方法的(自定义除外)),其中属性又可分为数据属性和访问器属性,他们的区别如下:

数据属性一般用于存储数据数值,访问器属性不包含数据值

访问器属性多用于get/set操作

第三部分:属性中特性的理解

ECMAScript为了描述对象属性(property)的各种特征,定义了特性(attribute)这个概念。也就是说特性不同于属性,特性是为了描述属性的。下面,我将分别讲解:

  • 数据属性及其特性
  • 访问器属性及其特性
  • 如何利用Object.defineProperties()方法定义多个特性
  • 如何利用Object.getOwnPropertyDescripter()方法读取属性的描述符以读取属性的特性

1.数据属性及其特性

刚刚我们说过,数据属性是用于存储数据数值的,因此数据属性具有一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性,由于ECMAScript规定:在JavaScript中不能直接访问属性的特性(注意:不是不能访问),所以我们把它放在两组方括号中。如下:

  • [[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由数据属性修改为访问器属性
  • [[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
  • [[Writable]]:默认值为true,表示能否修改属性的值,这是与[[Configurable]]不同之处。
  • [[Value]]:默认值为undefined,这个值即为属性的属性值,我们可以在这个位置上读取属性值,也可以在这个位置上写入属性值。
  • 注意:上述的默认是指通过构造函数或对象字面量创建的对象所自身拥有的属性,而不是下面要介绍的Object.defineProperty()方法

这些特性都具有默认值,但是如果这些默认值不是我们想要的,该怎么办呢?当然就是修改啦!我们可以通过Object.defineProperty()方法来修改属性默认的特性。英文difineProperty即为定义属性的意思。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中第三个参数描述符对象是对象字面量的方法创建的,里面的属性和属性值实际上保存的是要修改的特性和特性值。

下面通过几个例子来深入理解。

a

var person={};
Object.defineProperty(person,"name",{
  writable:false,
  value:"zhuzhenwei"
});
console.log(person.name);//zhuzhenwei
person.name="heting";
console.log(person.name);//zhuzhenwei 

这里我用对象字面量的方法创建了一个对象,但是没有同时创建方法和属性。而是利用了Object.defineProperty()方法来创建了属性和修改了默认值。这里将writable设置为false,于是后面我试图修改person.name时,是无效的。

b

var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei"
});
console.log(person.name);//zhuzhenwei
person.name="heting";
console.log(person.name);//zhuzhenwei

注意看这个例子,这个例子中我删去了writable:false,为什么还是不能修改呢?这是因为之前我在介绍特性时,前三个默认为ture,是在创建对象并创建属性的情况下得到的。对于通过调用Object.defineProperty()方法创建的属性,其前三个特性的默认值均为false,这里需要注意。

c

var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:false
});
console.log(person.name);//zhuzhenwei
delete person.name;
console.log(person.name);//zhuzhenwei

这里我们将新建的属性name的特性设置为了configurable:false;因此下面删除属性的操作是无效的。根据b,可知configurable,默认就是false,即使去掉也不可修改。

d

var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:true
});
console.log(person.name);//zhuzhenwei
delete person.name;
console.log(person.name);//undefined

在这里我将默认的configurable的值由默认的false修改为了true,于是变成了可配置的,那么最后就成功删除了。

e

var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:false
});
console.log(person.name);//zhuzhenwei
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:true
});
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)

如果之前已经设置成为了false,那么后面再改成true也是徒劳的,即:一旦把属性设置成为不可配置的,就不能再把它变回可配置了。

f

console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
});
console.log(person.name);//zhuzhenwei
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:true
});
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)

这里可以说明,即使前一步我们不管默认的configurable:false,后面得到的仍是不可配置。于是,可以得出结论,为了可配置,必须在第一次调用Object.defineProperty()函数时就将默认的值修改为true。

2.访问器属性及其特性  

之前提到,访问器属性不包含数据值,他们包含一对getter函数和setter函数(这两个函数不是必须的)。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据。同样,由于不能通过JavaScript来直接访问得到访问器属性的特性,所以下面列出的特性将由[[]]括起来以作区分。

  • [[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由访问器属性修改为数据属性
  • [[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
  • [[Get]]:在读取属性时调用的函数。默认值为undefined  关键:特性可以是一个函数
  • [[Set]]:  在写入属性时调用的函数。默认值为undefined 关键:特性可以是一个函数 由于get和set函数也属于属性的特性,那么他们就有可能(说有可能是因为这两个函数也不是必须的)出现在Object.defineproperty的第三个参数描述符对象的属性中。

注意:1.相对于数据属性,我们发现访问器属性中没有writable特性和value特性。这是因为访问器属性不包含数据值,那么我们怎么当然就不可修改属性的值(用不到writable特性),更不用考虑value了。

 2.访问器属性不能直接定义,必须是用Object.defineProperty()来定义。(通过这个规定我们就能准确地判断出访问器属性和数据属性了)

通过下面这个例子来深入理解:

var book={
  _year:2004,
  edition:1
};
Object.defineProperty(book,"year",{
  get:function(){<br>




return this._year;
  },
  set:function(newValue){
    if(newValue>2004){
      this._year=newValue;
      this.edition+=newValue-2004;
    }
  }
});
book.year=2005;
console.log(book.edition);//2

几个需要深入理解的地方:

  • 访问器属性不能直接定义,必须使用Object.defineProperty()来定义,且该属性具有set和ger特性,于是可以判断,_year和edition是数据属性,而year是访问器属性。
  • 我们看到_year这个数据属性前面是以_(下划线)开头的,这个一种常用的记号,用于表示只能通过对象方法访问的属性。从上面的例子中可以看到get相当于描述符对象的一个方法,而_year正是在这个对象方法访问的属性。而edition既可以通过对象方法访问,也可以由对象直接访问。
  • book.year表示正在读取访问器属性,这时会调用get函数,并返回了2004这个有效的值。
  • book.year=2005表示写入访问器属性,这时会调用set函数并传入新值,即将2005传给newValue,这个函数决定如何处理数据。
  • 这时使用访问器属性的常见方法-即设置一个属性的值会导致其他属性发生变化。

3.如何利用Object.defineProperties()方法定义多个特性

显然,一个对象不可能只具有一个属性,因此,定义多个属性的可能性很大,于是JavaScript提供了Object.defineProperties()方法解决这个问题。这个方法接收两个参数,第一个是要定义属性所在的对象,第二个是一个对象字面量方法创建的对象,对象的属性名即为要定义的特姓名,对象的属性值又是一个对象,这个对象里的属性名和属性值分别是特性名和特性值(这里不是很好理解,看例子即可)。

var book={};
Object.defineProperties(book,{
  _year:{
    writable:true,
    value:2004
  },
  edition:{
    writable:true,
    value:1
  },
  year:{
    get:function(){
      return this._year;
    },
    set:function(){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  }
}); 

4.如何利用Object.getOwnPropertyDescripter()方法读取属性的描述符以读取属性的特性

我们可以使用Object.getOwnPropertyDescripter()方法来取得给定属性的描述符。getOwnPropertyDescripter即为取得自身属性描述符的意思。这个方法接收两个参数:属性所在的对象要要读取其描述符的属性名称。返回一个对象。

对于访问器属性而言,这个对象的属性有configurable、enumerable、get和set;

对于数据属性而言,这个对象的属性有configurable、enumerable、writable和value。

var book={};
Object.defineProperties(book,{
  _year:{
    value:2004
  },
  edition:{
    value:1
  },
  year:{
    get:function(){
      return this._year;
    },
    set:function(){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  }
});
var descriptor=Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor.value);//2004
console.log(descriptor.configurable);//false 因为通过Object.defineProperties()方法创建的属性的特性configurable enumerable都是false
console.log(typeof descriptor.get);//undefined 注意:这是数据属性,是不具有get特性的
var descriptor=Object.getOwnPropertyDescriptor(book,"year");
console.log(descriptor.value);//undefined
console.log(descriptor.enumerable);//false
console.log(typeof descriptor.get);//function get虽然是属性的一个特性,但是它也是函数。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
解析js如何获取当前url中的参数值并复制给input
Jun 23 Javascript
javascript函数作用域学习示例(js作用域)
Jan 13 Javascript
JQuery用户名校验的具体实现
Mar 18 Javascript
vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
Apr 13 Javascript
三种Webpack打包方式(小结)
Sep 19 Javascript
Vue Echarts实现可视化世界地图代码实例
May 07 Javascript
Vue代码整洁之去重方法整理
Aug 06 Javascript
在 Vue 中编写 SVG 图标组件的方法
Feb 24 Javascript
vue实现div单选多选功能
Jul 16 Javascript
React倒计时功能实现代码——解耦通用
Sep 18 Javascript
vue实现日历表格(element-ui)
Sep 24 Javascript
vue基于Echarts的拖拽数据可视化功能实现
Dec 04 Vue.js
微信小程序开发之视频播放器 Video 弹幕 弹幕颜色自定义实例
Dec 08 #Javascript
AngularJS模仿Form表单提交的实现代码
Dec 08 #Javascript
解析如何利用iframe标签以及js制作时钟
Dec 08 #Javascript
详解微信小程序 页面跳转 传递参数
Dec 08 #Javascript
浅谈javascript中执行环境(作用域)与作用域链
Dec 08 #Javascript
原生js编写焦点图效果
Dec 08 #Javascript
JavaScript中${pageContext.request.contextPath}取值问题及解决方案
Dec 08 #Javascript
You might like
php实现jQuery扩展函数
2009/10/30 PHP
优化PHP程序的方法小结
2012/02/23 PHP
php集成环境xampp中apache无法启动问题解决方案
2014/11/18 PHP
PHP实现基于mysqli的Model基类完整实例
2016/04/08 PHP
微信公众号模板消息群发php代码示例
2016/12/29 PHP
Laravel框架源码解析之反射的使用详解
2020/05/14 PHP
PHP var关键字相关原理及使用实例解析
2020/07/11 PHP
jquery显示和隐藏div特效实例
2013/02/27 Javascript
JS 在指定数组中随机取出N个不重复的数据
2014/06/10 Javascript
jQuery实现气球弹出框式的侧边导航菜单效果
2015/09/22 Javascript
快速解决js动态改变dom元素属性后页面及时渲染的问题
2016/07/06 Javascript
jQuery EasyUI常用数据验证汇总
2016/09/18 Javascript
ES6/JavaScript使用技巧分享
2017/12/14 Javascript
vue 全选与反选的实现方法(无Bug 新手看过来)
2018/02/09 Javascript
JS实现数组去重及数组内对象去重功能示例
2019/02/02 Javascript
浅析vue中的MVVM实现原理
2019/03/04 Javascript
Node绑定全局TraceID的实现方法
2019/11/14 Javascript
微信小程序自定义顶部组件customHeader的示例代码
2020/06/03 Javascript
详谈vue中router-link和传统a链接的区别
2020/07/22 Javascript
js实现无缝轮播图插件封装
2020/07/31 Javascript
原生JS实现记忆翻牌游戏
2020/07/31 Javascript
微信小程序连续签到7天积分获得功能的示例代码
2020/08/20 Javascript
[04:02]DOTA2上海特锦赛小组赛第二日recap精彩回顾
2016/02/28 DOTA
[42:35]2018DOTA2亚洲邀请赛3月30日 小组赛A组 VG VS OpTic
2018/03/31 DOTA
用Python程序抓取网页的HTML信息的一个小实例
2015/05/02 Python
Python实现解析Bit Torrent种子文件内容的方法
2017/08/29 Python
基于Django模板中的数字自增(详解)
2017/09/05 Python
使用django的objects.filter()方法匹配多个关键字的方法
2019/07/18 Python
解决Python Matplotlib绘图数据点位置错乱问题
2020/05/16 Python
python里的单引号和双引号的有什么作用
2020/06/17 Python
施工人员岗位职责
2013/12/12 职场文书
吸烟检讨书2000字
2014/02/13 职场文书
六个一活动实施方案
2014/03/21 职场文书
2014年小学少先队工作总结
2014/12/18 职场文书
社区义诊通知
2015/04/24 职场文书
Dashboard管理Kubernetes集群与API访问配置
2022/04/01 Servers