首页 » 性感的程序员,nodejh » js中的内部属性与delete操作符

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() 方法,它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurableenumerablewritablevalue,可设置一个或多个值)。

如:

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 

值得注意的是,一旦 configurablewritable 一旦被设置为 false,就不能再通过 Object. defineProperty 将其设置为 true(执行会报错:Uncaught TypeError: Cannot redefine property:)。

2.2 访问器属性

它主要包括一对 gettersetter 函数,在读取访问器属性时,会调用 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() 方法设置属性时,不能同时声明访问器属性(setget)和数据属性(writable 或者 value )。意思就是,某个属性设置了 writable 或者 value 属性,那么这个属性就不能声明 getset 了,反之亦然。

如若像下面的方式进行定义,访问器属性和数据属性同时存在:

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 进行控制的,若 configurabletrue,则该变量或属性可以被删除,否则不能被删除。

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

MDN: delete
js中的内部属性与delete操作符