ES5规范之Object增强
在ES5规范中。另一个比較重要的改进,就是Object对象的增强。ES5为Object新增了一系列函数。用于编写安全健壮的程序,今天我们就来一一介绍它们的用法。
以下就是ES5中Object新增的函数:
Object.defineProperty(object, propertyName, descriptor);
Object.defineProperties(object, descriptors);
Object.getOwnPropertyDescriptor(object, propertyName);
Object.create(prototype, descriptors);
Object.getPrototypeOf(object);
Object.keys(object);
Object.getOwnPropertyNames(object);
Object.preventExtensions(object);
Object.isExtensible(object);
Object.seal(object);
Object.isSealed(object);
Object.freeze(object);
Object.isFrozen(object);
以下我们逐一介绍:
Object.defineProperty(object, propertyName, descriptor);
defineProperty函数用于定义一个对象上的属性以及这个属性的描写叙述符。这里涉及到描写叙述符这个概念,须要先理解一下,描写叙述符是用来描写叙述一个属性的值和这个属性在执行期的訪问控制。一个描写叙述符包括以下几个声明:
configurable: 表示能否通过delete删除对象中的该属性。以及能否又一次定义该属性。默认是false,默认情况下,不能用delete删除属性,不能又一次定义该属性。须要注意的是,一旦明白设置configurable为false之后,就再也不能又一次设置这个规则为true了。
enumerable: 表示是否能通过for-in循环获得对象中的该属性,默认值是false。默认情况下,使用for-in循环将不能看到该属性出现。
writable: 表示是否能改动该属性,默认值是false。默认情况下。不能再更改该属性的值。
value: 该属性的值,默认值是undefined。通常我们会设置value为一个有意义的值。
以下是每一个声明的默认值列表:(Get和Set兴许会介绍)
这里须要注意。当configurable和writable为false的情况下,试图删除或更改相相应的属性。常规模式下操作将会被忽略,假设是严格模式。将会抛出异常。
我们的代码会加上"use
strict";来启用严格模式。关于严格模式的细节,我们兴许的文章会专门介绍。
我们先来使用defineProperty函数定义一个对象的属性:
"use strict"; var person = {}; Object.defineProperty(person, 'name', {
value: 'Scott'
}); person.name = 'John';
上面的代码我们使用defineProperty为person对象定义了一个name属性。然后试图更改它的值。
假设在常规模式下执行。更改操作将会被忽略,这里我们加上了严格模式的声明,将会抛出以下的异常:
假设通过delete试图删除name属性。相同也会得到一个异常结果:
delete person.name;
我们略微修改一下代码,为defineProperty函数的最后一个參数加入一个writable声明:
"use strict"; var person = {}; Object.defineProperty(person, 'name', {
writable: true, //add writable as true
value: 'Scott'
}); person.name = 'John';
console.log('after changing the name: ', person.name); delete person.name;
console.log('after deleting the name: ', person.name);
加入writable为true后,再次执行程序,看看结果怎样:
我们如今能够更改name属性的值了。仅仅只是试图删除name时,仍然会抛出一个异常,如今我们须要再加入一个configurable声明:
"use strict"; var person = {}; Object.defineProperty(person, 'name', {
configurable: true, //add configurable as true
writable: true, //add writable as true
value: 'Scott'
}); person.name = 'John';
console.log('after changing the name: ', person.name); delete person.name;
console.log('after deleting the name: ', person.name);
从打印结果来看。更改操作和删除操作在严格模式下都顺利运行了。如今大家应该都了解到configurable和writable的作用了吧。
configurable同意或禁止删除操作的运行。writable同意或禁止更改操作的运行。
另外。上面我们也提到过。假设明白声明了configurable为false。则不能再使用defineProperty将其定义configurable为true了,以下代码将会抛出一个异常:
'use strict'; var person = {}; Object.defineProperty(person, 'name', {
configurable: false, //declare configurable as false
writable: false, //declare writable as false
value: 'Scott'
}); //try to redefine the name's descriptor
Object.defineProperty(person, 'name', {
configurable: true,
writable: true,
value: 'Scott'
});
当外部代码试图更改对象属性时。适当的使用configurable和writable为其加一些限制,能够提高代码的安全性。这一点对模块开发很实用。
以下来介绍一下enumerable声明,我们用一个简单的演示样例来解说:
"use strict"; var person = {}; Object.defineProperty(person, 'name', {
value: 'Scott'
}); //nothing will be logged
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key + ': ' + person[key]);
}
}
上面这段代码我们不会看到person中的键值对,由于默认情况下使用defineProperty定义属性时,属性的enumerable为false。即不可被遍历,假设须要在for-in中获取到name属性,我们须要为其声明enumerable为true:
"use strict"; var person = {}; Object.defineProperty(person, 'name', {
enumerable: true, //add enumerable as true
value: 'Scott'
}); //name: Scott
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key + ': ' + person[key]);
}
}
检測一个对象的属性是否可遍历。我们还能够使用Object的另外一个原型方法,它更为方便:
var isNameEnumerable = person.propertyIsEnumerable('name');
除了上面这些之外,在defineProperty函数的descriptor中还能够使用setter和getter对属性进行定义:
var getPerson = function() {
var person = {}; var personAge = 20; Object.defineProperty(person, 'age', {
get: function() {
return personAge;
},
set: function(newAge) {
if (newAge < 0) {
newAge = 0;
}
if (newAge > 150) {
newAge = 150
} personAge = newAge;
}
}); return person;
} var person = getPerson(); person.age = -1;
console.log(person.age); //0
person.age = 200;
console.log(person.age); //150
上面代码中我们把创建person对象的操作封装在getPerson函数中。然后使用set方法和get方法定义age属性的行为。在set和get方法中,间接使用了局部变量personAge来表示person的age属性,这样的方式的优点在于,能够对赋值操作加一些验证,使其符合我们业务逻辑的要求。只是须要注意的是,不是一定非要同一时候指定setter和getter的。在仅仅定义getter时该属性不能写。仅仅指定setter时不能读。假设仅仅有getter而尝试去写、仅仅有setter尝试去读的话,严格模式下会抛出异常,所以正确地使用setter和getter才干写出高质量的代码。
Object.defineProperties(object, descriptors);
在了解上面介绍的defineProperty函数之后,对于这个函数就比較easy理解了,defineProperties函数用于一次性定义多个属性,我们用一段代码来解释:
var person = {}; Object.defineProperties(person, {
'name': {
writable: false,
value: 'Scott'
},
'address': {
writable: true,
value: 'Beijing'
}
});
代码中我们最后一个參数包括两个属性:name和address,分别都有自己的属性描写叙述信息。这样的方式比較单一属性的声明来说简单明了,在一次性有多个属性须要声明时比較有用。
Object.getOwnPropertyDescriptor(object, propertyName);
这个函数用于获取指定属性的描写叙述符。当中会包含上面提到的一些描写叙述符声明。我们直接看以下演示样例代码:
var person = {}; Object.defineProperty(person, 'name', {
configurable: true,
writable: false,
enumerable: true,
value: 'Scott'
}); var descriptor = Object.getOwnPropertyDescriptor(person, 'name');
console.log(descriptor.configurable); //true
console.log(descriptor.writable); //false
console.log(descriptor.enumerable); //true
console.log(descriptor.value); //Scott
须要注意的是。假设定义属性时使用了set和get方法,那么返回的descriptor对象也是能够通过set和get訪问到对应的setter和getter的。
接着我们来了解一下与原型有关的两个函数:
Object.create(prototype, descriptors);
create函数用于在指定原型基础之上创建一个新的对象,第一个參数即指定的原型对象,第二个參数就是我们上面介绍到的描写叙述符,里面能够定义多个属性的描写叙述信息。
原型參数仅仅能是一个对象或者指定为null,以下三种方式都能够创建一个空对象:
var obj0 = Object.create({});
var obj1 = Object.create(null);
var obj2 = Object.create(Object.prototype);
我们当然还能够指定一个包括多个属性描写叙述信息的描写叙述符參数。来创建一个新的对象。在原来对象上进行扩展。
以下这个样例我们在一个已有对象的基础之上创建一个person对象,包括name属性和gender属性,当中name可更改不可删除。gender是仅仅读状态:
var human = {
info: 'human being'
}; var person = Object.create(human, {
name: {
configurable: false,
writable: true,
enumerable: true,
value: 'Scott'
},
gender: {
get: function() {
return "Male";
}
}
}); console.log(person);
上面代码相当于在human对象上进行扩展。加入了name和gender属性。进而创建一个新对象,我们来看一下person对象的结构:
从信息打印中,我们能够看出,由于name属性是可遍历的,所以显示在Object{}中,当我们展开后。会看到gender的get方法,也能够看得到person的原型链,包括info属性的原型就是我们上面定义的human对象了,证明person对象确实是human对象的扩展。
create与defineProperties有些相象之处,不同的是。create会创建一个新的对象,而defineProperties不会,我们也能够获取它的返回值。返回值就是原对象本身。
Object.getPrototypeOf(object);
此函数用于获取指定对象的原型。来看以下这个精简过的样例:
var human = {
info: 'human being'
}; var person = Object.create(human, {
name: {
value: 'Scott'
}
}); console.log(Object.getPrototypeOf(person) === human); //true
我们使用human作为原型创建一个person对象,然后使用getPrototypeOf函数获取person对象的原型。结果会返回human。我们上面也介绍到了,person对象的确是从human对象扩展而来。getPrototypeOf获取到的原型和我们期望的是一致的。
Object.keys(object);
此函数用于获取对象中可被遍历的所有属性的key的集合。
看以下样例:
var array = ['a', 'b', 'c'];
console.log(Object.keys(array)); //["0", "1", "2"] var person = Object.defineProperties({}, {
name: {
enumerable: true,
value: 'Scott'
},
info: {
enumerable: true,
value: 'I am Scott'
},
address: {
enumerable: false, //not enumerable
value: 'Beijing'
}
}); console.log(Object.keys(person)); //["name", "info"]
首先我们创建一个数组。使用keys函数获取全部的key,结果会打印出数组的下标组成的集合。然后我们定义了person对象的属性,当中name和info都是可遍历的,address是不可遍历的,结果会打印出name和info,而不会出现address,使用keys函数的时候须要注意这一点。
Object.getOwnPropertyNames(object);
此函数与keys函数相似,只是它会返回全部的键,包含哪些不可被遍历的属性。如今我们把keys函数替换成getOwnPropertyNames,看看结果怎样:
var array = ['a', 'b', 'c'];
console.log(Object.getOwnPropertyNames(array)); var person = Object.defineProperties({}, {
name: {
enumerable: true,
value: 'Scott'
},
info: {
enumerable: true,
value: 'I am Scott'
},
address: {
enumerable: false, //not enumerable
value: 'Beijing'
}
}); console.log(Object.getOwnPropertyNames(person));
结果打印例如以下:
我们会发现,array里面添加了一个length的key,而person对象也添加了一个address的key,我们能够得知。数组对象的length被设置为不可遍历的,也证明了getOwnPropertyNames确实能够把全部的属性的key获取到。
Object.preventExtensions(object); & Object.isExtensible(object);
preventExtensions函数用于禁止指定对象的扩展,即禁止向对象中加入新的属性。我们定义一个简单的对象。然后測试一下这个函数:
'use strict'; var person = {
name: 'Scott'
}; Object.preventExtensions(person); console.log(Object.isExtensible(person)); //false //Uncaught TypeError: Can't add property age, object is not extensible
person.age = 20;
測试证明,试图加入一个age属性,严格模式下将会抛出异常,证明person对象已经是不可扩展的。
Object.seal(object); & Object.isSealed(object);
seal函数用于对指定对象进行封存,已封存的对象禁止再加入新的属性。禁止删除已有的属性,禁止又一次定义已有属性的cnofigurable为true。来看以下一段代码:
'use strict'; var person = {
name: 'Scott'
}; Object.seal(person); console.log(Object.isSealed(person)); //true person.name = 'John'; //Uncaught TypeError: Can't add property age, object is not extensible
//person.age = 20; //Uncaught TypeError: Cannot delete property 'name' of #<Object>
delete person.name;
上面我们定义了一个普通字面量的person对象,它的name属性是可写可删除可遍历的,接着我们调用seal函数对其进行封存。假设我们想要推断一个对象是否已被封存,能够使用isSealed函数。被封存的对象禁止加入属性和删除已有的属性。所以我们试图加入一个age属性和删除已有的name属性都会在严格模式下抛出异常。须要注意的是,仅仅要是可写的属性。即使被封存也能够更改其值。所以更改name的值是不会有问题的。
另外。对已被封存对象的属性又一次定义也要特别小心,由于被封存的对像使用defineProperty又一次定义规则时,仅仅能更改writable和value。不能更改原来的enumerable,对于configurable更为严苛,不能明白声明。例如以下代码列举了一些注意事项:
'use strict'; var person = {
name: 'Scott'
}; Object.seal(person); person.name = 'Jack';
console.log(person.name); //Jack //after sealed, can't declare the configurable, and can't change the original enumerable.
Object.defineProperty(person, 'name', {
//configurable: true,
writable: false,
enumerable: true, //the enumerable here must be equal to the original if declare explicitly
value: 'John'
}); //Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
person.name = 'Scott';
能够看出writable是能够更改为false的,但注意enumerable仅仅能和原来对象一样为true,而configurable在这里不能明白声明,大家能够亲自測试一下。
Object.freeze(object); & Object.isFrozen(object);
freeze函数用于对指定对象进行冻结,冻结后的对象禁止加入属性,禁止删除属性,禁止更改属性。禁止使用defineProperty更改其configurable。writable,和enumerable的值。能够说freeze函数禁止对象属性上的全部操作,只是须要注意的是,假设对象的属性也是对象类型,那么这个属性对象以下的属性是不会被冻结的,我们依然能够操作:
'use strict'; var person = {
name: 'Scott',
info: {
weight: 65,
height: 175
}
}; Object.freeze(person); console.log(Object.isFrozen(person)); //true person.info.age = 20; //add 'age' property to info
person.info.weight = 70; //change the 'weight' property
上面代码中,我们向info中加入age属性,或者更改weight的值,这些操作都是没有问题的,所以假设须要对一个对象及以下的属性进行全然的冻结。我们须要递归的对每个属性对象进行冻结。
以上就是ES5对Object的增强,有了这些强大的API,能够确保我们的代码更加安全,进而能够构建出健壮的应用。另外,在上面的演示样例中,我们多次提到了严格模式,这也是ES5规范中重要的一个方面,下次我们会对严格模式进行一个全面的概括。
ES5规范之Object增强的更多相关文章
- Javascript编码规范,好的代码从书写规范开始,增强代码的可读性,可维护性,这是相当重要的!
1. 前言 JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理.本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护. 虽然本文档是针对JavaScript设 ...
- ES5特性之Object.freeze
Object.freeze方法比Object.seal方法更严格,不仅不能扩展新对象和不可重新配置属性的特性,还不能改变对象属性的值writable(不可写)
- 09 Object
Object 在看 ES6 Object的时候,我发觉ES5 Object 的更新我并不是完全知道. 于是觉得还是看一下. 1. __proto__ 作为一个 半吊子前端开发人员. 居然不知道这个.. ...
- lodash源码分析之获取数据类型
所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! --几米 本文为读 lodash 源码的第十八篇,后续文章会更新到 ...
- Javascript高级编程学习笔记(22)—— 对象继承
继承是所有面向对象的语言最让人津津乐道的概念 许多面向对象的语言都支持两种实现继承的方式: 1.接口继承 2.实现继承 由于ECMAScript中没有函数签名,所以自然也是不支持接口继承 所以JS中能 ...
- ES5新特性:理解 Array 中增强的 9 个 API
为了更方便的对JS中Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.index ...
- 利用babel工具将es6语法转换成es5,Object.assign方法报错
一.新建工程初始化项目 1.新建工程文件夹这里起名叫做es6,然后在里面创建两个文件夹分别为src .dist如下图:(src为待转换es6 js存放目录,dist为编译完成后的es5 js存放目录) ...
- 使用Object.create 克隆对象以及实现单继承
var Plane = function () { this.blood = 100; this.attack = 1; this.defense = 1; }; var plane = new Pl ...
- Object.prototype.toString.call(arg)详解
经常能碰到Object.prototype.toString.call对参数类型进行判断,一开始只知道怎么使用,却不了解具体实现的原理,最近恶补了一下相关知识,写个笔记加强理解,有什么不对的请指教. ...
随机推荐
- Grunt打包之seajs项目【转】
原文:http://www.cnblogs.com/accordion/p/4508154.html grunt与seajs grunt是前端流行的自定义任务的脚手架工具,我们可以使用grunt来为我 ...
- IE (6-11)版本,在使用iframe的框架时,通过a标签javascript:; 和js跳转parent.location的时候 出现在新页面打开的情况
问题描述: 使用iframe的情况下,在子框架中,使用如下形式的跳转: <a href="javascript:;" onclick="parent.locatio ...
- angualr4 路由 总结笔记
使用cli命令创建根路由模块 ng g cl app.router 或自己建一个路由配置文件 如:app/app.router.ts // app/app.router.ts // 将文件修改为 im ...
- 动态创建 script 实现跨域请求数据
动态创建script标签 (由事件触发) 在我们需要请求数据的时候我们就可以动态的创建 script 标签 src设置为我们需要请求数据的地址 另外我们可以附加参数 ?后面附加参数 例如 :?参数=1 ...
- Oracle创建表空间、用户、分配权限语句
--创建表空间 create tablespace 表空间名字 logging datafile 'E:\app\sinohuarui\oradata\orcl\文件名.dbf' size 50m a ...
- c#控件攻略宝典之ListBox控件
ListBox控件的使用: 1)控件属性 Items SelectedItems SelectioModes 2)数据绑定 DataSoure DisplayMember ValueMenber 3) ...
- [转载] su和sudo
转载自http://www.cnblogs.com/haichuan3000/articles/2123633.html Mandriva 说也奇怪,用root登录的用户比一般用户还难用,当初用FC6 ...
- YII 多表联查 纵表
A id 与B a.id B id 与C b.id C id 与D c.id 查A读D数据 应用场景: order表 ordergoods表 goods表 merchant加盟商 order 与ord ...
- 如何用webgl(three.js)搭建一个3D库房-第一课
今天我们来讨论一下如何使用当前流行的WebGL技术搭建一个库房并且实现实时有效交互 第一步.搭建一个3D库房首先你得知道库房长啥样,我们先来瞅瞅库房长啥样(这是我在网上找的一个库房图片,百度了“库房” ...
- eclipse环境下基于已构建struts2项目整合spring+hibernate
本文是基于已构建的struts2项目基础上整合 spring+hibernate,若读者还不熟悉struts2项目,请先阅读 eclipse环境下基于tomcat-7.0.82构建struts2项目 ...