第六章、面向对象的程序设计
6.1 理解对象
对象:是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。我们可以把ECMAScript的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数。
创建对象
// 创建一个Object的实例,再为它添加属性和方法
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function(){
alert(this.name);
};
// 通过对象字面语法
var person = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function(){
alert(this.name);
}};
对象属性
Object.defineProperty():可以修改属性默认的特性。示例:
var person = {};
Object.defineProperty(person, "name", {
configurable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
delete person.name;
alert(person.name); //"Nicholas"
Object.defineProperties():利用这个方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数:第一个对象是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。示例:
var book = {};
Object.defineProperties(book, {
_year: { value: 2004 },
edition: { value: 1 },
year: {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
数据属性
[[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
[[Enumerable]]:表示能否通过for-in循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
[[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined。
访问器属性
[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true。
[[Enumerable]]:表示能否通过for-in循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为true。
[[Get]]:在读取属性时调用的函数。默认值为undefined。
[[Set]]:在写入属性时调用的函数。默认值为undefined。
为解决使用同一个接口创建很多对象,会产生大量的重复代码。衍生出了以下几种对象创建模式。
// 工厂模式:缺点就是不能解决对象识别的问题。
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var o1 = createPerson('张三',18,'打杂');
var o2 = createPerson('李四',18,'打杂');
o1.sayName() // 张三
o2.sayName() // 李四
// 构造函数模式:缺点就是一些可以通用的对象方法,都会在每一个对象实例上再创建一次,造成不必要的资源浪费。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
alert(person1.sayName == person2.sayName); //false
原型模式
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如下面的例子所示。
//
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
Object.getPrototypeOf(person1) // 取得对象实例的原型
person1.hasOwnProperty() //用来检测一个属性存在实例中(true),还是原型中(false)。
// in 操作符
alert("name" in person1); //true ,无论属性是实例或原型的,只要能访问到就返回true
继承
javascript的继承主要依靠原型链,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。下来看继承实现:
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
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true