ES5对象的的创建及属性状态维护分析
来源:互联网 发布:oracle数据库使用手册 编辑:程序博客网 时间:2024/06/06 06:48
在说属性之前,我们先来了解一下ES5的新方法,Object.create()函数。
新的对象创建方法
在旧的“原型继承”观念中,它的本质上是“复制原型”,即:以原型为模板复制一个新的对象。然而我们应该注意到一点事实:在这个思路上,“构造器函数”本身是无意义的。更确切的说,构造器函数对实例的修饰作用可有可无,例如:
//在构造器中修饰对象实例function MyObject(){ this.yyy = ...;}
当意识到这一点后,ES5实现Object.cerate()这样一种简单的方法,通过这一方法将“构造器函数”从对象创建过程中赶了出去。在新的机制中,对象变成了简单的“原型继承+属性定义”,而不再需要“构造器”这样一层语义,例如:
//新的对象创建方法newObj = Object.create(prototypeObj,PropertyDescriptors);
这里的PropertyDescriptors是一组属性描述符,用于声明基于prototypeObj这个原型之上的一些新的属性添加或修改,它与defineProperties()方法中的props参数是一样的,并在事实上也将调用后者。它的用法如下例所示:
var aPrototypeObject = {name1:"value1"};var aNewInstance = Object.create(aPrototypeObject,{ name2:{value:'value2'}, name3:{get:function(){ return 'value3' }}})
很显然,在这种新方案中我们看不到类似MyObject()那样的构造器了。事实上在引擎实现Object.create()时也并不特别地声明某个构造器。
所以,所有由Object.create()创建的对象实例具有各自不同的原型(这取决于调用create()方法时传入的参数),但它们的constractor值指向相同的引用——引擎内建的Object构造器。
属性状态维护
ES5中在Object()上声明了三组方法,用于维护对象本身在属性方面的信息,如下表(Markdown不会使用分组列表,大家凑合看看。。如果有知道的也告诉我一下哈~)
其中,preventExtensions、seal和freeze三种状态都是针对对象来操作的,会影响到所有属性的性质的设置。需要强调的有两点:
- 由原型继承来的性质同样会受到影响
- 以当前对象作为原型时,子类可以通过重新定义同名属性来覆盖这些状态
更进一步的说,这三种状态是无法影响子类使用defineProperty()和defineProperties()来“重新定义(覆盖)”同名属性的。
本质上说,delete运算是用于删除运算对象属性的属性描述符,而非某个属性。
取属性列表
取属性列表的传统方法是使用for…in语句。为方便后续讨论,我们先为该语句封装一个与Object.keys()类似的方法:
Object.forIn = function(obj){ var Result = []; for(var n in obj) Result.push(n); return Result;}
forIn()得到的总是该对象全部可见的属性列表。而keys()将是其中的一个子集,即“自有的(不包括继承而来的)”可见属性列表。下面的例子将显示二者的不同:
var obj1 = {n1:100};var obj2 = Object.create(obj1,{n2 : {value :200,enumerable:true}});//显示'n1' , 'n2'// - 其中n1继承自obj1alert(Object.forIn(obj2));//显示'n2'alert(Object.keys(obj2));
getOwnPropertyNames()得到的与上述两种情况都不相同。它列举全部自有的属性,但无论它是否可见。也就是说,它是keys()所列举内容的超集,包括全部可见和不可见的、自有的属性。仍以上述为例:
// (续上例)//定义属性名n3,其enumerable性质默认为falseObject.defineProperty(obj2,'n3',{value:300})//仍然显示'n1','n2'// - 新定义的n3不可见alert(Object.forIn(obj2));//显示'n2'alert(Object.keys(obj2));//显示n2,n3alert(Object.getOwnPropertyNames(obj2));
使用defineProperty来维护属性的性质
在defineProperty()或defineProperties()中操作某个属性时,如果该名字的属性未声明则新建它;如果已经存在,则使用描述符中的新的性质来覆盖旧的性质值。
这也意味着一个使用”数据属性描述符”的属性,也可以重新使用”存取属性描述符”——但总的来说只能存在其中一个。例如:
var pOld,pNew;var obj = { data : 'oldValue'}//显示'value,writable,enumerable,configuable'pOld = Object.getOwnPropertyDescriptor(obj,'data');alert(Object.keys(pOld));//步骤一:通过一个闭包来保存旧的obj.data的值Object.defineProperty(obj,'data',function(oldValue){ return { get:function(){ return oldValue}, configurable:false }}(obj.data))//显示'get,set,enumerable,configurable'pNew = Object.getOwnPropertyDescriptor(obj,'data');alert(pNew);//步骤二:测试使用重定义的getter来取obj.data的值// - 显示 'oldValue'alert(obj.data);//步骤三:(测试)尝试再次声明data属性// - 由于在步骤一中已经设置configurable为false,因此导致异常(can't redefine)。Object.defineProperty(obj,'data',{value:100});
对于继承自原型的属性,修改其值的效果
如果某个从原型继承来的属性是可写的,并且它使用的是”数据属性描述符”,那么在子类中修改该值,将隐式地创建一个属性描述符。这个新属性描述符将按照”向对象添加一个属性”的规格来初始化。即:必然是数据属性描述符,且Writable,Enumerable和Configurable均为true值。例如:
var obj1 = {n1 : 100};var obj2 = Object.create(obj1);//显示为空// - 重置n1的enumerable性质为false,因此在obj1中是不可见的Object.defineProperty(obj1,'n1',{enumerable:false})alert(Object.keys(obj1));//显示为空// - n1不是obj2的自有属性alert(Object.getOwnPropertyNames(obj2));//显示n1// - 由于n1赋值导致新的属性描述符,因此n1成为了自有的属性obj2.n1 = 'newValue';alert(Object.getOwnPropertyNames(obj2));//显示n1,表明n1是可见的// - 由于新的属性描述符的enumerable重置为true,因此在obj2中它是可见的alert(Object.keys(obj2));
如果一个属性使用的是”存取属性描述符”,那么无论它的读写性为何,都不会新建属性描述符。对子类中该属性的读写,都只会忠诚地调用(继承而来的、原型中的)读写器。
重写原型继承来的属性的描述符
使用defineProperty()或defineProperties()将重新定义该属性,会显式的创建一个属性描述符。在这种情况下,该属性也将变成自雷对象中”自有的”属性,它的可见性等性质就由新的描述符来决定。
与上一小节不同的是,这与原型中该属性是否”只读”或是否允许修改性质(configurable)无关。
这可能导致类似如下的情况:在父类中某个属性时只读的,并且不可修改其描述符性质的,但是在子类中,同一个名字的属性却可以读写并可以重新修改性质。更为严重的是,仅仅观察两个对象实例的外观,我们无法识别这种差异是如何导致的。下面的示例说明这种情况:
var obj1 = {n1 : 100};var obj2 = Object.create(obj1);//对于原型对象obj1,修改其属性n1的性质,使其不可列举、修改、且不能重设性质Object.defineProperty(obj1,'n1',{writable:false,enumerable:false,configurable:false});//显示为空,obj1.n1是不可列举的alert(Object.keys(obj1));//由于不可重设性质,因此对obj1.n1的下述调用将导致异常//Object.defineProperty(obj1,'n1',{configurable:true});
接下来我们观察”重新定义属性”带来的效果:
//(续上例)//重新定义obj2.n1Object.defineProperty(obj2,'n1',{value:obj2.n1,writable:true,enumerable:true,configurable:true});//显示newValue'// - 结论:可以通过重定义属性,使该属性从"只读"变成"可读写"(以及其他性质的变化)obj2.n1 = 'newValue';alert(obj2.n1);//列举obj2的自有性质,结果显示:n1// - 现在n1是自有的属性了alert(Object.getOwnpropertyNames(obj2));
从表面上看,一个父类中只读的属性在子类变成了可读写。而且,一旦我们用delete删除该属性,它又会恢复父类中的值和性质。例如:
//尝试删除该属性// - 显示100,即它在原型中的值delete obj2.n1;alert(obj2.n1);
再次强调这一事实:在ES5中没有任何方法可以阻止上述过程。也就是说,我们无法阻止子类对父类同名属性的重定义,也无法避免这种重定义可能带来的业务逻辑问题。
- ES5对象的的创建及属性状态维护分析
- ES5中对象属性的访问描述符
- 维护UI对象状态的消息
- JAVA对象的属性值自动维护
- es5下对象相关的属性和对象的处理方法
- 物料的维护状态
- es6中"类"--class的方式创建对象和es5中正常用法比较
- javascript中面向对象特性、对象的创建方式及对象属性的特性
- 创建线程可分离状态的属性
- XMLHttpRequest对象的status属性状态吗
- 维护ASP的会话状态
- 业务账号的状态维护
- Hibernate 实体对象的状态及转化
- Hibernate 实体对象的状态及转化
- 我所接触到的ES5还有console对象
- ES5数组对象的forEach, map, filter方法
- JavaScript对象的创建和属性访问
- OC-对象创建、属性的调用
- HTML学习笔记2
- 给VMware下的Linux扩展磁盘空间(以CentOS6.3为例)
- android-async-http源码宏观分析
- servlet 剖析
- Appium常用方法介绍
- ES5对象的的创建及属性状态维护分析
- Android应用集成百度定位及导航
- Linux更新程序脚本
- Backbone入门基础 - 集合
- ScrollView嵌套GridView需要处理的事件
- children与childNodes的区别
- 建议119:不要使用自己的加密算法
- FMS 安装测试 自己进行
- 2016/9/7课上作业