数据属性的 4 个特性: - Configurable:1表示能否通过 delete 删除属性从而重新定义,2能否修改属性的特性,3能否把属性修改为访问器属性. 对象直接量里默认值 true. - Enumerable: 表示能否通过 for-in 循环返回属性. 对象直接量里默认值 true. - Writable: 表示能否修改属性的值. 对象直接量里默认值 true. - Value: 包含这个属性的数据值. 对象直接量里默认值 undefined.
- // 查看对象直接量的属性的属性特性默认值
- var people = {
- name:'jaychou',
- sayName:function () {
- console.log(this.name);
- }
- };
- /*{
- value: "jaychou", writable: true, enumerable: true, configurable: true
- }/
- console.log(Object.getOwnPropertyDescriptor(people,'name'));
- /*{
- value: ƒ, writable: true, enumerable: true, configurable: true
- }/
- console.log(Object.getOwnPropertyDescriptor(people,'sayName'));
- //getOwnPropertyDescriptor 对于继承属性和不存在的属性, 返回 undefined
要修改属性默认的特性, 使用 Object.defineProperty() 方法, 接收 3 个参数: 对象, 属性名字和描述符对象.
- // 修改属性默认特性:
- Object.defineProperty(person,'job',{
- emumerable:false,// 不可枚举
- value:'singer',
- writable:false,// 不可写
- configurable:true
- });
- /*{
- name: "jaychou", sayName: ƒ, job: "singer"
- }/
- console.log(person);
- for(var prop in person){
- // 打印 name,sayName
- console.log(prop);
- }
- // 会报错
- try{
- person.job = 'director';
- }catch (e) {
- //Cannot assign to read only property 'job' of object
- console.log(e);
- }
可以多次调用 Object.defineProperty() 方法修改同一个属性, 但在把 configurable 特性设置为 false 之后就会有限制了:
- Object.defineProperty(person,'height',{
- configurable:false,// 不可配置
- writable:true,
- value:172
- });
- try{
- Object.defineProperty(person,'height',{
- configurable:true,// 出错
- enumerable:true,// 出错
- value:175,// 正常
- writable:false,//writable 从 true 变 false 可以, false 变 true 也会出错
- });
- }catch (e) {
- //Cannot redefine property: height at Function.defineProperty
- console.log(e);
- }
- try{
- delete person.height;
- }catch (e) {
- // 设置成不可配置后也不可删除: Cannot delete property 'height' of #<Object>
- console.log(e);
- }
另外, 调用 Object.defineProperty() 方法时, 如果不指定, configurable,enumerable 和 writable 特性的默认值都是 false. 如果是修改已有属性, 则无此限制.
2. 存储器属性
存储器属性不包含数据值, 只包含包含 getter 和 setter 函数 (非必需). 在读取存储器属性时, 会调用 getter 函数, 这个函数负责返回有效的值; 在写入存储器属性时, 会调用 setter 函数并传入新值, 这个函数负责决定如何处理数据. 4 个属性特性如下: - Configurable:1表示能否通过 delete 删除属性从而重新定义,2能否修改属性的特性,3能否把属性修改为数据属性. 对象直接量的默认值 true - Enumerable: 表示能否通过 for-in 循环返回属性. 对象直接量的默认值 true - Get: 在读取属性时调用的函数. 对象直接量默认值 undefined - Set: 在写入属性时调用的函数. 对象直接量的默认值 undefined
定义存储器属性最简单的方法是使用对象直接量语法的拓展写法:
- var p = {
- x:3.0,
- y:4.0,
- //r 是可读写的存取器属性
- get r(){
- return Math.sqrt(this.xthis.x+this.ythis.y);
- },
- set r(newValue){
- var oldvalue = Math.sqrt(this.xthis.x+this.ythis.y);
- var ratio = newValue/oldvalue;
- this.x = ratio;
- this.y= ratio;
- },
- //theta 是只读存取器属性
- get theta(){
- return Math.atan2(this.y,this.x);
- }
- }
- console.log(p.r);
- p.r = 25;
使用 Object.defineProperty() 方法定义存储器属性:
- var book = {
- _year:2004,
- edition:1
- };
- Object.defineProperty(book,"year",{
- get:function () {
- return this._year;
- },
- set:function (newValue) {
- if(newValue>2004){
- this._year = newValue;
- this.edition += newValue - 2004;
- }
- }
- })
- /*{
- get: ƒ, set: ƒ, enumerable: false, configurable: false
- }/
- console.log(Object.getOwnPropertyDescriptor(book,'year'));
如例子所示, 使用存储器属性的常见方式, 即设置一个属性的值会导致其他属性发生变化. 还有一种常见就是现在流行的类似于 vue 的响应式原理, 就是把 data 中的属性都使用 defineProperty 修改为存储器属性, 可以监听到数据的变化.
3. 定义多个属性
经常要创建或修改多个属性, 这时候可以使用 Object.defineProperties() 方法, 它接收 2 个参数, 要添加或修改属性的对象和一个映射表, 包含名称和属性描述符.
- var book1 = {
- };
- Object.defineProperties(book1,{
- _year:{
- value:'2008'
- },
- editor:{
- enumerable:true,
- value:'2'
- },
- year:{
- get:function () {
- return this._year;
- },
- set:function (newValue) {
- this._year = newValue;
- this.edition += newValue - 2004;
- }
- }
- });
4. 对象的可扩展性
对象的可拓展性表示是否可以给对象添加新属性. 所有内置对象和自定义对象都是显式可扩展的, 宿主对象的可扩展性是由 JavaScript 引擎定义的.
1. 查询对象可拓展性
- var teacher = {
- age:25
- };
- //true: 代表可拓展
- console.log(Object.isExtensible(teacher));
2. 转换为不可拓展 ("锁定对象")
- Object.preventExtensions(teacher);
- //false
- console.log(Object.isExtensible(teacher));
- try{
- teacher.subject = 'math';
- }catch (e) {
- //TypeError: Cannot add property subject, object is not extensible
- console.log(e);
- }
转换成不可拓展的操作是不可逆的, 而且只能影响到对象本身的可拓展性, 如果给一个不可拓展对象的原型添加属性, 这个不可拓展对象同样会继承这些新属性.
5. 密封对象
密封对象比锁定对象更高一层, 除了不可拓展以外, 对象的所有自身属性都设置成了不可配置的. 同样密封对象操作是不可逆的.
- var tea1 = {
- subject:'math'
- };
- //false: 代表未密封
- console.log(Object.isSealed(tea1));
- Object.seal(tea1);
- try{
- Object.defineProperty(tea1,'subject',{
- //enumerable:false,// 出错
- //configurable:true,// 出错
- writable:false// 和上面说的一样, writable 从 true 变成 false 可以, false 变成 true 则出错
- });
- }catch (e) {
- console.log('出错..');
- console.log(e);
- }
- //true: 已密封
- console.log(Object.isSealed(tea1));
6. 冻结对象
冻结比密封对象多的效果是: 可以将它自有的所有数据属性设置为只读 (如果对象的存取器属性具有 setter 方法, 存取器属性将不受影响, 仍可以通过给属性赋值调用它们).
- var tea2 = {
- subject:'Chinese'
- };
- //false: 代表未冻结
- console.log(Object.isFrozen(tea2));
- Object.freeze(tea2);
- try{
- tea2.subject = 'math';
- }catch (e) {
- //TypeError: Cannot assign to read only property 'subject' of object
- console.log(e);
- }
- //true: 已冻结
- console.log(Object.isFrozen(tea2));
7. 属性特性规则总结
如果对象是不可拓展的, 则可以编辑已有的自有属性, 但不能给它添加新属性.
如果属性是不可配置的, 则不能修改它的可配置性和可枚举性.
如果存取器属性是不可配置的, 则不能修改其 getter 和 setter 方法, 也不能将它转换为数据属性.
如果数据属性是不可配置的, 则不能将它转换为存取器属性.
如果数据属性是不可配置的, 则不能将它的可写性从 false 修改为 true, 但可以从 true 修改为 false.
如果数据属性是不可配置且不可写的, 则不能修改它的值. 然而可配置但不可写属性的值是可以修改的 (做法: 先将它标记为可写的, 然后修改它的值, 最后转换为不可写的).
来源: http://www.qdfuns.com/article/51116/b70a070fefd5fc7ba341122ac1932c7f.html