前面的话

  对于操作系统中的文件,我们可以驾轻就熟将其设置为只读、隐藏、系统文件或普通文件。于对象来说,属性描述符提供类似的功能,用来描述对象的值、是否可配置、是否可修改以及是否可枚举。本文就来介绍对象中神秘的属性描述符

描述符类型

  对象属性描述符的类型分为两种:数据属性和访问器属性

数据属性

  数据属性(data property)包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个特性

【1】Configurable(可配置性)

  可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true

【2】Enumerable(可枚举性)

  可枚举性决定属性是否出现在对象的属性枚举中,比如是否可以通过for-in循环返回该属性,默认值为true

【3】Writable(可写性)

  可写性决定是否可以修改属性的值,默认值为true

【4】Value(属性值)

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

访问器属性

  对象属性是名字、值和一组属性描述符构成的。而属性值可以用一个或两个方法替代,这两个方法就是getter和setter。而这种属性类型叫访问器属性(accessor property)

【1】Configurable(可配置性)

  可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true

【2】Enumerable(可枚举性)

  可枚举性决定属性是否出现在对象的属性枚举中,比如是否可以通过for-in循环返回该属性,默认值为true

【3】getter

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

【4】setter

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

  和数据属性不同,访问器属性不具有可写性(Writable)。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性。如果它只有setter方法,那么它是一个只写属性。读取只写属性总是返回undefined

描述符方法

  前面介绍了属性描述符,要想设置它们,就需要用到描述符方法。描述符方法总共有以下4个:

【1】Object.getOwnPropertyDescriptor()

  Object.getOwnPropertyDescriptor(o,name)方法用于查询一个属性的描述符,并以对象的形式返回

  查询obj.a属性时,可配置性、可枚举性、可写性都是默认的true,而value是a的属性值1

  查询obj.b属性时,因为obj.b属性不存在,该方法返回undefined

  1. var obj = {a:1};
  2. //Object {value: 1, writable: true, enumerable: true, configurable: true}
  3. console.log(Object.getOwnPropertyDescriptor(obj,'a'));
  4. //undefined
  5. console.log(Object.getOwnPropertyDescriptor(obj,'b'));

【2】Object.defineProperty()

  Object.defineProperty(o,name,desc)方法用于创建或配置对象的一个属性的描述符,返回配置后的对象

  使用该方法创建或配置对象属性的描述符时,如果不针对该属性进行描述符的配置,则该项描述符默认为false

  1. var obj = {};
  2. //{a:1}
  3. console.log(Object.defineProperty(obj,'a',{
  4. value:1,
  5. writable: true
  6. }));
  7.  
  8. //由于没有配置enumerable和configurable,所以它们的值为false
  9. //{value: 1, writable: true, enumerable: false, configurable: false}
  10. console.log(Object.getOwnPropertyDescriptor(obj,'a'));

【3】Object.defineProperties()

  Object.defineProperty(o,descriptors)方法用于创建或配置对象的多个属性的描述符,返回配置后的对象

  1. var obj = {
  2. a:1
  3. };
  4. //{a: 1, b: 2}
  5. console.log(Object.defineProperties(obj,{
  6. a:{writable:false},
  7. b:{value:2}
  8. }));
  9.  
  10. //{value: 1, writable: false, enumerable: true, configurable: true}
  11. console.log(Object.getOwnPropertyDescriptor(obj,'a'));
  12. //{value: 2, writable: false, enumerable: false, configurable: false}
  13. console.log(Object.getOwnPropertyDescriptor(obj,'b'));

【4】Object.create()

  Object.create(proto,descriptors)方法使用指定的原型和属性来创建一个对象

  1. var o = Object.create(Object.prototype,{
  2. a:{writable: false,value:1,enumerable:true}
  3. });
  4. //{value: 1, writable: false, enumerable: true, configurable: true}
  5. console.log(Object.getOwnPropertyDescriptor(obj,'a'));

描述符详述

  前面分别介绍了数据属性和访问器属性的描述符,但没有详细说明其含义及使用,接下来逐一进行说明

