学习ECMAScript5规范中Object新增的API
2015-10-25 11:54
756 查看
Object在javascript编程中的重要性不言而喻,本文主要介绍下ECMAScript5规范Object新增的几个API。ECMAScript 5.1 (或ES5) 是ECMAScript(基于JavaScript的规范)标准最新修正。与HTML5规范进程本质类似,ES5通过对现有JavaScript方法添加语句和原生ECMAScript对象做合并实现标准化。这些新增的API还是很有意思的,给javascript增加了很多很有用的功能,比如不可变对象等。chrome浏览器对ECMAScript5规范的支持很好,我用的chrome
46。
Object.create的第二个参数,会在Object.defineProperties()中介绍。使用方式如下:
ES5中引入了getter和setter,比如下面这段代码:
使用了get和set关键字来定义某个属性的getter和setter。如果一个属性只有getter,那么就只能读取不能修改;如果只有setter,那么就只能修改不能读取。可以看到使用getter和setter能够像java一样封装我们的属性。
Object.prototype中定义了__defineGetter__、__defineSetter__、__lookupGetter__、__lookupSetter__,这4个函数名字很奇怪,因为他们不是标准的API,以后很有可能被废弃,所以不要在生产环境中使用,不过平时测试用来玩玩还是可以的,至少chrome目前还支持这几个API。
之前的javascript中对象的属性并没有什么特别,可以直接读取和修改。但是ES5引入了"属性描述符"的概念。属性描述符有下面几个选项:
如果刚开始o.a 的 configurable 设置为true,那上面代码么用错误了,并且属性会在最后被删除。这里需要特别注意:如果configurable为false,依然可以修改writable的值。
看一段下面这段代码,了解下属性描述符的默认值问题,以及我们最常用的定义属性的方式。
还有一个很重要的问题,值得注意,否则会导致下面这个错误。
我们知道属性的get和set描述符,属于accessors,不能和value或者writable描述符混用。
了解了这些之后,Object.defineProperties()就很容易了,不过是个批量方法而已。
对象经过冻结之后,属性描述符就不能再变化了。依然可以调用defineProperty,如果不改变属性描述符的值,那么是不会报错。一旦试图修改成新的值,就会报错:cannot redefine。
下面这个例子演示了一个冻结对象中的非冻结对象是可以被修改的(浅冻结)。
参考文章:
ECMAScript5规范http://www.ecma-international.org/ecma-262/5.1/
MDN开发者帮助https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
46。
Object.create(proto, [propertiesObject])
这是ECMAScript5中新增的一种创建对象的方式,能够允许我们设置新建对象的原型。// 创建对象(没有原型对象) var myObj = Object.create(null); myObj.name = "aty"; console.log("name="+myObj.name);//name=aty // Uncaught TypeError: myObj.toString is not a function myObj.toString();创建的myObj对象是没有原型的,这跟我们通过普通方式的创建对象(原型对象是Object.prototype)很不同。用Object.create的好处是原型链干净,网上有有给出了以下没有Object.create的浏览器的解决同样解决方案。以下代码不但说明了Object.create的技术内幕,同时可以支持低版本的IE同样可以实现干净的原型。
if (typeof Object.create !== 'function') { Object.create = function(o) { function F() { } F.prototype = o; return new F(); }; }
Object.create的第二个参数,会在Object.defineProperties()中介绍。使用方式如下:
//创建一个可写的,可枚举的,可配置的属性p o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });
setter和getter
之前的javascript没有什么getter/setter概念,我们都是直接读取和修改某个对象的属性。这样直接暴露出了对象的属性,在OOP中是不建议这么做的,没有做到封装。// 直接读取和修改对象的属性 var obj = {name : "aty"}; console.log(obj.name); obj.name = "qun"; console.log(obj.name);
ES5中引入了getter和setter,比如下面这段代码:
var obj = { get getName() { console.log("getName is invoked."); if(this.name === undefined) { return "aty";//默认值 } else { return this.name; } }, set setName(newName){ console.log("setName is invoked."); this.name = newName; } }; console.log("name=" + obj.getName);//aty obj.setName = "qun"; console.log("name=" + obj.getName);//qun
使用了get和set关键字来定义某个属性的getter和setter。如果一个属性只有getter,那么就只能读取不能修改;如果只有setter,那么就只能修改不能读取。可以看到使用getter和setter能够像java一样封装我们的属性。
Object.prototype中定义了__defineGetter__、__defineSetter__、__lookupGetter__、__lookupSetter__,这4个函数名字很奇怪,因为他们不是标准的API,以后很有可能被废弃,所以不要在生产环境中使用,不过平时测试用来玩玩还是可以的,至少chrome目前还支持这几个API。
var obj = { get foo() { return Math.random() > 0.5 ? "foo" : "bar"; } }; obj.__defineGetter__('gimmeFive', function() { return 5; }); obj.__lookupGetter__("foo"); // (function (){return Math.random() > 0.5 ? "foo" : "bar"})
Object.defineProperty()和Object.defineProperties()
Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。Object.defineProperties() 方法在一个对象上添加或修改一个或者多个自有属性,并返回该对象。之前的javascript中对象的属性并没有什么特别,可以直接读取和修改。但是ES5引入了"属性描述符"的概念。属性描述符有下面几个选项:
1.value
设置属性的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。var o = {}; Object.defineProperty(o, "a", {}); Object.defineProperty(o, "b", {value:1 } ); console.log(o.a); //undefined console.log(o.b); //1
2.enumerable
enumerable定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。当且仅当该属性出现在相应的对象枚举属性中,值为true。默认为 false。var o = {}; Object.defineProperty(o, "a", { value : 1, enumerable:true }); Object.defineProperty(o, "b", { value : 2, enumerable:false }); Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true for (var i in o) { console.log(i); } // 打印 'a' 和 'd' (in undefined order) Object.keys(o); // ["a", "d"] o.propertyIsEnumerable('a'); // true o.propertyIsEnumerable('b'); // false o.propertyIsEnumerable('c'); // false
3.writable
当writable设置为false时,表示non-writable,属性不能被修改,也不能被删除。var o = {}; // 创建一个新对象 Object.defineProperty(o, "a", { value : 37, writable : false }); console.log(o.a); // 打印 37 o.a = 25; // 没有错误抛出(在严格模式下会抛出,即使之前已经有相同的值) console.log(o.a); // 打印 37, 赋值不起作用。 delete o.a;//没有错误抛出(在严格模式下会抛出:Cannot delete property 'a') console.log(o.a); // 37
4.configurable
当且仅当这个属性描述符值为 true 时,该属性可能会改变,也可能会被从相应的对象删除。默认为 false。var o = {}; Object.defineProperty(o, "a", { get : function(){return 1;}, configurable : false } ); // throws a TypeError Object.defineProperty(o, "a", {configurable : true}); // throws a TypeError Object.defineProperty(o, "a", {enumerable : true}); // throws a TypeError (set was undefined previously) Object.defineProperty(o, "a", {set : function(){}}); // throws a TypeError (even though the new get does exactly the same thing) Object.defineProperty(o, "a", {get : function(){return 1;}}); // throws a TypeError Object.defineProperty(o, "a", {value : 12}); console.log(o.a); // logs 1 delete o.a; // Nothing happens(在严格模式下会报错Cannot delete property 'a' ) console.log(o.a); // logs 1
如果刚开始o.a 的 configurable 设置为true,那上面代码么用错误了,并且属性会在最后被删除。这里需要特别注意:如果configurable为false,依然可以修改writable的值。
"use strict"; var obj = { }; Object.defineProperty(obj, "name", { configurable:false, writable:true}); obj.name = "aty"; console.log(obj.name);//aty // 完全正常 Object.defineProperty(obj, "name", {writable: false}); // Cannot assign to read only property 'name' obj.name = "qun"; console.log(obj.name);
5.get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。方法将返回用作属性的值。默认为undefined。6.set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将收到作为唯一参数的新值分配给属性。默认为undefined。看一段下面这段代码,了解下属性描述符的默认值问题,以及我们最常用的定义属性的方式。
var o = {}; o.a = 1; // 等同于 : Object.defineProperty(o, "a", { value : 1, writable : true, configurable : true, enumerable : true }); // 另一方面, Object.defineProperty(o, "a", { value : 1 }); // 等同于 : Object.defineProperty(o, "a", { value : 1, writable : false, configurable : false, enumerable : false });
还有一个很重要的问题,值得注意,否则会导致下面这个错误。
Uncaught TypeError: Invalid property. A property cannot both have accessors and be writable or have a value, #<Object>
我们知道属性的get和set描述符,属于accessors,不能和value或者writable描述符混用。
var obj = {}; // 报错 Object.defineProperty(obj, "x" , { writable:false, get : function(){console.log("aty");}, set : function(){} }); // 报错 Object.defineProperty(obj, "x" , { value:11, get : function(){console.log("aty");}, set : function(){} }); // 正确 Object.defineProperty(obj, "x" , { configurable : false, enumerable : true get : function(){console.log("aty");}, set : function(){} });
了解了这些之后,Object.defineProperties()就很容易了,不过是个批量方法而已。
var obj = {}; Object.defineProperties(obj, { "property1": { value: true, writable: true }, "property2": { value: "Hello", writable: false } });
Object.freeze()/Object.isFrozen()
Object.freeze() 方法可以冻结一个对象。冻结对象是指那些不能添加新的属性,不能修改已有属性的值,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性、value、get、set的对象。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。冻结对象的所有自身属性都不可能以任何方式被修改。任何尝试修改该对象的操作都会失败,可能是静默失败,也可能会抛出异常(严格模式中)。var obj = { prop: function (){}, foo: "bar" }; // 可以添加新的属性,已有的属性可以被修改或删除 obj.foo = "baz"; obj.lumpy = "woof"; delete obj.prop; var o = Object.freeze(obj); assert(Object.isFrozen(obj) === true); // 现在任何修改操作都会失败 obj.foo = "quux"; // 静默失败 obj.quaxxor = "the friendly duck"; // 静默失败,并没有成功添加上新的属性 // ...在严格模式中会抛出TypeError异常 function fail(){ "use strict"; obj.foo = "sparky"; // 抛出TypeError异常 delete obj.quaxxor; // 抛出TypeError异常 obj.sparky = "arf"; // 抛出TypeError异常 } fail(); // 使用Object.defineProperty方法同样会抛出TypeError异常 Object.defineProperty(obj, "ohai", { value: 17 }); // 抛出TypeError异常 Object.defineProperty(obj, "foo", { value: "eit" }); // 抛出TypeError异常
对象经过冻结之后,属性描述符就不能再变化了。依然可以调用defineProperty,如果不改变属性描述符的值,那么是不会报错。一旦试图修改成新的值,就会报错:cannot redefine。
"use strict"; var obj = {name:"aty"}; Object.freeze(obj); // ok Object.defineProperty(obj, "name", {writable: false}); // ok Object.defineProperty(obj, "name", {value: "aty"}); // ok Object.defineProperty(obj, "name", {configurable: false}); // ok Object.defineProperty(obj, "name", {enumerable: true}); // Uncaught TypeError: Cannot redefine property: name Object.defineProperty(obj, "name", {writable: true}); // Uncaught TypeError: Cannot redefine property: name Object.defineProperty(obj, "name", {enumerable: false});
下面这个例子演示了一个冻结对象中的非冻结对象是可以被修改的(浅冻结)。
var obj = { internal : {} }; Object.freeze(obj); obj.internal.a = "aValue"; obj.internal.a // "aValue" // 想让一个对象变的完全冻结,冻结所有对象中的对象,我们可以使用下面的函数. function deepFreeze (o) { var prop, propKey; Object.freeze(o); // 首先冻结第一层对象. for (propKey in o) { prop = o[propKey]; if (!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)) { // 跳过原型链上的属性和已冻结的对象. continue; } deepFreeze(prop); //递归调用. } } var obj2 = { internal : {} }; deepFreeze(obj2); obj2.internal.a = "anotherValue"; obj2.internal.a; // undefined
Object.seal()/Object.isSealed()
Object.seal() 方法可以让一个对象密封,并返回被密封后的对象。密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、get、set,但是可以修改已有属性的value和writable。"use strict"; var obj = {name:"aty"}; Object.seal(obj); obj.name = 11; console.log(obj.name);//11 // ok Object.defineProperty(obj, "name", {writable:false}); // Uncaught TypeError: Cannot assign to read only property 'name' of #<Object> obj.name = 22;
Object.preventExtensions()
Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。但是仍然可以修改和删除已有属性。"use strict"; var obj = {name:"aty",age:26}; Object.preventExtensions(obj); // ok Object.defineProperty(obj, "name", {writable:false}); Object.defineProperty(obj, "name", {writable:true}); delete obj.age; // Can't add property nonExist, object is not extensible obj.nonExist = 11;
Object.isExtensible()
Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。默认情况下,对象是可扩展的:即可以为他们添加新的属性。Object.preventExtensions,Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展(non-extensible)。// 新对象默认是可扩展的. var empty = {}; assert(Object.isExtensible(empty) === true); // ...可以变的不可扩展. Object.preventExtensions(empty); assert(Object.isExtensible(empty) === false); // 密封对象是不可扩展的. var sealed = Object.seal({}); assert(Object.isExtensible(sealed) === false); // 冻结对象也是不可扩展. var frozen = Object.freeze({}); assert(Object.isExtensible(frozen) === false);
Object.keys()
Object.keys() 返回一个由给定对象的所有可枚举自身属性的属性名组成的数组。这与for...in不同,for...in会返回对象原型上的属性,而Object.keys()不会查找原型。var obj = Object.create({"base":12}); Object.defineProperty(obj,"x",{enumerable:false}); Object.defineProperty(obj,"y",{enumerable:true}); console.log(Object.keys(obj));//["y"] for(var key in obj) { console.log(key);//base,y }
Object.getOwnPropertyNames()
Object.getOwnPropertyNames()返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。var obj = Object.create({"base":12}); Object.defineProperty(obj,"x",{enumerable:false}); Object.defineProperty(obj,"y",{enumerable:true}); console.log(Object.keys(obj));//["y"] console.log(Object.getOwnPropertyNames(obj));//["x","y"]
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor() 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)。如果指定的属性存在于对象上,则返回其属性描述符(property descriptor),否则返回 undefined。则个方法对于我们了解属性的性质,非常有用。var o, d; o = { get foo() { return 17; } }; d = Object.getOwnPropertyDescriptor(o, "foo"); // d is { configurable: true, enumerable: true, get: /*访问器函数*/, set: undefined } o = { bar: 42 }; d = Object.getOwnPropertyDescriptor(o, "bar"); // d is { configurable: true, enumerable: true, value: 42, writable: true } o = {}; Object.defineProperty(o, "baz", { value: 8675309, writable: false, enumerable: false }); d = Object.getOwnPropertyDescriptor(o, "baz"); // d is { value: 8675309, writable: false, enumerable: false, configurable: false }
Object.getPrototypeOf()
以前的javascript中我们没有办法直接操作原型,这个方法对于我们研究js原型继承很有用。var proto = {}; var obj = Object.create(proto); Object.getPrototypeOf(obj) === proto; // true
参考文章:
ECMAScript5规范http://www.ecma-international.org/ecma-262/5.1/
MDN开发者帮助https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
相关文章推荐
- ECMAScript 5中的属性描述符详解
- 实例详解ECMAScript5中新增的Array方法
- ECMAScript 5严格模式(Strict Mode)介绍
- JavaScript高级程序设计(第3版)学习笔记13 ECMAScript5新特性
- JavaScript设计模式之原型模式(Object.create与prototype)介绍
- JavaScript中使用Object.create()创建对象介绍
- JavaScript函数式编程
- JavaScript (Array) map 方法
- ECMA-262,第 5 版 最新 JavaScript 规范
- JavaScript学习 - Object.defineProperty()
- ECMAScript5中数组方法的性能问题
- JavaScript初始化数组两种方式间的差异
- Web前端面试指导(三十六):["1", "2", "3"].map(parseInt) 答案是多少?
- JavaScript链式调用计算器
- 原型链的两种继承方式及区别
- 创建对象的几种方式
- 原型继承总结
- 再谈原型链,深刻理解原型及原型链(案例分析,详细图解,一目了然)
- JavaScript中数组Array.prototype的常用的方法总结
- ECMAScript5 Object的新属性方法(包含Object.create()、Object.defineProperty()等)