创建对象《javascript高级程序设计》阅读笔记
下面记录一下创建对象的几种方式:
1,最简单的方法:
var person=new Object();
person.name="rason2008";
person.age=20;
person.job="student";
person.sayName=function(){
alert(this.name);
};
此方法缺点:创建很多对象会产生大量重复代码,下面的工厂模式可以解决此问题
2,工厂模式:
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;
}
此方法缺点:没有解决对象识别的问题(即怎么知道一个对象的类型),下面的构造函数模式可以解决这个问题
3,构造函数模式:
function Person(name ,age,job){
this.name=name ;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
以这种方式调用构造函数实际上会经历一下四个步骤:
(1)创建一个新的对象;
(2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
(3)执行构造函数中的代码
(4)返回新对象
构造函数解决对象识别问题:
var person1=new Person("rason2008",20,"student");
var person2=new Person("girlFriend",20,"student");
alert(person1.constructor==Person); //true
alert(person2.constructor==Person); //true
不过检测对象类型,一般还是用instanceof操作符更加可靠
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
将构造函数当作函数:
构造函数与其它函数的唯一区别,就在于它们的调用方式不同。任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new来调用,那它跟普通函数也不会有神马两样。
var person1=new Person("rason2008",20,"student"); //当作构造函数来调用
person1.sayName(); //rason2008
Person("rason2008",20,"student"); //作为普通函数调用
window.sayName(); //rason2008
var o= new Object();
Person.call(o,"rason2008",20,"student"); //在另一个对象的作用域调用
o.sayName(); //rason2008
构造函数存在的问题:
(1) function Person(name ,age,job){
this.name=name ;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
继续拿这个例子来说,如果我们创建了两个Person对象,person1和person2,那么也创建了两个完成同样任务的Function实例;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。
可以用下面的方式解决这个问题:
function Person(name ,age,job){
this.name=name ;
this.age=age;
this.job=job;
this.sayName=sayName;
}
function sayName(){
alert(this.name);
};
但这也导致了新的问题:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就没有丝毫封装性可言了。
解决上面遇到的问题就要用到下面讨论的原型模式了:
4,在介绍最佳创建对象的方法之前要先介绍一下原型模式:
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。那么显而易见,使用原型的好处是可以让所有对象实例共享它所包含的属性和方法。
function Person(){
}
Person.prototype.name="rason2008";
Person.prototype.age=20;
Person.prototype.job="student";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
var person2=new Person();
person1.sayName(); //"rason2008"
person2.sayName(); //"rason2008"
alert(person1.sayName==person2.sayName); //true
理解原型:
(1)只要创建一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,该属性指向该函数的原型
(2)函数的原型会自动获得一个constructor属性,该属性指向prototype属性所在的函数
(3)当调用构造函数创建一个新实例后,该实例会获得一个指向构造函数的原型的属性_proto_
上面的例子如下图:
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
每当代码读取某个对象的某个属性时,都会执行一次搜索:先在实例中找给定的属性名,找不到则继续搜索指针指向的原型对象,就是沿着原型链找,一找到就返回。所以如果实例中定义了和原型中同名的属性,那么原型中的该属性就会被屏蔽掉,但它还是存在原型中的,只是利用实例访问不了而已,要访问原型中被屏蔽的属性则要先使用delete操作符删除实例属性。
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,存在实例中时会返回true。
而单独使用in操作符时,无论属性存在实例中还是原型中都会返回true。
因此,由上面两个知识点可以写一个方法判断属性只存在于原型中:
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
更简单的原型语法:
大家应该注意到了,前面例子每添加一个属性和方法就要敲一边Person.prototype。下面介绍一个简单的方法:
function Person(){}
Person.prototype={
name:"rason2008",
age:20,
job:"student",
sayName:function(){
alert(this.name);
}
}
但是要注意的是此时constructor属性不再指向Person。前面说过,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。此种方法本质上重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数)。
因此我们可以像那面那样特意将constructor设置回适当的值:
function Person(){}
Person.prototype={
constructor:Person
name:"rason2008",
age:20,
job:"student",
sayName:function(){
alert(this.name);
}
}
原型的动态性:
下面让大家观察两个例子来理解原型的动态性:
function Person(){}
var person=new Person();
Person.prototype={
constructor:Person
name:"rason2008",
age:20,
job:"student",
sayName:function(){
alert(this.name);
}
}
function Person(){}
Person.prototype={
constructor:Person
name:"rason2008",
age:20,
job:"student",
sayName:function(){
alert(this.name);
}
}
var person=new Person();
Person.prototype={
constructor:Person
name:"rason2008",
age:20,
job:"student",
friends:["girlfriends1","girlfriends2","girlfriends3"] , //谁不想那么多girlfriends,搞IT伤的起吗
sayName:function(){
alert(this.name);
}
}
var person1=new Person();
var person2=new Person();
person1.friends.push("girlfriend4");
alert(person1.friends); //"girlfriends1","girlfriends2","girlfriends3","girlfriend4"
alert(person2.friends); //"girlfriends1","girlfriends2","girlfriends3","girlfriend4" 看出问题所在了吗?你弟弟增加了一个女盆友你也增加了同一个女盆友,问题很严重
alert(person1.friends===person2.friends); //true 你的女盆友就是你弟弟的女朋友,are you crazy?
5,终于轮到了介绍最最常用的创建自定义对象的方法了:
此方法综合构造函数和原型模式的优势,perfect了木有?
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["girlfriends1","girlfriends2","girlfriends3"] ;
}
alert(person1.friends); //"girlfriends1","girlfriends2","girlfriends3","girlfriend4"
alert(person2.friends); //"girlfriends1","girlfriends2","girlfriends3"