可写性(writable)

  可写性决定是否可以修改属性的值,默认值为true

  1. var o = {a:1};
  2. o.a = 2;
  3. console.log(o.a);//

  设置writable:false后,赋值语句会静默失效

  1. var o = {a:1};
  2. Object.defineProperty(o,'a',{
  3. writable:false
  4. });
  5. console.log(o.a);//
  6. //由于设置了writable为false,所以o.a=2这个语句会静默失效
  7. o.a = 2;
  8. console.log(o.a);//
  9. Object.defineProperty(o,'a',{
  10. writable:true
  11. });
  12. //由于writable设置为true,所以o.a可以被修改为2
  13. o.a = 2;
  14. console.log(o.a);//

  在严格模式下通过赋值语句为writable为false的属性赋值,会提示类型错误TypeError

  1. 'use strict';
  2. var o = {a:1};
  3. Object.defineProperty(o,'a',{
  4. writable:false
  5. });
  6. //Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
  7. o.a = 2;

  [注意]设置writable:false后,通过Object.defineProperty()方法改变属性value的值不会受影响,因为这也意味着在重置writable的属性值为false

  1. var o = {a:1};
  2. Object.defineProperty(o,'a',{
  3. writable:false
  4. });
  5. console.log(o.a);//
  6. Object.defineProperty(o,'a',{
  7. value:2
  8. });
  9. console.log(o.a);//

可配置性(Configurable)

  可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true

  【1】设置Configurable:false后,无法使用delete删除属性

  1. var o = {a:1};
  2. Object.defineProperty(o,'a',{
  3. configurable:false
  4. });
  5. delete o.a;//false
  6. console.log(o.a);//

  在严格模式下删除为configurable为false的属性,会提示类型错误TypeError

  1. 'use strict';
  2. var o = {a:1};
  3. Object.defineProperty(o,'a',{
  4. configurable:false
  5. });
  6. //Uncaught TypeError: Cannot delete property 'a' of #<Object>
  7. delete o.a;

  [注意]使用var命令声明变量时,变量的configurable为false

  1. var a = 1;
  2. //{value: 1, writable: true, enumerable: true, configurable: false}
  3. Object.getOwnPropertyDescriptor(this,'a');

  【2】一般地,设置Configurable:false后,将无法再使用defineProperty()方法来修改属性描述符

  1. var o = {a:1};
  2. Object.defineProperty(o,'a',{
  3. configurable:false
  4. });
  5. //Uncaught TypeError: Cannot redefine property: a
  6. Object.defineProperty(o,'a',{
  7. configurable:true
  8. });

  有一个例外,设置Configurable:false后,只允许writable的状态从true变为false

  1. var o = {a:1};
  2. Object.defineProperty(o,'a',{
  3. configurable:false,
  4. writable:true
  5. });
  6. o.a = 2;
  7. console.log(o.a);//
  8. Object.defineProperty(o,'a',{
  9. writable:false
  10. });
  11. //由于writable:false生效,对象a的o属性无法修改值,所以o.a=3的赋值语句静默失败
  12. o.a = 3;
  13. console.log(o.a);//

可枚举性(Enumerable)

  可枚举性决定属性是否出现在对象的属性枚举中,具体来说,for-in循环、Object.keys方法、JSON.stringify方法是否会取到该属性

  用户定义的普通属性默认是可枚举的,而原生继承的属性默认是不可枚举的

  1. //由于原生继承的属性默认不可枚举,所以只取得自定义的属性a:1
  2. var o = {a:1};
  3. for(var i in o){
  4. console.log(o[i]);//
  5. }
  1. //由于enumerable被设置为false,在for-in循环中a属性无法被枚举出来
  2. var o = {a:1};
  3. Object.defineProperty(o,'a',{enumerable:false});
  4. for(var i in o){
  5. console.log(o[i]);//undefined
  6. }

propertyIsEnumerable()

  propertyIsEnumerable()方法用于判断对象的属性是否可枚举

  1. var o = {a:1};
  2. console.log(o.propertyIsEnumerable('a'));//true
  3. Object.defineProperty(o,'a',{enumerable:false});
  4. console.log(o.propertyIsEnumerable('a'));//false

