1. 工厂模式
什么是工厂模式?工厂模式是一种众所周知的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程。工厂模式是一种创建型模式,简单来说,工厂模式就是创建对象的一种方式。
作用:把一些对象封装,使一些重复的,占空间的代码封装到一个函数中。通过这个函数就可以创建我们需要的对象,还能降低代码的冗余的。例如:你要生成一个班级学生的信息,每个学生都有姓名、年龄等特征,如果还是一个一个的生成,就会造成代码冗余,还不方便。这时候你创建一个“工厂”,每次调用就能生成一个学生的信息。
使用工厂模式创建对象
1. 优点: 批量创建对象 封装创建对象的函数 提高代码复用率
2. 缺点: 这种方式本质上是将创建对象的过程进行了封装,本质并没有改变,我们创建一个student时无法知道其具体的数据类型,只知道这是一个对象。而且还会方法冗余。
//将创建对象的代码封装在一个函数中
function createStudent(name, age, gender) {
var student = new Object();
student.name = name;
student.age = age;
student.gender = gender;
// student.sayName = function () {
// console.log(this.name);
// }
// 由于每次创建对象都会生成一个新的sayName方法(即student1.sayName()和
// student2.sayName()的地址不一样),这样后面创建的对象如果数量过多,又
// 会造成代码冗余
return student;
}
// 所以把方法写在全局作用域,这样每次创建对象都是调用同一个方法
function sayName() {
console.log(this.name);
}
//利用工厂函数来创建对象
var student1 = createStudent("zhangsan", 10, 'male');
var student2 = createStudent("lisi", 10, 'female');
console.log(student1);
console.log(student2);
2.构造函数模式
什么是构造函数?构造函数就是把我们对象里面一些相同的属性和方法抽象出来封装到函数里面。是用于创建特定类型对象的。 JavaScript中可以自定义构造函数,从而自定义对象类型的属性和方法,构造函数本身也是函数,只不过可以用来创建对象。
构造函数与普通函数唯一的区别就是调用方式不同。除此之外,构造函数也是函数。 任何函数只要使用 new 操作符调用就是构造函数,而不使用 new 操作符调用的函数就是普通函数 。
function Student(name, age, gender) { // 构造函数的函数名的首字母大写
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = sayName;
}
function sayName() {
console.log(this.name);
}
// 调用构造函数要用new关键字
// 使用 new 操作符创建 Student 的实例
var s1 = new Student("zhangsan", 11, "male");
var s2 = new Student("lisi", 12, "female");
s1.sayName(); // zhangsan
s2.sayName(); // lisi
在这个案例中,Student()构造函数代替了 createStudent()工厂函数。实际上,Student()内部的代码跟 createStudent()基本是一样的,只是有如下区别。
-
没有显式地创建对象。
-
属性和方法直接赋值给了 this。
-
没有 return。
new操作符做了什么事情
1.创建一个实例对象 Student的实例对象。
2.this指向person实例对象 p1 p2
3.执行函数体 ,给实例对象添加属性。
4.返回Student实例对象。
3.原型模式
每个函数都有一个prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。
作用:将所有的属性和方法全部维护到原型对象中,构造函数中不存放任何属性和方法。
function Student(){};
Student.prototype={
// 因为这样创建原型对象,constructor属性会指向Object
// constructor:Student,
//方法1:这种方式恢复 constructor 属性会创建一个[[Enumerable]]为 true 的属性
// 默认constructo 属性不可枚举
name:'terry',
age:12,
firends:[],
getName:function(){
console.log(this.name);
}
}
// 恢复方法2 constructor 属性 这种方法比较严谨
Object.defineProperty(Student.prototype, "constructor", {
enumerable: false,
value: Student
});
var s1=new Student();
var s2=new Student();
s1.name = 'wangwu'; // 通过在实例上添加同名属性来简单地遮蔽原型上的属性
console.log(s1.name); // wangwu
console.log(s1.constructor == Student); // true constructor 属性指向Student
s1.firends.push('larry');
console.log(s1.firends,s2.firends); // 属性共享 向s1的数组添加元素 s2的数组也会添加
4.组合模式
组合使用构造函数模式和原型模式。
构造函数用于定义实例属性,原型模式用于定义方法和共享属性。这种模式是目前在ECMAScript中使用最广泛,认同度最高的一种创建自定义类型的方法。
// 将实例的私有的属性和私有方法维护到构造函数中
function Student(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
this.frineds=[];
}
// 将实例的公共属性和公共方法维护到原型对象中
Student.prototype={
constructor:Student,
getName(){
console.log(this.name);
}
}
var s1=new Student("terry",12,'男');
var s2=new Student("larry",13,'男');
console.log(s1 instanceof Student); // true 判断一个对象是某个对象的实例
console.log(s1 instanceof Object); // true 所有自定义对象都继承自 Object