- 我们可以ECMAScript的对象想象成散列表:无非就是一组名值对,其中值是数据或函数。
- 每个对象都是基于一个引用类型创建的,这个引用类型可以使第五章讨论的原生类型,也可以是开发人员定义的类型。
6.1 理解对象
对象字面量方法
6.1.1 属性类型
ECMAScript中有两种属性:数据属性和访问器属性。
6.2 创建对象
使用一个同一个接口创建很多对象,会产生大量的重复代码。
6.2.1 工厂模式
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 person1 = createPerson("zhang",22,"software engineer");
工厂模式虽然解决了创建多个对象的问题,但是没有解决对象识别的问题(即怎样知道一个对象的类型)。
6.2.2 构造函数模式
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person("zhang",22,"doctor");
创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上回经历四个步骤:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此this指向了这个新对象);
- 执行构造函数中的代码
- 返回新对象
构造函数模式和工厂模式差别:
- 没有显示创建对象
- 直接将属性和方法赋给了this对象
- 没有return语句
构造函数的主要问题,就是每个方法都要在每个实例上重新创建一次。ECMAScript中的函数是对象,因此每定义一个函数,也就是实例化一个对象,不同实例上的同名函数是不相等的。
function Person(){
this.sayName = new Function("alert(this.name)"j);//与声明函数在逻辑上是等价的
}
alert(person1.sayName == person2.sayName);//false
6.2.3 原型模式
每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含由特定类型的所有实例共享的属性和方法。
function Person(){}
Person.prototype.name = "zhang";
Person.prototype.age = 22;
Person.prototype.job = "doctor";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.name = "jian";
person1.sayName();//jianvar person2 = new Person();
person2.sayName();//zhang
hasOwnProperty()方法可以检测一个属性是存在与实例中还是存在与原型中。这个方法(不要忘记他是从Object继承来的)只在给定属性存在于对象实例中时,才会返回true。
function Person(){}
Person.prototype.name = "zhang";
Person.prototype.age = 22;
Person.prototype.job = "doctor";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.name = "jian";
person1.sayName();var person2 = new Person();
person2.sayName();
function Person(){}
Person.prototype.name = "zhang";
Person.prototype.age = 22;
Person.prototype.job = "doctor";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.name = "jian";
person1.sayName();var person2 = new Person();
person2.sayName();
通过使用 hasOwnProperty() 方法,什么时候访问的是实例属性,什么时候访问的是原型属性就一清二楚了。调用 person1.hasOwnProperty( "name") 时,只有当 person1 重写 name 属性后才会返回 true ,因为只有这时候 name 才是一个实例属性,而非原型属性。图 6-2 展示了上面例子在不同情况下的实现与原型的关系(为了简单起见,图中省略了与 Person 构造函数的关系)。
for-in方法
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();
var person2 = new Person();
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
person1.name = "Greg";
alert(person1.name); //"Greg" ——来自实例
alert(person1.hasOwnProperty("name")); //true
alert("name" in person1); //true
alert(person2.name); //"Nicholas" ——来自原型
alert(person2.hasOwnProperty("name")); //false
alert("name" in person2); //true
delete person1.name;
alert(person1.name); //"Nicholas" ——来自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
调用 "name" in person1 始终都返回 true ,无论该属性存在于实例中还是存在于原型中。同时使用 hasOwnProperty() 方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中,如下所示。
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
更简单的原型语法
function Person(){
}
Person.prototype = {
name:"zhang",
sayName:function(){
alert(this.name);
}
}
在上面的代码中,我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外: constructor 属性不再指向 Person 了。前面曾经介绍过,每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的 prototype 对象,因此constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数。此时,尽管 instanceof操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了,如下所示。
var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true
如果constructor很重要,可以如下代码设置:
function Person(){
}
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};