23. 理解对象

一、面向对象概念

面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但,ECMAScript 中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”


一开始,创建自定义对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法,如下所示。

<xmp>
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
    alert(this.name);
};
</xmp>


几年后,对象字面量成为创建这种对象的首选模式。前面的例子用对象字面量语法可以写成这样:

<xmp>
var person = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function(){
        alert(this.name);
    }
};
</xmp>

二、属性类型

ECMAScript 中有两种属性:数据属性和访问器属性。
1. 数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的特性。

属性描述
[[Configurable]]表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认值为 true
[[Enumerable]]表示能否通过 for-in 循环返回属性。默认值为 true
[[Writable]]表示能否修改属性的值。默认值为 true
[[Value]]包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined 。
<xmp>
var person = {
    name: "Nicholas"
};
</xmp>

这里创建了一个名为 name 的属性,为它指定的值是 “Nicholas” 。也就是说, [[Value]] 特性将被设置为 “Nicholas” ,而对这个值的任何修改都将反映在这个位置。
要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty() 方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是: configurable 、 enumerable 、 writable 和 value 。设置其中的一或多个值,可以修改对应的特性值。例如:

<xmp>
var person = {};
Object.defineProperty(person, "name", {
    writable: false,
    value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"
</xmp>


2. 访问器属性

属性描述
[[Configurable]]表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。默认值为 true
[[Enumerable]]表示能否通过 for-in 循环返回属性。默认值为 true
[[Get]]在读取属性时调用的函数。默认值为 undefined
[[Set]]在写入属性时调用的函数。默认值为 undefined

访问器属性不能直接定义,必须使用 Object.defineProperty() 来定义。

<xmp>
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 = 2005;
alert(book.edition); //2
</xmp>

注:在 不 支 持 Object.defineProperty() 方 法 的 浏 览 器 中 不 能 修 改 [[Configurable]] 和[[Enumerable]] 。IE8只能在 DOM 对象上使用这个方法,而且只能创建访问器属性。由于实现不彻底,建议不要在 IE8 中使用 Object.defineProperty()方法。

三、继承

1、原型链继承
ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。

<xmp>
function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
};
function SubType(){
    this.subproperty = false;
}

//继承了 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
    return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
</xmp>

上面代码的原型关系图如下:

SubType 继承了 SuperType ,而 SuperType 继承了 Object 。当调用 instance.toString()时,实际上调用的是保存在 Object.prototype 中的那个方法。


2、组合继承
组合继承(combination inheritance),有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。

<xmp>
function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

function SubType(name, age){
   //继承属性
    SuperType.call(this, name);
    this.age = age;
}

//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
</xmp>


3、寄生式继承
寄生式继承是创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

<xmp>
function createAnother(original){
    var clone = object(original); //通过调用函数创建一个新对象
    clone.sayHi = function(){ //以某种方式来增强这个对象
        alert("hi");
    };
    return clone; //返回这个对象
}

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
</xmp>

这个例子中的代码基于 person 返回了一个新对象—— anotherPerson 。新对象不仅具有 person的所有属性和方法,而且还有自己的 sayHi() 方法。
注:使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;

评论

0 条评论