详解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 相关文章推荐
jQuery学习笔记之Helloworld
Dec 22 Javascript
浏览器图片选择预览、旋转、批量上传的JS代码实现
Dec 04 Javascript
js数组去重的常用方法总结
Jan 24 Javascript
关于onchange事件在IE和FF下的表现及解决方法
Mar 08 Javascript
WEB前端设计师常用工具集锦
Dec 09 Javascript
JS组件Bootstrap实现弹出框效果代码
Apr 26 Javascript
JS中双击和单击事件冲突的解决方法
Apr 09 Javascript
Vue页面骨架屏注入方法
May 13 Javascript
jQuery实现动态生成年月日级联下拉列表示例
May 11 jQuery
关于layui的动态图标不显示的解决方法
Sep 04 Javascript
解决node终端下运行js文件不支持ES6语法
Apr 04 Javascript
微信小程序仿通讯录功能
Apr 09 Javascript
微信小程序开发之视频播放器 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
5.PHP的其他功能
2006/10/09 PHP
利用递归把多维数组转为一维数组的函数
2006/10/09 PHP
PHP 网页过期时间的控制代码
2009/06/29 PHP
php线性表顺序存储实现代码(增删查改)
2012/02/16 PHP
浅析php原型模式
2014/11/25 PHP
Zend Guard使用指南及问题处理
2015/01/07 PHP
php使用cookie实现记住用户名和密码实现代码
2015/04/27 PHP
PHP常用的小程序代码段
2015/11/14 PHP
thinkPHP js文件中U方法不被解析问题的解决方法
2016/12/05 PHP
Laravel5.7 数据库操作迁移的实现方法
2019/04/12 PHP
js读取本地excel文档数据的代码
2010/11/11 Javascript
js操作CheckBoxList实现全选/反选(在客服端完成)
2013/02/02 Javascript
js同比例缩放图片的小例子
2013/10/30 Javascript
JavaScript动态操作表格实例(添加,删除行,列及单元格)
2013/11/25 Javascript
JavaScript自定义日期格式化函数详细解析
2014/01/14 Javascript
调用jQuery滑出效果时闪烁的解决方法
2014/03/27 Javascript
javascript实现2048游戏示例
2014/05/04 Javascript
javascript学习笔记(三)BOM和DOM详解
2014/09/30 Javascript
JavaScript获取Url里的参数
2014/12/18 Javascript
Jquery结合HTML5实现文件上传
2015/06/25 Javascript
JS实现随页面滚动显示/隐藏窗口固定位置元素
2016/02/26 Javascript
js点击按钮实现水波纹效果代码(CSS3和Canves)
2016/09/15 Javascript
给easyui datebox扩展一个清空的实例
2016/11/09 Javascript
VUE JS 使用组件实现双向绑定的示例代码
2017/01/10 Javascript
JavaScript数组操作详解
2017/02/04 Javascript
把JavaScript代码改成ES6语法不完全指南(分享)
2017/09/10 Javascript
JavaScript基础教程之如何实现一个简单的promise
2018/09/11 Javascript
JavaScript解析机制与闭包原理实例详解
2019/03/08 Javascript
如何使用pyinstaller打包32位的exe程序
2019/05/26 Python
使用PyInstaller将Pygame库编写的小游戏程序打包为exe文件及出现问题解决方法
2019/09/06 Python
Python远程方法调用实现过程解析
2020/07/28 Python
荷兰网上药店:Drogisterij.net
2019/09/03 全球购物
煤矿隐患排查制度
2015/08/05 职场文书
人力资源部工作计划
2019/05/14 职场文书
mysql优化之query_cache_limit参数说明
2021/07/01 MySQL
springboot新建项目pom.xml文件第一行报错的解决
2022/01/18 Java/Android