一、工厂模式
用函数来封装,通过传入参数以特定接口创建对象的细节。
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var p1 = createPerson('kimi',26,'web');
p1.sayName(); //kimi
var p2 = createPerson('liujin',25,'java');
p2.sayName(); //liujin复制代码
缺点:没有解决对象识别的问题。
二、构造函数模式
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
}
var p1 = new Person('kimi','26','Web');
p1.sayName(); //kimi
var p2 = new Person('liujin','25','java');
p2.sayName(); //liujin复制代码
构造函数与工厂模式的区别:
1、没有显示的创建对象。
2、直接将属性和方法赋值给this。
3、没有return语句。
4、调用时使用new关键字,首字母要大写。
构造函数存在的问题:构造函数中的属性和方法是实例特有的,不同的实例上的同名函数是不相等的。
解决同名函数不共享的问题:将构造函数中的函数定义为全局的函数。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
var p1 = new Person('kimi','26','Web');
p1.sayName(); //kimi
var p2 = new Person('liujin','25','Java');
p2.sayName(); //liujin复制代码
然而新的问题来了:定义很多全局函数之后,破坏了对象的封装性。
解决办法:原型模式
三、原型模式
function Person(){}
Person.prototype.name = 'kimi';
Person.prototype.age = '26';
Person.prototype.job = 'Web';
Person.prototype.sayName = function(){
console.log(this.name);
};
var p1 = new Person();
p1.sayName(); //kimi
var p2 = new Person();
p2.sayName(); //kimi复制代码
1、对原型的理解:
每个函数都有一个prototype属性,这个属性是一个指针,执行一个原型对象,而这个对象包含所有实例所共享的属性和方法。
每个原型都有一个constructor指针,这个指针指向Prototype属性所在的函数。
实例中存在一个__proto__属性,它指向构造函数的原型对象。
2、获取某个对象的属性的过程
首先在对象实例中搜索有没有该方法,如果有则返回该实例的值,如果没有继续搜索原型对象中是否包含该属性。
3、在对象实例中添加一个属性,该实例属性与原型中的属性同名时,该实例属性会覆盖原型属性,但并不会重写原型中的属性。
例子:
function Person(){}
Person.prototype.name = 'kimi';
Person.prototype.age = '26';
Person.prototype.job = 'Web';
Person.prototype.sayName = function(){
console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();
p1.name = 'zhangsan';
console.log(p1.name); //zhangsan
console.log(p2.name); //kimi复制代码
4、删除实例属性
delete 实例.property (注意将p1.name = null不管用)
function Person(){}
Person.prototype.name = 'kimi';
Person.prototype.age = '26';
Person.prototype.job = 'Web';
Person.prototype.sayName = function(){
console.log(this.name);
};
var p1 = new Person();
var p2 = new Person();
p1.name = 'zhangsan';
p1.name = null;
delete p1.name;
console.log(p1.name); //kimi
console.log(p2.name); //kimi复制代码
5、更简单的原型写法
function Person(){
}
Person.prototype = {
name:'kimi',
age:26,
job:'Web',
sayName:function(){
console.log(this.name);
}
};
var p1 = new Person();
console.log(p1.constructor); //[Function: Object]
console.log(p1.name); //kimi复制代码
可以看到它的构造函数发生了变化。本质上是重写了prototype对象,因此constructor属性也变成了新对象的constructor属性(指向Object构造函数)。
function Person(){
}
Person.prototype = {
constructor:Person, //可以特意将其设为适当的值。
name:'kimi',
age:26,
job:'Web',
sayName:function(){
console.log(this.name);
}
};
var p1 = new Person();
console.log(p1.constructor); //[Function: Person]
console.log(p1.name); //kimi复制代码
6、原型的动态性
原型的动态性就是在给原型添加属性或者方法前,我们可以先创建实例。原因:实例与原型之间的连接是一个指针。
function Person(){}
var p1 = new Person();
Person.prototype.sayHi = function(){
console.log('hi');
};
p1.sayHi(); //hi复制代码
但是在重写原型前创建对象,却会报错。
function Person(){
}
var p1 = new Person();
Person.prototype = {
constructor:Person,
name:'kimi',
age:26,
job:'Web',
sayName:function(){
console.log(this.name);
}
};
p1.sayName(); //p1.sayName is not a function复制代码
原因:重写原型时,切断了实例与新的原型对象之间的联系。
7、原型对象的问题:对于包含引用类型的属性,一个实例对其进行修改,也会反映在其他实例上。
4、组合使用构造函数和原型模式
思想:使用构造函数定义实例属性,原型定义方法和共享属性。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ['Shelby','Court'];
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name);
}
};
var p1 = new Person('Kimi',26,'Web');
var p2 = new Person('liujin',25,'Java');
p1.friends.push('Van');
console.log(p1.friends); //[ 'Shelby', 'Court', 'Van' ]
console.log(p2.friends); //[ 'Shelby', 'Court' ]复制代码
5、动态原型模式
把所有信息都封装在构造函数中,通过判断某一方法是否存在来决定是否需要初始化原型。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ['Shelby','Court'];
if(typeof this.sayName !== 'function'){
Person.prototype.sayName = function(){ //只会初始化一次
console.log(this.name);
}
}
}
var p1 = new Person('Kimi',26,'Web');
p1.sayName(); //kimi复制代码
6、寄生构造函数模式
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function (){
console.log(this.name);
};
return o;
}
var p1 = new Person('kimi',26,'Web');
p1.sayName();复制代码
寄生构造函数模式返回的对象和构造函数,原型之间没有关系,所以不能依赖instanceof来确定对象的类型。不建议使用这种模式。
7、稳妥构造函数
function Person(name,age,job){
var o = new Object();
//定义私有变量和函数
o.sayName = function (){
console.log(name); //不引用this
};
return o;
}
var p1 = Person('kimi',26,'Web'); //不使用new关键字
console.log(p1.name); //undefined
p1.sayName(); //只能通过sayName方法去获取name的值复制代码