js中的内部属性与delete操作符
在讲解 configurable
之前,先来看一道面试题:
a = 1;
console.log( window.a ); // 1
console.log( delete window.a ); // true
console.log( window.a ); // undefined
var b = 2;
console.log( window.b ); // 2
console.log( delete window.b ); // false
console.log( window.b ); // 2
从上面的这道题可以看出两个的区别:在没有使用var声明变量时,使用delete关键词是可以进行删除的,再次获取时值就是undefined了;在使用var声明的变量,使用delete是不能删除的,再获取时值依然是2。
1. delete 操作符
delete 操作符用来删除一个对象的属性。在严格模式中,如果属性是一个不可配置(non-configurable)属性,删除时会抛出异常,非严格模式下返回 false。其他情况都返回 true。如上面的例子中,delete无法删除变量a时,则返回false;而delete能成功删除变量b,则返回true。
- delete 操作符与直接释放内存(只能通过解除引用来间接释放)没有关系。可查看内存管理页面。
- 你可以使用 delete 操作符来删除一个隐式声明的全局变量,也就是没有使用 var 定义的全局变量.全局变量其实是global对象(window)的属性.
- 如果 delete 操作符删除成功,则被删除的属性将从所属的对象上彻底消失。然后,如果该对象的原型链上有一个同名属性,则该对象会从原型链上继承该同名属性。
function Person() {
this.name = 'nodejh';
}
Person.prototype.name = '小蒋';
var student = new Person();
console.log(student.name); // nodejh
console.log(delete student.name); // true, 删除成功
console.log(student.name); // 小蒋
console.log(delete student.name); // true, 但无法删除原型中的 name 属性
console.log(student.name); // 小蒋
console.log(delete Person.prototype.name); // true, 删除成功
console.log(student.name); // undefined
2. JS 的内部属性
ECMAScript中有两种属性:数据属性和访问器属性。
2.1 数据属性
数据属性指包含一个数据值的位置,可在该位置读取或写入值,该属性有4个供述其行为的特性:
[[configurable]]:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true。
[[enumerable]]:表示是否可通过for-in循环返回属性。默认true。
[[writable]]:表示是否可修改属性的值。默认true。
[[value]]:包含该属性的数据值。读取/写入都是该值。默认为undefined;如上面实例对象 Person 中定义了 name 属性,其值为 ’nodejh', 对该值的修改都在这个位置。
要修改对象属性的默认特征(默认都为 true
),可调用 Object.defineProperty()
方法,它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurable
、enumerable
、writable
和 value
,可设置一个或多个值)。
如:
var person = { name: 'nodejh' };
Object.getOwnPropertyDescriptor(person, 'name’);
// { configurable:true,
// enumerable:true,
// value: "nodejh",
// writable: true }
Object.defineProperty(person, 'name', {
configurable: false,
enumerable: false,
writable: false,
value: 'nodejh',
});
Object.getOwnPropertyDescriptor(person, 'name’);
// { configurable:false,
// enumerable:true,
// value: "nodejh",
// writable: false}
console.log(delete person.name); // false
person.name = 'nodejh2';
console.log(person.name); // nodejh
值得注意的是,一旦 configurable
和 writable
一旦被设置为 false
,就不能再通过 Object. defineProperty
将其设置为 true
(执行会报错:Uncaught TypeError: Cannot redefine property:
)。
2.2 访问器属性
它主要包括一对 getter
和 setter
函数,在读取访问器属性时,会调用 getter
返回有效值;写入访问器属性时,调用 setter
,写入新值;该属性有以下4个特征:
访问器属性不能直接定义,必须使用defineProperty()来定义,如下:
var person = {
_age: 18
};
Object.defineProperty(person, 'isAdult', {
Configurable : false,
get: function () {
if (this._age >= 18) {
return true;
} else {
return false;
}
}
});
console.log( person.isAdult ); // true
Object.getOwnPropertyDescriptor(person, 'isAdult’);
// { configurable: false,
// enumerable: false,
// get: (),
// set: undefined }
还有一点需要额外注意一下,Object.defineProperty()
方法设置属性时,不能同时声明访问器属性(set
和 get
)和数据属性(writable
或者 value
)。意思就是,某个属性设置了 writable
或者 value
属性,那么这个属性就不能声明 get
和 set
了,反之亦然。
如若像下面的方式进行定义,访问器属性和数据属性同时存在:
var o = {};
Object.defineProperty(o, 'name', {
value: 'wenzi',
set: function(name) {
myName = name;
},
get: function() {
return myName;
}
});
// Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute,
上面的代码看起来貌似是没有什么问题,但是真正执行时会报错。
- 对于数据属性,可以取得:configurable,enumberable,writable和value;
- 对于访问器属性,可以取得:configurable,enumberable,get和set。
由此我们可知:一个变量或属性是否可以被删除,是由其内部属性 configurable
进行控制的,若 configurable
为 true
,则该变量或属性可以被删除,否则不能被删除。
3. 总结
回到最初的面试题,其中 a 是隐式声明的全局变量,也就是没有使用 var 定义的全局变量。全局变量其实是 global 对象 (window) 的属性。
a = 1;
console.log( window.a ); // 1
Object.getOwnPropertyDescriptor(window, 'a’);
// { configurable:true,
// enumerable:true,
// value: 1,
// writable: true }
console.log( delete window.a ); // true
console.log( window.a ); // undefined
var b = 2;
console.log( window.b ); // 2
Object.getOwnPropertyDescriptor(window, 'b');
// { configurable:false,
// enumerable:true,
// value: 1,
// writable: true }
console.log( delete window.b ); // false
console.log( window.b ); // 2
—