详解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函数库)S.Sams Lifexperience ScriptClassLib
Apr 29 Javascript
jquery实现滑动图片自己测试的例子
Nov 05 Javascript
JavaScript通过setTimeout实时显示当前时间的方法
Apr 16 Javascript
JS JSOP跨域请求实例详解
Jul 04 Javascript
微信公众平台开发教程(六)获取个性二维码的实例
Dec 02 Javascript
easyUI实现类似搜索框关键词自动提示功能示例代码
Dec 27 Javascript
JavaScript字符串检索字符的方法
Jun 23 Javascript
vue动画打包后失效问题的解决方法
Sep 18 Javascript
laydate如何根据开始时间或者结束时间限制范围
Nov 15 Javascript
Vue-Cli项目优化操作的实现
Oct 27 Javascript
jquery自定义组件实例详解
Dec 31 jQuery
一文帮你理解PReact10.5.13源码
Apr 03 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
一个ftp类(ini.php)
2006/10/09 PHP
php读取二进制流(C语言结构体struct数据文件)的深入解析
2013/06/13 PHP
PHP对象递归引用造成内存泄漏分析
2014/08/28 PHP
MacOS 安装 PHP的图片裁剪扩展Tclip
2015/03/25 PHP
大家须知简单的php性能优化注意点
2016/01/04 PHP
PHP 的比较运算与逻辑运算详解
2016/05/12 PHP
实例分析基于PHP微信网页获取用户信息
2017/11/24 PHP
PHP连接MySQL数据库并以json格式输出
2018/05/21 PHP
多浏览器兼容的获取元素和鼠标的位置的js代码
2009/12/15 Javascript
Javascript排序算法之计数排序的实例
2014/04/05 Javascript
js实现简洁的TAB滑动门效果代码
2015/09/06 Javascript
JS实现图片平面旋转的方法
2016/03/01 Javascript
前端弹出对话框 js实现ajax交互
2016/09/09 Javascript
基于javascript实现的购物商城商品倒计时实例
2016/12/11 Javascript
js遍历获取表格内数据的方法(必看)
2017/04/06 Javascript
JS实现微信里判断页面是否被分享成功的方法
2017/06/06 Javascript
vue中使用cropperjs的方法
2018/03/01 Javascript
JS实现DOM节点插入操作之子节点与兄弟节点插入操作示例
2018/07/30 Javascript
Vuex的实战使用详解
2019/10/31 Javascript
ES11屡试不爽的新特性,你用上了几个
2020/10/21 Javascript
Python提取网页中超链接的方法
2016/09/18 Python
python取余运算符知识点详解
2019/06/27 Python
Python-opencv 双线性插值实例
2020/01/17 Python
使用 Python 处理3万多条数据只要几秒钟
2020/01/19 Python
使用Python将图片转正方形的两种方法实例代码详解
2020/04/29 Python
Python读取yaml文件的详细教程
2020/07/21 Python
css3 伪元素和伪类选择器详解
2014/09/04 HTML / CSS
全球烹饪课程的领先预订平台:Cookly
2020/01/28 全球购物
硕士研究生自我鉴定范文
2013/12/27 职场文书
有多年工作经验的自我评价
2014/03/02 职场文书
少儿节目主持串词
2014/04/02 职场文书
期末评语大全
2014/05/04 职场文书
写给纪委的违纪检讨书
2015/05/05 职场文书
CAD实训总结范文
2015/08/03 职场文书
小学英语教学反思范文
2016/02/15 职场文书
利用ajax+php实现商品价格计算
2021/03/31 PHP