深入理解JavaScript中的属性和特性

时间:2023-01-09 11:17:22

深入理解JavaScript中的属性和特性

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

  

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

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

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中的属性和特性的更多相关文章

  1. 理解JavaScript中的属性描述符

    我们把描述JavaScript中定义内部特性的属性叫做属性描述符 分为两大类:数据描述符和存取描述符 数据描述符是一个拥有可写或不可写的属性(Writable); 存取描述符不包含数据值,是一组拥有g ...

  2. 深入理解JavaScript中创建对象模式的演变(原型)

    深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...

  3. 理解JavaScript 的原型属性

    1.原型继承 面向对象编程可以通过很多途径实现.其他的语言,比如 Java,使用基于类的模型实现: 类及对象实例区别对待.但在 JavaScript 中没有类的概念,取而代之的是一切皆对象.JavaS ...

  4. 【拾遗】理解Javascript中的Arguments

    前言 最近在看JavaScript相关的知识点,看到了老外的一本Javascript For Web Developers,遇到了一个知识盲点,觉得老外写的很明白很透彻,记录下来加深印象,下面是我摘出 ...

  5. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  6. 详解 javascript中offsetleft属性的用法(转)

    详解 javascript中offsetleft属性的用法 转载  2015-11-11   投稿:mrr    我要评论 本章节通过代码实例介绍一下offsetleft属性的用法,需要的朋友可以做一 ...

  7. JavaScript中面向对象的三大特性(一个菜鸟的不正经日常)

    经过几天的学习,把jQuery给啃会了,但是运用的还不算特别熟练,总感觉自己在JavaScript方面的基础十分欠缺,所以继续拾起JavaScript,开始更好的编程之旅~ 今天学的是JavaScri ...

  8. 深入理解JavaScript中的作用域和上下文

    介绍 JavaScript中有一个被称为作用域(Scope)的特性.虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,我会尽我所能用最简单的方式来解释作用域.理解作用域将使你的代码脱颖而出,减 ...

  9. 深入理解Javascript中构造函数和原型对象的区别

    在 Javascript中prototype属性的详解 这篇文章中,详细介绍了构造函数的缺点以及原型(prototype),原型链(prototype chain),构造函数(constructor) ...

随机推荐

  1. 解决eclipse中svn插件总是提示输入密码的问题

    一.背景 最近在eclipse中使用svn插件进行远程仓库代码管理时,老是出现提示让输入密码,特别烦人,经过努力,终于解决该问题,拿来和大家分享~ 二.svn插件密码机制以及出现问题的原因分析 当我们 ...

  2. mysql show status

    在LAMP架构的网站开发过程中,有些时候我们需要了解MySQL的服务器状态信息,譬如当前MySQL启动后的运行时间,当前MySQL的客户端会话连接数,当前MySQL服务器执行的慢查询数,当前MySQL ...

  3. ecpilise引入Maven项目目录不正常,无JRE,无Maven Dependencies

    原因是我的eclipse默认open perspective是java ee,改成java就恢复正常了.

  4. (转)排列算法 Permutation Generation

    转自:http://www.cnblogs.com/dragonpig/archive/2010/01/21/1653680.html http://www.notesandreviews.com/p ...

  5. BZOJ_1628_[Usaco2007_Demo]_City_skyline_(单调栈)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1628 给出\(n\)个距形的影子,问最少是多少个建筑的?(建筑的影子可以重叠). 分析 用单调 ...

  6. html5 video.js 使用及兼容所有浏览器

    废话少说,直接开始 一.准备材料 video.js下载: http://www.videojs.com/ 二.代码 引入相关文件:(必须放在文件的开头,也是说一定要放在video标签之前) 贴入htm ...

  7. 在Linux系统上获取命令帮助信息和划分man文档

    使用历史命令history 打完以后前面会有顺序号的比如1 cd2 ls3 pwd如果需要重新执行cd命令则可以执行 !3 命令 命令补全功能 比如你要执行history命令 可以打上histo+键 ...

  8. 类型和原生函数及类型转换(二:终结js类型判断)

    typeof instanceof isArray() Object.prototype.toString.call() DOM对象与DOM集合对象的类型判断 一.typeof typeof是一个一元 ...

  9. iOS开发之--Masonry多个平均布局

    使用Masonry平均布局,代码如下: 1.创建 // 图片组数 NSArray *imgAry = @[@"home_icon01",@"home_icon02&quo ...

  10. 20172325『Java程序设计』课程 结对编程练习_四则运算第三周阶段总结

    20172325『Java程序设计』课程 结对编程练习_四则运算第三周阶段总结 结对伙伴 学号:20172306 姓名:刘辰 在这次项目的完成过程中刘辰同学付出了很多,在代码的实践上完成的很出色,在技 ...