get和set

  get是一个隐藏函数,在获取属性值时调用。set也是一个隐藏函数,在设置属性值时调用,它们的默认值都是undefined。Object.definedProperty()中的get和set对应于对象字面量中get和set方法

  [注意]getter和setter取代了数据属性中的value和writable属性

  【1】给只设置get方法,没有设置set方法的对象赋值会静默失败,在严格模式下会报错

  1. var o = {
  2. get a(){
  3. return 2;
  4. }
  5. }
  6. console.log(o.a);//
  7. //由于没有设置set方法,所以o.a=3的赋值语句会静默失败
  8. o.a = 3;
  9. console.log(o.a);//
  1. Object.defineProperty(o,'a',{
  2. get: function(){
  3. return 2;
  4. }
  5. })
  6. console.log(o.a);//
  7. //由于没有设置set方法,所以o.a=3的赋值语句会静默失败
  8. o.a = 3;
  9. console.log(o.a);//

  在严格模式下,给没有设置set方法的访问器属性赋值会报错

  1. 'use strict';
  2. var o = {
  3. get a(){
  4. return 2;
  5. }
  6. }
  7. console.log(o.a);//
  8. //由于没有设置set方法,所以o.a=3的赋值语句会报错
  9. //Uncaught TypeError: Cannot set property a of #<Object> which has only a getter
  10. o.a = 3;
  1. 'use strict';
  2. Object.defineProperty(o,'a',{
  3. get: function(){
  4. return 2;
  5. }
  6. })
  7. console.log(o.a);//
  8. //由于没有设置set方法,所以o.a=3的赋值语句会报错
  9. //Uncaught TypeError: Cannot set property a of #<Object> which has only a getter
  10. o.a = 3;

  【2】只设置set方法,而不设置get方法,则对象属性值为undefined

  1. var o = {
  2. set a(val){
  3. return 2;
  4. }
  5. }
  6. o.a = 1;
  7. console.log(o.a);//undefined
  1. Object.defineProperty(o,'a',{
  2. set: function(){
  3. return 2;
  4. }
  5. })
  6. o.a = 1;
  7. console.log(o.a);//undefined

  【3】一般地,set和get方法是成对出现的

  1. var o ={
  2. get a(){
  3. return this._a;
  4. },
  5. set a(val){
  6. this._a = val*2;
  7. }
  8. }
  9. o.a = 1;
  10. console.log(o.a);//
  1. Object.defineProperty(o,'a',{
  2. get: function(){
  3. return this._a;
  4. },
  5. set :function(val){
  6. this._a = val*2;
  7. }
  8. })
  9. o.a = 1;
  10. console.log(o.a);//

对象状态

  属性描述符只能用来控制对象中一个属性的状态。而如果要控制对象的状态,就要用到下面的6种方法

Object.preventExtensions()(禁止扩展)

  Object.preventExtensions()方法使一个对象无法再添加新的属性,并返回当前对象

Object.isExtensible()(测试扩展)

  Object.isExtensible()方法用来检测该对象是否可以扩展

  1. var o = {a:1};
  2. console.log(Object.isExtensible(o));//true
  3. o.b = 2;
  4. console.log(o);//{a: 1, b: 2}
  5. console.log(Object.preventExtensions(o));//{a: 1, b: 2}
  6. //由于对象o禁止扩展,所以该赋值语句静默失败
  7. o.c = 3;
  8. console.log(Object.isExtensible(o));//false
  9. console.log(o);//{a: 1, b: 2}

  在严格模式下,给禁止扩展的对象添加属性会报TypeError错误

  1. 'use strict';
  2. var o = {a:1};
  3. console.log(Object.preventExtensions(o));//{a:1}
  4. //Uncaught TypeError: Can't add property c, object is not extensible
  5. o.c = 3;

  Object.preventExtensions()方法并不改变对象中属性的描述符状态

  1. var o = {a:1};
  2. //{value: 1, writable: true, enumerable: true, configurable: true}
  3. console.log(Object.getOwnPropertyDescriptor(o,'a'));
  4. Object.preventExtensions(o);
  5. //{value: 1, writable: true, enumerable: true, configurable: true}
  6. console.log(Object.getOwnPropertyDescriptor(o,'a'));

Object.seal()(对象封印)

  对象封印又叫对象密封,使一个对象不可扩展并且所有属性不可配置,并返回当前对象

Object.isSealed()(测试封印)

  Object.isSealed()方法用来检测该方法是否被封印

  1. var o = {a:1,b:2};
  2. console.log(Object.isSealed(o));//false
  3. console.log(Object.seal(o));//{a:1,b:2}
  4. console.log(Object.isSealed(o));//true
  5. console.log(delete o.b);//false
  6. o.c = 3;
  7. console.log(o);//{a:1,b:2}

  在严格模式下,删除旧属性或添加新属性都会报错

  1. 'use strict';
  2. var o = {a:1,b:2};
  3. console.log(Object.seal(o));//{a:1,b:2}
  4. //Uncaught TypeError: Cannot delete property 'b' of #<Object>
  5. delete o.b;

  这个方法实际上会在现有对象上调用Object.preventExtensions()方法,并把所有现有属性的configurable描述符置为false

  1. var o = {a:1,b:2};
  2. //{value: 1, writable: true, enumerable: true, configurable: true}
  3. console.log(Object.getOwnPropertyDescriptor(o,'a'));
  4. console.log(Object.seal(o));//{a:1,b:2}
  5. //{value: 1, writable: true, enumerable: true, configurable: false}
  6. console.log(Object.getOwnPropertyDescriptor(o,'a'));

Object.freeze()(对象冻结)

  Object.freeze()方法使一个对象不可扩展,不可配置,也不可改写,变成一个仅可以枚举的只读常量,并返回当前对象

