《javascript面向对象精要》总结(二)

理解对象

属性的定义,探测,删除:

我们知道,我们可以在任何时候给js的对象去添加属性。至于添加属性的具体过程,是javascript在对象上调用了一个叫[[Put]]的内部方法,该方法会在对象上创建一个节点来保存属性(自有属性)。当已有的属性被设置一个新的值时,调用的是一个叫[[Set]]的方法,该方法将属性的当前值替换成新值。判断一个属性是否存在,有时我们会这样做:

if(person.name){
    //do something
}

但是,当name的值是一个对象,非空字符串,非零数字或者true时,会评估为真,但是当值是null,undefined,0,false,NaN或空字符串时,会被评估为假。显而易见这里就会存在一些问题了。

更加可靠的是使用in操作符,用法都知道,不说了。但是in操作符还会检查原型属性,这有可能也不是我们想要的,我们还可以使用hasOwnProperty()这个方法。每个javascript对象都有这个方法。

当我们想要删除一个对象的某个属性的时候,使用delete操作符就可以了。

属性的枚举:

典型的方法是使用for-in循环进行迭代(只能遍历可枚举的属性),他也会遍历原型属性:

var property;
for(property in object){
    console.log("Name:"+property);
    console.log("Value:" + object[property]);
}

ECMAScript5中引入了Object.keys()方法,可以获取对象的属性名字数组(自有属性,不包含原型属性):

var property = Object.keys(object);
var i len;
for (i=0,len=peoperty.length;i<len;i++){
    console.log("Name:"+property[i]);
    console.log("Value:" + object[property[i]]);
}
属性的类型:

属性的类型有两种,一种是数据属性,我们之前用到的属性都是数据属性,数据属性包含一个值。
访问器属性不包含值,是定义了一个当前属性被读取时调用的函数,和一个当属性被写入是调用的函数(可以是一个也可以是两个都有)。

var person = {
    _name:"Shi",//前置下划线是一个俗称的命名规范,表示该属性被认为是私有,但是实际上还是公开的。

    get name(){
        return this._name;
    },

    set name(value){
        this._name = value;
    }
};

console.log(person.name);
person.name = "SHI";
console.log(person.name);
属性特征:

通用的属性特征:[[Enumerable]]决定了属性是否可以被枚举,[[Configurable]]决定了属性是否可以被配置。我们平时声明的属性都是可枚举,可配置的,所以你可以随时删除和更改。改变属性的特征可以使用Object.defineProperty()方法,该方法接受三个参数:拥有该属性的对象,属性名,包含需要设置的特征的属性描述对象:

var person = {
    name:"Shi"
}

Object.defineProperty(person,"name",{
    enumerable:false
});

console.log("name" in person);    //true
console.log(person.propertyIsEnumerable("name"));    //false

var properties = Object.keys(person);
console.log(properties.length);        // 0

Object.defineProperty(person,"name",{
    configurable:false
});

delete person.name;        //严格模式下会导致错误,非严格模式会失败。
console.log(person.name);    //"Shi"

//状态已经变成不可配置,不能修改了。
Object.defineProperty(person,"name",{
    configurable:true
});

数据的属性特征:[[Value]]包含属性值,当你在对象上创建属性时该特征被自动赋值。[[Writable]]是一个布尔值,指示该属性是否可以写入。所有属性都是可写的,除非你另外指定。

var person = {};

Object.defineProperty(person,"name",{
    configurable:true,
    enumerable:true,
    value:"Shi",
    writable:true
});
//Object.defineProperty()定义新的属性时,要为所有的特征值制定一个值,否则布尔型的特征值会被制定为false。

访问器属性特征:访问其属性不需要存值,所以没有[[Value]]和[[Writable]],取而代之的是[[Get]]和[[Set]]。

var person = {
    _name:"Shi"
};
Object.defineProperty(person,"name",{
    get:function(){
        return this._name;
    },
    set:function(value){
        this._name = value;
    },
    enumerable:true,
    configurable:true
});

当然,从上面的代码可以知道,当我们想要创建一个只能读取的属性时,只需要定义一个get访问器属性就可以了。其他的特征属性不设置则默认为(false)。下面说说获取属性特征的方法:

var person = {
    name:"Shi"
};
var descriptor = Object.getOwnPropertyDescriptor(person,"name");
console.log(descriptor.enumerable);
console.log(descriptor.configurable);
console.log(descriptor.writable);
console.log(descriptor.value);
//非常清晰,不多说。
禁止修改对象:

对象也和属性一样,具有指导其行为的内部特征。[[Extensible]]指明该对象本身是否可以被修改。我们之前创建的所有对象默认都是可以扩展的,这意味着新的属性可以随时被添加。当我们设置了[[Extensible]]为false,就可以禁止新的属性被添加。有三种方法可以锁定对象属性。
禁止扩展:使用Object.preventExtensions()创建一个不可扩展的对象。该方法接受一个参数,就是你希望使其不可扩展的对象。可以使用Object.isExtensible()来检查[[Extensible]]的值。

var person = {
    name:"Shi"
};
console.log(Object.isExtensible(person));    //true
Object.preventExtensions(person);
console.log(Object.isExtensible(person));    //false

person.sayName = function(){
    console.log(this.name);
}
console.log("sayName" in person);    //false

对象封印:被封印的对象不可扩展而且所有的属性都不可配置,也就是不仅不能添加属性,也不能删除属性或者改变其类型,只能读取属性的值。可以是用Object.seal()来封印一个对象,该方法调用的时候,该属性的[[Extensible]]设置为false,其所有属性的[[configurable]]特征被设置为flase。可以使用Object.isSealed()来判断一个对象是否被封印。

var person = {
    name:"Shi"
};
console.log(Object.isExtensible(person));    //true
console.log(Object.isSealed(person));    //false

Object.seal(person);
console.log(Object.isExtensible(person));    //false
console.log(Object.isSealed(person));    //true

person.sayName = function(){
    console.log(this.name);
}
console.log("sayName" in person);    //false
person.name = "Grey";
console.log(person.name);    //"Grey"

对象冻结:和封印一样,但是冻结的对象是不能写入数据的。使用Object.freeze()来冻结一个对象。使用Object.isFrozen()来判断一个对象是否被冻结。

Object.freeze(person);
person.name = "Grey";
console.log(person.name);    //"Shi"