深入理解javascript对象系列第三篇——神秘的属性描述符
前面的话
对于操作系统中的文件,我们可以驾轻就熟将其设置为只读、隐藏、系统文件或普通文件。于对象来说,属性描述符提供类似的功能,用来描述对象的值、是否可配置、是否可修改以及是否可枚举。本文就来介绍对象中神秘的属性描述符
描述符类型
对象属性描述符的类型分为两种:数据属性和访问器属性
数据属性
数据属性(data property)包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个特性
可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true
可枚举性决定属性是否出现在对象的属性枚举中,比如是否可以通过for-in循环返回该属性,默认值为true
可写性决定是否可以修改属性的值,默认值为true
【4】Value(属性值)
属性值包含这个属性的数据值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。默认值为undefined
访问器属性
对象属性是名字、值和一组属性描述符构成的。而属性值可以用一个或两个方法替代,这两个方法就是getter和setter。而这种属性类型叫访问器属性(accessor property)
可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true
可枚举性决定属性是否出现在对象的属性枚举中,比如是否可以通过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
var obj = {a:1};
//Object {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//undefined
console.log(Object.getOwnPropertyDescriptor(obj,'b'));
【2】Object.defineProperty()
Object.defineProperty(o,name,desc)方法用于创建或配置对象的一个属性的描述符,返回配置后的对象
使用该方法创建或配置对象属性的描述符时,如果不针对该属性进行描述符的配置,则该项描述符默认为false
var obj = {};
//{a:1}
console.log(Object.defineProperty(obj,'a',{
value:1,
writable: true
}));
//由于没有配置enumerable和configurable,所以它们的值为false
//{value: 1, writable: true, enumerable: false, configurable: false}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
【3】Object.defineProperties()
Object.defineProperty(o,descriptors)方法用于创建或配置对象的多个属性的描述符,返回配置后的对象
var obj = {
a:1
};
//{a: 1, b: 2}
console.log(Object.defineProperties(obj,{
a:{writable:false},
b:{value:2}
}));
//{value: 1, writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//{value: 2, writable: false, enumerable: false, configurable: false}
console.log(Object.getOwnPropertyDescriptor(obj,'b'));
【4】Object.create()
Object.create(proto,descriptors)方法使用指定的原型和属性来创建一个对象
var o = Object.create(Object.prototype,{
a:{writable: false,value:1,enumerable:true}
});
//{value: 1, writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
描述符详述
前面分别介绍了数据属性和访问器属性的描述符,但没有详细说明其含义及使用,接下来逐一进行说明
可写性(writable)
可写性决定是否可以修改属性的值,默认值为true
var o = {a:1};
o.a = 2;
console.log(o.a);//
设置writable:false后,赋值语句会静默失效
var o = {a:1};
Object.defineProperty(o,'a',{
writable:false
});
console.log(o.a);//
//由于设置了writable为false,所以o.a=2这个语句会静默失效
o.a = 2;
console.log(o.a);//
Object.defineProperty(o,'a',{
writable:true
});
//由于writable设置为true,所以o.a可以被修改为2
o.a = 2;
console.log(o.a);//
在严格模式下通过赋值语句为writable为false的属性赋值,会提示类型错误TypeError
'use strict';
var o = {a:1};
Object.defineProperty(o,'a',{
writable:false
});
//Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
o.a = 2;
[注意]设置writable:false后,通过Object.defineProperty()方法改变属性value的值不会受影响,因为这也意味着在重置writable的属性值为false
var o = {a:1};
Object.defineProperty(o,'a',{
writable:false
});
console.log(o.a);//
Object.defineProperty(o,'a',{
value:2
});
console.log(o.a);//
可配置性(Configurable)
可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true
【1】设置Configurable:false后,无法使用delete删除属性
var o = {a:1};
Object.defineProperty(o,'a',{
configurable:false
});
delete o.a;//false
console.log(o.a);//
在严格模式下删除为configurable为false的属性,会提示类型错误TypeError
'use strict';
var o = {a:1};
Object.defineProperty(o,'a',{
configurable:false
});
//Uncaught TypeError: Cannot delete property 'a' of #<Object>
delete o.a;
[注意]使用var命令声明变量时,变量的configurable为false
var a = 1;
//{value: 1, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(this,'a');
【2】一般地,设置Configurable:false后,将无法再使用defineProperty()方法来修改属性描述符
var o = {a:1};
Object.defineProperty(o,'a',{
configurable:false
});
//Uncaught TypeError: Cannot redefine property: a
Object.defineProperty(o,'a',{
configurable:true
});
有一个例外,设置Configurable:false后,只允许writable的状态从true变为false
var o = {a:1};
Object.defineProperty(o,'a',{
configurable:false,
writable:true
});
o.a = 2;
console.log(o.a);//
Object.defineProperty(o,'a',{
writable:false
});
//由于writable:false生效,对象a的o属性无法修改值,所以o.a=3的赋值语句静默失败
o.a = 3;
console.log(o.a);//
可枚举性(Enumerable)
可枚举性决定属性是否出现在对象的属性枚举中,具体来说,for-in循环、Object.keys方法、JSON.stringify方法是否会取到该属性
用户定义的普通属性默认是可枚举的,而原生继承的属性默认是不可枚举的
//由于原生继承的属性默认不可枚举,所以只取得自定义的属性a:1
var o = {a:1};
for(var i in o){
console.log(o[i]);//
}
//由于enumerable被设置为false,在for-in循环中a属性无法被枚举出来
var o = {a:1};
Object.defineProperty(o,'a',{enumerable:false});
for(var i in o){
console.log(o[i]);//undefined
}
propertyIsEnumerable()
propertyIsEnumerable()方法用于判断对象的属性是否可枚举
var o = {a:1};
console.log(o.propertyIsEnumerable('a'));//true
Object.defineProperty(o,'a',{enumerable:false});
console.log(o.propertyIsEnumerable('a'));//false
get和set
get是一个隐藏函数,在获取属性值时调用。set也是一个隐藏函数,在设置属性值时调用,它们的默认值都是undefined。Object.definedProperty()中的get和set对应于对象字面量中get和set方法
[注意]getter和setter取代了数据属性中的value和writable属性
【1】给只设置get方法,没有设置set方法的对象赋值会静默失败,在严格模式下会报错
var o = {
get a(){
return 2;
}
}
console.log(o.a);//
//由于没有设置set方法,所以o.a=3的赋值语句会静默失败
o.a = 3;
console.log(o.a);//
Object.defineProperty(o,'a',{
get: function(){
return 2;
}
})
console.log(o.a);//
//由于没有设置set方法,所以o.a=3的赋值语句会静默失败
o.a = 3;
console.log(o.a);//
在严格模式下,给没有设置set方法的访问器属性赋值会报错
'use strict';
var o = {
get a(){
return 2;
}
}
console.log(o.a);//
//由于没有设置set方法,所以o.a=3的赋值语句会报错
//Uncaught TypeError: Cannot set property a of #<Object> which has only a getter
o.a = 3;
'use strict';
Object.defineProperty(o,'a',{
get: function(){
return 2;
}
})
console.log(o.a);//
//由于没有设置set方法,所以o.a=3的赋值语句会报错
//Uncaught TypeError: Cannot set property a of #<Object> which has only a getter
o.a = 3;
【2】只设置set方法,而不设置get方法,则对象属性值为undefined
var o = {
set a(val){
return 2;
}
}
o.a = 1;
console.log(o.a);//undefined
Object.defineProperty(o,'a',{
set: function(){
return 2;
}
})
o.a = 1;
console.log(o.a);//undefined
【3】一般地,set和get方法是成对出现的
var o ={
get a(){
return this._a;
},
set a(val){
this._a = val*2;
}
}
o.a = 1;
console.log(o.a);//
Object.defineProperty(o,'a',{
get: function(){
return this._a;
},
set :function(val){
this._a = val*2;
}
})
o.a = 1;
console.log(o.a);//
对象状态
属性描述符只能用来控制对象中一个属性的状态。而如果要控制对象的状态,就要用到下面的6种方法
Object.preventExtensions()(禁止扩展)
Object.preventExtensions()方法使一个对象无法再添加新的属性,并返回当前对象
Object.isExtensible()(测试扩展)
Object.isExtensible()方法用来检测该对象是否可以扩展
var o = {a:1};
console.log(Object.isExtensible(o));//true
o.b = 2;
console.log(o);//{a: 1, b: 2}
console.log(Object.preventExtensions(o));//{a: 1, b: 2}
//由于对象o禁止扩展,所以该赋值语句静默失败
o.c = 3;
console.log(Object.isExtensible(o));//false
console.log(o);//{a: 1, b: 2}
在严格模式下,给禁止扩展的对象添加属性会报TypeError错误
'use strict';
var o = {a:1};
console.log(Object.preventExtensions(o));//{a:1}
//Uncaught TypeError: Can't add property c, object is not extensible
o.c = 3;
Object.preventExtensions()方法并不改变对象中属性的描述符状态
var o = {a:1};
//{value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
Object.preventExtensions(o);
//{value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
Object.seal()(对象封印)
对象封印又叫对象密封,使一个对象不可扩展并且所有属性不可配置,并返回当前对象
Object.isSealed()(测试封印)
Object.isSealed()方法用来检测该方法是否被封印
var o = {a:1,b:2};
console.log(Object.isSealed(o));//false
console.log(Object.seal(o));//{a:1,b:2}
console.log(Object.isSealed(o));//true
console.log(delete o.b);//false
o.c = 3;
console.log(o);//{a:1,b:2}
在严格模式下,删除旧属性或添加新属性都会报错
'use strict';
var o = {a:1,b:2};
console.log(Object.seal(o));//{a:1,b:2}
//Uncaught TypeError: Cannot delete property 'b' of #<Object>
delete o.b;
这个方法实际上会在现有对象上调用Object.preventExtensions()方法,并把所有现有属性的configurable描述符置为false
var o = {a:1,b:2};
//{value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
console.log(Object.seal(o));//{a:1,b:2}
//{value: 1, writable: true, enumerable: true, configurable: false}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
Object.freeze()(对象冻结)
Object.freeze()方法使一个对象不可扩展,不可配置,也不可改写,变成一个仅可以枚举的只读常量,并返回当前对象
Object.isFrozen()(检测冻结)
Object.isFrozen()方法用来检测一个对象是否被冻结
var o = {a:1,b:2};
console.log(Object.isFrozen(o));//false
console.log(Object.freeze(o));//{a:1,b:2}
console.log(Object.isFrozen(o));//true
o.a = 3;
console.log(o);//{a:1,b:2}
在严格模式下,删除旧属性、添加新属性、更改现有属性都会报错
'use strict';
var o = {a:1,b:2};
console.log(Object.freeze(o));//{a:1,b:2}
//Uncaught TypeError: Cannot assign to read only property 'a' of object '#<Object>'
o.a = 3;
这个方法实际上会在现有对象上调用Object.seal()方法,并把所有现有属性的writable描述符置为false
var o = {a:1};
//{value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(o,'a'));
console.log(Object.freeze(o));//{a:1}
//{value: 1, writable: false, enumerable: true, configurable: false}
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对象系列第三篇——神秘的属性描述符的更多相关文章
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- 深入理解javascript函数系列第三篇
前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...
- 深入理解javascript作用域系列第三篇——声明提升(hoisting)
× 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...
- 深入理解javascript作用域系列第三篇
前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...
- javascript面向对象系列第三篇——实现继承的3种形式
× 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
- 深入理解javascript作用域系列第四篇
前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...
- 深入理解javascript对象系列第一篇——初识对象
× 目录 [1]定义 [2]创建 [3]组成[4]引用[5]方法 前面的话 javascript中的难点是函数.对象和继承,前面已经介绍过函数系列.从本系列开始介绍对象部分,本文是该系列的第一篇——初 ...
- 深入理解javascript对象系列第二篇——属性操作
× 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...
随机推荐
- *HDU1848 博弈
Fibonacci again and again Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Jav ...
- Skyshop: Image-Based Lighting Tools & Shaders插件调整反射光不明显的模型
在Skyshop插件中,使用类似不锈钢等材质的模型,实时反光效果非常好,如果是其他反光不明显的模型,如砖头,建筑等,这时候就需要调整模型的Shader的高光贴图了. 如官方例子中的用砖块组成的柱子,反 ...
- php 图片上传的公共方法(按图片宽高缩放或原图)
写的用于图片上传的公共方法类调用方法: $upload_name='pic';$type = 'logo_val';$file_name = 'logo_' . $user_id .create_st ...
- UIWindow & UIWindowLevel
转自:http://www.cnblogs.com/smileEvday/archive/2012/03/27/2420362.html 一.UIWindow是一种特殊的UIView,通常在一个程序中 ...
- mysql 连接慢的问题(超过了1秒)
http://www.cnblogs.com/isenhome/p/5133547.html 症状描述 本机连接mysql速度很快 远程ping mysql主机速度正常 远程连接mysql速度需要等待 ...
- Unity3D 物体跟随鼠标旋转
float speed=1000f; void Update () { if (Input.GetMouseButton (0)) { transform.Rotate (0,-Input.GetAx ...
- 数据库中数据DELETE了怎样恢复
USE [BBDAS_FP_DATA] GO /****** Object: StoredProcedure [dbo].[Recover_Deleted_Data_Proc] Script Date ...
- 使用JDBC调用存储过程
DELIMITER $$ DROP PROCEDURE IF EXISTS `jdbc`.`addUser` $$ ),in birthday date,in money float,out pid ...
- 安卓调用百度地图api 错误 mcode参数不存在
自己的手机app里用到了百度地图sdk,希望根据手机获得的坐标来逆向到百度地图的坐标. 根据api文档拼写了url,因为是移动端,说是要添加mcode参数,然后我的url看起来如下: http://a ...
- 剑指Offer面试题:14.链表的倒数第k个节点
PS:这是一道出境率极高的题目,记得去年参加校园招聘时我看到了3次,但是每次写的都不完善. 一.题目:链表的倒数第k个节点 题目:输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题 ...