花了些时间在JS创建对象这块,写下做总结,也备以后复习。
1. 工厂模式:
用函数封装以特定接口创建对象的细节,解决了创建多个相似对象的问题,但没有解决对象识别的问题,代码示例如下:
function creatPerson(name,age,job){
var o = new Object(); //在函数中定义对象和对象的属性
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);
};
reurn o;
}
var person1=creatPerson("Nick",18,"student"); //创建对象person1
var person2=creatPersom("Marry",27,"teacher"); //创建对象person2
1.在函数中定义对象,并定义对象的各种属性,虽然属性可以为方法,但是建议将属性为方法的属性定义到函数之外,这样可以避免重复创建该方法
2.在利用该模式创建对象时使用的是var someone = creatPerson(…); 而不是var someone = new creatPerson(…);
3.在函数的最后返回该对象
4.不推荐使用这种方式创建对象,但应该了解
2. 构造函数模式:
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,这也正是构造函数模式胜过工厂模式的地方,代码示例如下:
function Person(name, age, job){
this.name = name;
this.age = age;
this,job = job;
this.sayName = function(){
alert(this.name); //注意没有return语句
};
}
var person1=new Person("Nick",18,"student"); //创建对象person1
var person2=new Persom("Marry",27,"teacher"); //创建对象person2
与工厂模式不同之处有以下三点:
1. 没有显式的创建对象;
2. 直接将属性和方法赋给this队形;
3. 没有return 语句。
4. 创建新对象使用的是var someone = new Person() ; 而不是var someone = Person()。可以来看一下两者的区别:
<script type="text/javascript">
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
var person1=new Person("Nick",19,"student"); //person1正常
var person2 =Person("Marry",18,"teacher"); //person2不使用new
person1.sayName();
person2.sayName();
window.sayName();
</script>
这样的结果是只跳出“Nick”,然后你点确定之后就没了,如下:
然后我们把第一句person1.sayName()注释掉,再来看这次的效果,结果如下:
也是啥都没有。
然后我们再把第二句注释掉,出现:
嗯,为啥勒?????这是因为当构造函数调用不使用new操作符时,属性和方法会添加给window对象,所以在调用window对象的sayName会出现Marry,而person2没啥反应。
所以,要创建Person的新对象,必须使用new操作符,而以该方式调用狗仔函数实际上会经历以下四个步骤:
(1)创建一个新对象;
(2)将构造函数的作用域赋给新对象(所以this就指向了这个新对象);
(3)执行构造函数中的代码(为新对象添加属性);
(4)返回新对象。
但是由于方法也在函数中定义,所以和工厂模式一样,每创建一次对象,方法就会被重复创建一次,所以同样建议方法属性定义在函数外面。
3. 原型模式:
我们创建的每个函数都有一个prototype(原型)属性,该属性是一个指针,指向一个对象,而该对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处就是可以让它的对象实例共享它的属性和方法,代码示例如下:
function Person(){ //构造函数为空函数
}
//所有属性直接添加到Person的prototype属性中
Person.prototype.name="Nick";
Person.prototype.age="18";
Person.prototype.job="student";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nick"
var person2 = new Person();
person2.sayName(); //"Nick"
特点:
1.构造函数为空函数;
2.对象属性直接添加在prototype属性中;
3.虽然解决了构造函数每个方法都会在每个实例中重新创建一遍的问题。但是所有实例在默认情况下都取得了相同的属性值,实例一般都是要有属于自己的全部属性的。
问题:
由于省略了传递初始化参数,所有实例在默认情况下都取得相同的属性值,原型的所有属性是被很多实例共享的,这种共享对于函数非常合适,对包含基本值的属性页说的过去(当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性),但对于包含引用类型值的属性,问题就比较突出了。(基础类型和引用类型区别) 代码如下:
Person.prototype = {
name:"Nick",
age : 18,
job:"student",
friends:["Bob","Marry"],
sayName:function(){
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Vava");
alert(person1.friends); //"Bob","Marry","Vava"
alert(person2.friends); //"Bob","Marry","Vava"
alert(person1.friends===person2.friends); //"ture"
由于friends数组存在于Person.prototype而非person1中,所以person1中的修改就会引起数组的修改。但如果给person1加上属性friends,就不会引起这种现象,因为此时person1有属于自己的friends,如下:
//省略Person定义
var person1 = new Person();
person1.friends=["Tom"];
var person2 = new Person();
person1.friends.push("Vava");
alert(person1.friends);
alert(person2.friends);
alert(person1.friends===person2.friends);
结果弹出如下顺序的窗口:
(alert(person1.friends); )
(alert(person2.friends); )
(alert(person1.friends===person2.friends); )
4. 组合使用构造函数模型和原型模式:
最常见的模式,构造函数模型用于定义实例属性,而原型模式用于定义方法和共享的属性:每个实例都有自己的一份实例属性的副本,又同时共享着对方法的引用,最大限度的节省了内存。(而且还支持向构造函数传递参数),代码示例如下:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["Tom","Nick"];
}
Person.prototype={
constructor:Person;
sayName:function(){
alert(this.name);
}
}
var person1 = new Person("Marry",27,"teacher");
var person2 = new Person("Jack",19,"student");
person1.friends.push("Vava");
alert(person1.friends); //"Tom","Nick","Vava"
alert(person2.friends); //"Tom","Nick"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //ture
实例属性都是在构造函数中定义的,而所有实例共享的属性constructor和方法sayName()则是在原型中定义的,所以person1和person2分别一个friends,修改其中一个不会影响另一个。
5. 动态原型模式:
将所有信息封装在构造函数内,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。代码示例如下:
function Person(name,age,job){
//属性
this.name=name;
this.age=age;
this.job=job;
//方法
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
只有在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);
};
reurn o;
}
var person1=Person("Nick",18,"student");
person1.sayName(); //"Nick"
构造函数在不返回值的情况下,默认返回新对象实例,而通过在构造函数末尾加return语句,可以重写调用构造函数时返回的值。寄生构造函数可以在构造函数不适应的情况使用,比如创建具有额外方法的已有类型(如数组,Date类型等),但是又不污染原有的类型。
7. 稳妥构造函数模式:
稳妥对象:指没有公共属性,而且其方法不引用this的对象。
稳妥构造函数与寄生构造函数相似,但是有2点不同:新创建对象的实例方法不引用this,不使用new操作符调用构造函数。
function Person(name,age,job){
var o = new Object();
//定义私有属性或函数
o.name=name;
o.age=age;
o.job=job;
//添加方法
o.sayName=function(){
alert(this.name);
};
reurn o;
}
var person1 =Person("Nick",16,"student");
person1.sayName(); //"Nick"
//注意!!!!
person1.name="Mary";
person1.sayName(); //"Nick"
上面修改了person1.name的属性,但是下面的sayName()方法并没有改变值,所以说该模式提供安全性,使得它适合在某些安全执行环境。