JavaScript工厂模式
工厂模式的作用
创建对象;降低代码的冗余度。
function person(name,age,gander){
return {
name:name,
age:age,
gander:gander,
sayName:sayName=function(){
console.log(name);
}
}
}
var p1=person('larry',20,'male');
var p2=person('terry',22,'female');
console.log(p1);
console.log(p2);
p1.sayName();
p2.sayName();
console.log(p1.sayNam === p2.sayName);//false
缺点:不知道谁创建了对象;每创建一个对象就要创建一个sayName()函数,造成了内存资源的浪费。
//优化 创建全局的sayName函数解决了 内存资源浪费问题
function sayName(){
console.log(name);
}
//但还是不知道谁创建了这一个对象
function person(name,age,gander){
return {
name:name,
age:age,
gander:gander,
sayName:sayName
}
}
var p1=person('larry',20,'male');
var p2=person('terry',22,'female');
console.log(p1);
console.log(p2);
p1.sayName();
p2.sayName();
console.log(p1.sayNam === p2.sayName);//true
所以就有了自定义构造函数模式
构造函数模式
构造函数模式和工厂模式的区别:
1.不需要返回值
2.属性和方法直接赋值给this
3.知道是谁创建了对象
function Person(name,age,gander){
//new的作用:
//1.js创建一个obj对象
//2.js把this指向了obj对象
//3.执行函数内部代码 唯一需要自己手动操作
//4.js会自动的返回一个obj对象
this.name=name;
this.age=age;
this.gander=gander;
this.sayName=function(){
console.log(this.name);
}
}
var p1=new Person('zhangsan',20,'male');
var p2=new Person('lisi',18,'female');
console.log(p.sayName === p2.sayName);//false
console.log(p.sayName instanceof p2.sayName);//false
//解决内存资源浪费的方法 同上面工厂模式的一样
instanceof
constructor是用来标识对象类型的。但是用instanceof操作符来确定对象类型更加可靠。
instanceof操作符作用于检测构造函数的prototype属性是否出现在实例对象的原型链上。
var obj=new Object();
console.log(obj instanceof Object);//true
//构造函数模型上的也可以检测
console.log(p1 instanceof Person);//true
原型模式
使用prototype属性在函数的原型对象里面创建属性和方法。属性和方法可以被实例对象共享。
function Person(){}
Person.prototype.name='zhangsan';
Person.prototype.age=20;
Person.prototype.gender='male';
Person.prototype.sayName=function(){
console.log(this.name);
};
var person1=new Person();
person1.sayName();//zhangsan
var person2=new Person();
person2.sayName();//zhangsan
console.log(person1.sayName === person2.sayName);//true
优点:属性和方法共享,解决了内存资源浪费的问题;
缺点:原型对象里面的属性值是不可以改变的;只能遮挡(如下案例)
function Person(){}
Person.prototype.name='zhangsan';
Person.prototype.age=20;
Person.prototype.sayName=function(){
console.log(this.name);
}
var person1=new Person();
var person2=new Person();
person1.name='lisi';
console.log(person1.name);//lisi 来自实例
console.log(person2.name);//zhangsan 来自原型
遮挡可以用delete方法把该实例属性删除。
function Person(){}
Person.prototype.name='terry';
Person.prototype.age=20;
var person1=new Person();
// 通过hasOwnProperty()可以查看访问的是实例属性还是原型属性
console.log(person1.hasOwnProperty('name'));//false
person1.name='lisi';
console.log(person1.name);//lisi 来自实例
console.log(person1.hasOwnProperty('name'));//true
//通过delete删除name属性
delete person1.name;
console.log(person1.name);//terry 来自原型对象
console.log(person1.hasOwnProperty('name'));//false
这里可以衍生出一个判断属性是否是原型对象属性的方法:
function Person(){}
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
// 无论属性是在实例上还是在原型上,都可以检测到
console.log("name" in person1); // true
console.log("name" in person2); // true
// 判断一个属性是否是原型属性
function hasPrototypeProperty(obj,name){
//不在实例中但是可以访问到的属性属于原型属性
return !obj.hasOwnProperty(name) && (name in obj);
}
console.log(hasPrototypeProperty(person1, 'name'));//true
简单原型模式
优点:降低了代码的冗余度;
function Person(){}
Person.prototype={
name:'larry',
age:20,
gender:'male';
sayName:function(){
console.log(this.name);
}
};
//Person.prototype创建的属性和方法 相当于Person.prototype=new Object();创建了一个新的对象;Person.prototype成为了Object构造函数的一个实例对象,他的[[prototype]]就会指向Object构造函数的原型对象。所以其constructor指向就会发生改变,指向了Object构造函数
var person1=new Person();
console.log(person1.constructor === Person);//false
console.lg(person1.constructor === Object);//true
解决constructor指向问题
1.可以在重写原型对象时,专门给他设置一个constructor的属性
2.原生的constructor是不可枚举的,所以可以通过Object.defineProperty()方法来定义constructor属性
function Person(){}
Person.prototype={
//1.直接设置constructor属性
//constructor:Person;
name:'lisi',
age:20,
gender:'male',
sayName:function(){
console.log(this.name);
}
};
//2.通过Object.defineProperty()方法来需改constructor属性
Object.defineProperty(Person.prototype,'constructro',{
enumerable:false,
value:Person
});
var person1 = new Person()
console.log(person1.constructor == Person); //true
console.log(person1.constructor == Object); //false
原型问题
导致所有实例默认都拿到相同的属性值;真正的问题来自包含引用值的属性。来看下面的案例:
function Person(){}
Person.prototype={
constructor:Person,
name:'lisi',
age:30,
firends:['zhangsan','wangwu'],
sayName:function(){
console.log(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("zhaoliu");
console.log(person1.friends); // [ 'lisi', 'wangwu', 'zhaoliu' ]
console.log(person2.friends); // [ 'lisi', 'wangwu', 'zhaoliu' ]
console.log(person1.friends === person2.friends); // true
组合模式
组合使用构造函数模式和原型模式。
利用构造函数用于定义实例属性,原型模式用于定义方法和共享属性。
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
this.firends=['lisi','zhangsan'];
}
Person.prototype={
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
var p1 = new Person('larry', 44, 'male');
var p2 = new Person('terry', 39, 'male');
p1.firends.push('robin');
console.log(p1.firends); // [ 'zhangsan', 'lisi', 'robin' ]
console.log(p2.firends); // [ 'zhangsan', 'lisi' ]
console.log(p1.firends === p2.firends); // false
console.log(p1.sayName === p2.sayName); // true