一:理解工厂模式
案例:
function CreatePerson(name,age,sex) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayName = function(){
return this.name;
}
return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age); // 28
console.log(p1.sex); // 男
console.log(p1.sayName()); // longen
console.log(p2.name); // tugenhua
console.log(p2.age); // 27
console.log(p2.sex); // 女
console.log(p2.sayName()); // tugenhua
// 返回都是object 无法识别对象的类型 不知道他们是哪个对象的实列
console.log(typeof p1); // object
console.log(typeof p2); // object
console.log(p1 instanceof Object); // true
工厂模式是为了解决多个类似对象声明的问题;也就是为了解决实列化对象产生重复的问题。
但是没有结解决对象识别的问题
二.构造函数模式
既解决了重复实例化的问题,又解决了对象识别的问题
案例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person('Grey',27,'Doctor');
Person()中的代码除了与第一个工厂模式的 createPerson()中相同的部分外,还存在以下不同之处:
1.没有显式地创建对象;
2. 直接将属性和方法赋给了 this 对象;
3. 没有 return 语句。
按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。
要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
在前面例子的最后,person1 和 person2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor(构造函数)属性,该属性指向 Person,如下所示。
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
对象的 constructor 属性最初是用来标识对象类型的。但是,提到检测对象类型,还是 instanceof 操作符要更可靠一些。我们在这个例子中创建的所有对象既是 Object 的实例,同时也是 Person的实例,这一点通过 instanceof 操作符可以得到验证。
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。
在这个例子中,person1 和 person2 之所以同时是 Object 的实例,是因为所有对象均继承自 Object.
前面例子中定义的 Person()函数可以通过下列任何一种方式来调用。
// 当作构造函数使用
var person = new Person(“Nicholas”, 29, “Software Engineer”);
person.sayName(); //“Nicholas”
// 作为普通函数调用
Person(“Greg”, 27, “Doctor”); // 添加到 window
window.sayName(); //“Greg”
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, “Kristen”, 25, “Nurse”);
o.sayName(); //“Kristen”
这个例子中的前两行代码展示了构造函数的典型用法,即使用 new 操作符来创建一个新对象。
接下来的两行代码展示了不使用new操作符调用Person()会出现什么结果:属性和方法都被添加给window对象了。
当在全局作用域中调用一个函数时,this 对象总是指向 Global 对象(在浏览器中就是 window 对象)。
因此,在调用完函数之后,可以通过 window 对象来调用 sayName()方法,并且还返回了"Greg"。最后,也可以使用 call()(或者 apply())在某个特殊对象的作用域中调用Person()函数。这里是在对象o 的作用域中调用的,因此调用后o 就拥有了所有属性和sayName()方法。
优点:没有显示的创建对象,没有返回语句,直接将属性赋给this对象,将Person的实例对象标识为一种特定的类型
缺点:每个方法在每个实例上面都需要重新定义一遍,
三.原型模式
原型链模式开发会导致不同的对象之间数据共享
案例:
function person() {
}
person.prototype.sleep = function () {
return "睡觉";
}
person.prototype.name = "张三";
var p = new person();
console.log(p);
var p1 = new person();
console.log(p1);
注意:省略了为构造函数传递初始化参数,结果所有实例享有相同的属性(对于函数实用,但是对于那些基本属性也说的过去,但是对于引用类型的数据就麻烦了)
基本属性我们可以在实例当中添加一个同名属性,这样可以隐藏原型当中的对应的属性,但是引用类型的属性却会导致所有实例共享
四.组合构造模式 + 原型链模式
构造函数用于定义实例属性,原型上面定义共享的属性和方法
案例:
function f1() {
this.name = "毛豆"
}
f1.prototype.sleep = function () {
return "睡觉"
}
var f = new f1();
console.log(f);
五.单例模式
限制类的实例化次数只能是一次。
如果该实例不存在的情况下,可以通过一个方法创建一个类来实现创建类的新实例,如果实例已经存在,它会简单返回该对象的引用。
案例:
var obj = (function () {
var newobj;
function getobj() {
//实例化对象 返回对象
if (newobj == undefined) {
newobj = new factory();
}
return newobj;
}
//创建对象的实例
function factory() {
//对象的封装
this.name = "";
this.sex = "";
this.sleep = function () {
}
}
return {
getObj: getobj
}
})();
console.log(obj.getObj());
适用场景
1.需要频繁实例化然后销毁的对象。
2.频繁访问数据库或文件的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。