Object.isFrozen()(检测冻结)

  Object.isFrozen()方法用来检测一个对象是否被冻结

  1. var o = {a:1,b:2};
  2. console.log(Object.isFrozen(o));//false
  3. console.log(Object.freeze(o));//{a:1,b:2}
  4. console.log(Object.isFrozen(o));//true
  5. o.a = 3;
  6. console.log(o);//{a:1,b:2}

  在严格模式下,删除旧属性、添加新属性、更改现有属性都会报错

  1. 'use strict';
  2. var o = {a:1,b:2};
  3. console.log(Object.freeze(o));//{a:1,b:2}
  4. //Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
  5. o.a = 3;

  这个方法实际上会在现有对象上调用Object.seal()方法,并把所有现有属性的writable描述符置为false

  1. var o = {a:1};
  2. //{value: 1, writable: true, enumerable: true, configurable: true}
  3. console.log(Object.getOwnPropertyDescriptor(o,'a'));
  4. console.log(Object.freeze(o));//{a:1}
  5. //{value: 1, writable: false, enumerable: true, configurable: false}
  6. console.log(Object.getOwnPropertyDescriptor(o,'a'));

参考资料

【1】  阮一峰Javascript标准参考教程——属性描述对象 http://javascript.ruanyifeng.com/stdlib/attributes.html
【2】《javascript权威指南(第6版)》第6章 对象
【3】《你不知道的javascript上卷》第3章 对象
【4】《javascript高级程序设计(第3版)》第6章 面向对象的程序设计
【5】《javascript面向对象精要》 第3章 理解对象

深入理解javascript对象系列第三篇——神秘的属性描述符的更多相关文章

  1. 深入理解javascript函数系列第三篇——属性和方法

    × 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...

  2. 深入理解javascript函数系列第三篇

    前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...

  3. 深入理解javascript作用域系列第三篇——声明提升(hoisting)

    × 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...

  4. 深入理解javascript作用域系列第三篇

    前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...

  5. javascript面向对象系列第三篇——实现继承的3种形式

    × 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...

  6. 深入理解javascript作用域系列第四篇——块作用域

    × 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...

  7. 深入理解javascript作用域系列第四篇

    前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...

  8. 深入理解javascript对象系列第一篇——初识对象

    × 目录 [1]定义 [2]创建 [3]组成[4]引用[5]方法 前面的话 javascript中的难点是函数.对象和继承,前面已经介绍过函数系列.从本系列开始介绍对象部分,本文是该系列的第一篇——初 ...

  9. 深入理解javascript对象系列第二篇——属性操作

    × 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...

随机推荐

  1. 树形DP

    切题ing!!!!! HDU  2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...

  2. <input>和<textarea>

    作为一个刚刚涉足PHP开发的菜鸟,第一次使用博客.在这里分享一些经验给和需要的朋友,互相探讨.共同学习,希望对你有所帮助. 废话不多说,下面进入正题. 应该有朋友和我一样,需要用到文本框,要求它会自动 ...

  3. java爬虫:在请求body中增加json数据采集

    1,http://www.hqepay.com/public/expressquery.html 查询快递不是将键值对post过去,而是将json数据放到body中发送过去.抓包如下: 2,需要导入一 ...

  4. IIS 连接 oracle报Oracle.DataAccess版本错误解决办法

    通过IIS连接oracle时报“Could not load file or assembly 'Oracle.DataAccess, Version=2.112.3.0, Culture=neutr ...

  5. backbone新手填坑教程资源

    backbone 入门第二版 http://www.kancloud.cn/kancloud/backbonejs-learning-note/49379 backbone 入门讲解 http://w ...

  6. C语言两种查找方式(分块查找,二分法)

    二分法(必须要保证数据是有序排列的):   分块查找(数据有如下特点:块间有序,块内无序):    

  7. 最近在新公司的一些HTML学习

    还是先把代码贴在这  后期再写感想 <!DOCTYPE html> <head> <meta http-equiv="x-ua-compatible" ...

  8. DPC和ISR的理解

    首先来说中断 计算机的中断分为软中断和硬中断,即IRQL和DIRQL,共32个级别,从0~31级别依次提升,0~2属于软中断 一般线程运行于PASSIVE_LEVEL级别,如果不想在运行时切换到其他线 ...

  9. C# DataSet

    一.基本概念 DataSet是ADO.NET的中心概念.可以把DataSet当成内存中的数据库,DataSet是不依赖于数据库的独立数据集合.所谓独立,就是说,即使断开数据链路,或者关闭数据库,Dat ...

  10. 安卓工具箱:color of Style

    <?xml version="1.0" encoding="utf-8"?> <resources> <color name=&q ...