面向对象的程序设计
一、理解对象
1.数据属性(configurable,enumerable,writable,value)
Object.defineProperty()方法:
作用:修改属性默认的特性
接收3个参数:属性所在对象,属性的名字,一个描述符对象
例如:
<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var person={};
Object.defineProperty(person,"name",{ //name是数据属性,可以在这里修改它的4个默认值
<span style="white-space:pre"> </span>configurable:false, //这里可以修改上述四个属性的值(true/false)
<span style="white-space:pre"> </span>value:"Nicholas"
});</span></span>
数据属性的值可以直接赋予,即,不通过Object.defineProperty()方法,例如:
var person={
name:"Nicholas"
};
2.访问器属性(configurable,enumerable,get,set)
注意:访问器属性不能直接定义,只能通过Object.defineProperty()定义。
举例:
<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var book={
_year:2004,
edition:1
};
Object.defineProperty(book,"year",{ //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</span></span>
附:定义访问器的旧有方法:
<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var book={
_year:2004,
edition:1
};
book._defineGetter_("year",function(){
return this._year;
});
book._defineSetter_("year",function(newValue){
if(newValue>2004){
this._year=newValue;
this.edition+=newValue-2004;
}
});
book.year=2005;
alert(book.edition); //2</span></span>
3.定义多个属性
Object.defineProperties()方法:
接收两个参数:
要操作的对象、对象的属性
例如:
<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">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;
}
}
}
});</span></span>
4.读取属性特性的方法
Object.getOwnPropertyDescriptor():
接收两个参数:
属性所在对象,属性名称
用法举例:
var descriptor=Object.getOwnPropertyDescriptor(book,"_year");
alert(descriptor.value); //可以访问到属性_year的value(value,configurable等特性)
二、创建对象
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("Nicholas",29,"software engineer");
var person2=createPerson("Greg",25,"doctor");
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("Nicholas",29,"software engineer");
var person2=new Person("Greg",25,"doctor");
这里,person1、person2是Person的两个不同的实例,可以理解为,Person是我们自定义的一个类,person1、person2是为这个类创建的两个对象。
两个对象都有一个constructor(构造函数)属性,该属性指向Person。即:person1.constructor==Person;
一些注意点:
构造函数的调用:
//当作构造函数调用
var person=new Person("Nicholas",29,"software engineer");
person.sayName();//"Nicholas"
//当作普通函数调用
Person("Greg",25,"doctor");//直接调用,默认添加到window(相当于把这里创建的Person的属性和方法都赋给了window对象)
window.sayName();//"Greg"
//在另一个对象的作用域中调用
var o=new Object();
Person.call(o,"Sherry",20,"student");
o.sayName();//"Sherry"
3.原型模式
说明:我们创建的每一个函数都有一个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"
如果创建实例后,对原型对象的属性进行"修改",实例对象会自动给自己添加一个属性,浏览器访问时,会先检查实例对象自己的属性,如果找到了想要获得的属性的名称,则不会再去查找原型对象的属性,也就是,会屏蔽原型对象 相应的属性。例如:
person1.name="Sherry";
alert(person1.name); //"Sherry"--来自实例
alert(person2.name); //"Nicholas"--来自原型
若赋值后,又想删除实例属性,则用delete操作符:
delete person1.name;
alert(person1.name); //"Nicholas"
检测一个属性是实例属性还是原型属性:
hasOwnProperty():
alert(person1.hasOwnProperty("name")); //检查person1有没有名为name的实例属性(true/false)
检测一个属性是否存在于一个对象中(存在于实例中或实例的原型中皆可):
in操作符:
alert("name" in person1); //true(这里,就算person1没有name这个实例属性,也返回true,因为原型对象中包含name属性)
取得对象上所有可枚举的实例属性(实例对象,只返回实例属性;原型对象,返回原型属性):
Object.keys():
接受参数:对象(原型对象、实例对象皆可)
返回:该对象所有可枚举属性组成的字符串数组
取得对象上所有实例属性(无论是否可枚举):
getOwnPropertyNames():同样传入对象
更简单的原型语法:
function Person(){
}
Person.prototype={
constructor:Person,
name:"Nicholas",
age:29,
job:"software engineer",
sayName:function(){
alert(this.name);
}
};
注意:这样相当于重写了默认的prototype对象,因此,这里的原型对象的constructor属性默认不再指向Person构造函数,而是指向Object构造函数。因此,如果需要constructor依然指向Person,需要显式设定,如代码红色部分。不过,这样的设定,会导致constructor的enumerable特性被设置为true(默认是false)
原型的动态性:
function Person(){
}
var friend=new Person();
Person.prototype={
constructor:Person,
name:"Nicholas",
age:29,
job:"software engineer",
sayName:function(){
alert(this.name);
}
};
friend.sayName(); //error解析:
在没有给Person()的原型对象添加属性和方法时,原型对象默认只有一个constructor属性,指向Person构造函数。这时,创建的实例对象friend,指向这个只有constructor属性的原型对象。而后,重写了Person的原型对象,如例子中这样的字面量写入法,相当于给Person创建了一个新的原型对象,在这之后创建的实例对象,都会指向这个新的原型对象,也就具有name这些属性了。但是,friend已经创建完毕,它指向的依然还是重写前的那个原型对象,因此,它不具有sayName()方法,发生错误。
4.构造函数模式+原型模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["Sherry","Court"];
}
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}
}
var person1=new Person("Nicholas",29,"software engineer");
var person2=new Person("Greg",27,"doctor");
person1.friends.push("Van");
alert(person1.friends); //"Sherry,Court,Van"
alert(person2.friends); //"Sherry,Court" 注:如果是单纯的原型模式,这里返回值同上行,由原型对象的共享造成
5.动态原型模式
原理:通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
if(typeof this.sayName!="function"){ //这里,只在sayName()方法不存在的情况下,才会将它添加到原型中。这段代码只在初次调用构造函数时会执行。
Person.prototype.sayName=function(){
alert(this.name);
};
}
}
var friend=new Person("Nicholas",29,"software engineer");
friend.sayName();
6.寄生构造函数模式(建议在可以使用其他模式的情况下,不要使用这种模式)
基本思想:
创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。
function Person(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 friend=new Person("Nicholas",29,"software engineer"); //注意这里用new调用,和工厂模式不同
friend.sayName(); //"Nicholas"
这个模式可以在特殊的情况下用来为对象创建构造函数:
function SpecialArray(){
var values=new Array();
values.push.apply(values,arguments);
values.toPipedString=function(){
return this.join("|");
};
return values;
}
var colors=new SpecialArray("red","blue","green");
alert(colors.toPipedString()); //"red|blue|green"
7.稳妥构造函数模式
function Person(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(name); //这里与寄生构造函数不同
};
return o;
}
var friend=Person("Nicholas",29,"software engineer"); //这里不用new,与寄生构造函数不同
friend.sayName(); //"Nichoals"这里,变量friend中保存的是一个稳妥对象,除了调用sayName()方法外,没有别的方式可以访问其数据成员,因此这种模式相对安全。