对象的基本概念

面向对象(Object-Oriented,OO),的语言最大的特征就是它们都有类的概念,通过类可以创建任意多个具有相同属性和方法的对象。

创建自定义对象最简单的方式就是创建一个Object 的实例,然后再给他添加属性和方法。如:

var math = new Object();
math.π = function pi(){
    return Math.PI;
};

document.write(math.π());

这里借用了Math.PI,最后的结果是无论调用的是math.π还是Math.PI都会返回圆周率的值。其中,前者就是我们自己自定义的对象。又如:

var person = new Object();
person.name = "Oliver";
person.age = 18;
person.job = "Software Engineer";
person.sayName = function(){
    alert(this.name);
};

上面这个对象可以用下面的结构化图像解释清楚:

Object => person
                |-name                    
                |-age
                |-job
                |-sayName()
                            |-this.name

这里面person 是对象,又是Object 的实例,其中他有三个属性和一个方法,这个方法用于显示this.name,会被解析为person.name。

上面的例子又可以用字面量的语法写成这样:

var person = {
    name: "Oliver",
    age: 18,
    job: "Software Engineer",
    sayName: function(){
        alert(this.name);
    }
};

属性类型

根据ECMA-262 第五版,ECMAScript 中有两种属性(内部值):数据属性和访问器属性。为了表示特性的内部值,该规范把它们放在了两对方括号中,如[[Enumerable]]。

数据属性

数据属性有4 个描述其行为的特性:

  • [[Configurable]]:表示能否通过delete 删除属性;

  • [[Enumerable]]:表示能否通过for-in 循环返回属性;

  • [[Writable]]:表示能否修改属性的值;

  • [[Valu]]:包含这个属性的数据值;

其中,前三个默认都是true,最后一个默认为undefined。

如何修改数据属性默认的特性呢?这就要用到ECMAScript 5 的Object.defineProperty()方法。这个方法接收三个参数:属性所在的对象,属性的名字和一个描述符对象。描述符对象就是上面提到的4 个描述其行为的特性:configurable、enumerable、writable 和value。

修改方式如下:

var person = {};
Object.defineProperty(person, "name", {
    writable: false,
    value: "Oliver"
});

console.log(person.name); //Oliver
person.name = "Niko";
console.log(person.name); //Niko

又如:

var person = {
    name: "Troy",
    age: 18
};
Object.defineProperty(person, "name", {
    writable: false,
    value: "Oliver",
    enumerable: false
});

for (x in person){
    console.log(x); //age
}

如:

var person = {
    name: "Troy",
    age: 18
};
Object.defineProperty(person, "name", {
    value: "Oliver",
    configurable: false
});

delete person.name;
console.log(person.name) //Oliver

要注意的是,一旦把属性定义为不可配置的,就不能再把它变回可配置的了。

不要在IE8 中使用Object.defineProperty()方法。

访问器属性

该属性不包含数据值,只有一对getter 和setter 函数(两者为可选);4个 特性如下:

  • [[Configurable]]

  • [[Enumerable]]

  • [[Get]]:在读取属性时调用的函数;默认undefined;

  • [[Set]]:在写入属性时调用的函数;默认undefined;

同样,必须使用Object.defindProperty()来定义。如:

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;
        }
    }
});

book.year = 2006;
console.log(book.edition);

两个默认的属性:_year 和edition。year 前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。

对于上面的下划线不太明白的,可以参考下面一种写法:


var book = {
    year: 2004,
    edition: 1
};

Object.defineProperty(book, "setYear", {
    get: function(){
        return this.year;
    },
    set: function(newValue){
        if(newValue > 2004){
            this.year = newValue;
            this.edition += newValue - 2004;
        }
    }
});

book.setYear = 2007;
console.log(book.edition);
console.log(book.year)

两者功能相同,实际上该方法是给book 对象又设置了另一个属性即第一段代码中的_year 和第二段代码中的setYear。

另外,一般在这个方法之前,要创建访问器属性,一般都是使用两个非标准的方法:__defineGetter__()__defineSetter__()。如:

var book = {
    name: "hello"
};
book.__defineGetter__("name",function(){
    return "world";
});

document.write(book.name);

定义多个属性

ECMAScript 5 又定义了一个Object.defineProperties()方法。利用这个方法可以通过描述符一次定义多个属性。如:

var book = {};
Object.defineProperties(book, {
    _year:{
        value: 2004,
        writable: true
    },
    edition:{
        value: 1,
        writable: true
    },
    year:{
        get: function(){
            return this._year;
        },
        set: function(newValue){
            if(newValue > 2004){
                this._year = newValue;    
                this.edition += newValue - 2004;
            }
        }
    }
});
book.year = 2006;
console.log(book.year + " " + book.edition);

还真是奇葩,不知是否浏览器的问题,数据属性还是要加上为true 的writable 访问器属性才能起作用。不是默认为true 么。真是奇怪

读取属性的特征

使用ECMAScript 5 的Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。接收两个参数:所在的对象和要读取其描述符的属性名称。如:

var cars = {};
Object.defineProperties(cars, {
    name: {
        value: "Benz",
        writable: false
    },
    _color: {
        value: "White",
        writable: true
    },
    price: {
        value: 300000,
        writable: true
    },
    color: {
        get: function(){
            return this._color;
        },
        set: function(newValue){
            if(newValue == "Black"){
                this._color = "Black";
                this.price = 280000
            }else if(newValue == "White"){
                this._color = "White";
                this.price = 300000
            }
        }
    }
});
function selectColors(colorValue){
    cars.color = colorValue;
    document.write(cars.name + ": " + cars.color + "款 " + "(" + cars.price + "元人民币" + ")");
}
selectColors("Black"); //Benz: Black款 (280000元人民币)
selectColors("White"); //Benz: White款 (300000元人民币)

var descriptor = Object.getOwnPropertyDescriptor(cars, "name");
document.write(descriptor.value + descriptor.writable + descriptor.configurable + descriptor.enumerable); //Benzfalsefalsefalse

var descriptor2 = Object.getOwnPropertyDescriptor(cars, "color");
document.write(descriptor2.get + descriptor2.set + descriptor2.configurable + descriptor2.enumerable + descriptor2.value); //function () { return this._color; }function (newValue) { if(newValue == "Black"){ this._color = "Black"; this.price = 280000 }else if(newValue == "White"){ this._color = "White"; this.price = 300000 } }falsefalseundefined

JS菌
6.4k 声望2k 粉丝