1. 工厂模式
function createPerson(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function() {
alert(this.name);
};
return o;
}
var person1 = createPerson("王钱青",24);
console.log(person1.constructor); // Object() {[native code]}
问题: 实例化出来的对象类型无法确定,实例化出来的对象全是Object类型,未解决这一问题,引入构造函数模式
2. 构造函数模式
function Person(name,age) {
this.name = name;
this.age = age;
this.sayName = function() {
alert(this.name);
};
}
var person1 = new Person("张三", 24);
var person2 = new Person("李四", 22);
console.log(person1.constructor); // Person构造函数
console.log(person2.constructor); // Person构造函数
使用new操作符,实例化对象的4个步骤
- 开辟堆内存空间,创建新对象。
- 将构造函数的作用域赋给新对象(this指向这个新对象)。
- 执行构造函数中代码(为这个新对象添加属性)。
- 返回新对象。
可以使用instanceof检测对象类型
alert(person1 instanceof Object); // true
alert(person1 instanceof Person); // true
问题:每实例化一个对象,就创建一个sayName方法,造成内存空间浪费,简单的解决思路,如下把方法声明在构造函数外部
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName() {
alert(this.name);
}
这个办法虽然解决方法共享问题,但却体现不出面向对象思想的封装性。于是引入下面原型模式。
3. 原型模式
function Person() {}
Person.prototype.name = "王钱青";
Person.prototype.age = 24;
Person.prototype.sayName = function() {
alert(this.name);
};
// 简单的原型语法
function Person() {}
Person.prototype = {
constructor: Person, //这里必须指定constructor,因为对象字面量相当于创建了一个新的对象,覆盖掉原来的
name: "王钱青",
age: 24,
sayName: function() {
alert(this.name);
}
};
问题: 无法传递参数,所有实例默认情况下共享相同的初始值。
最大问题是共享本身所导致的,共享对函数非常合适,基本值的属性也说得过去,对引用属性问题最大,如下
function Person() {}
Person.prototype = {
name: "wqq",
age: 24,
friends: ["张三","李四"],
sayName: function() {
alert(this.name);
}
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push("凌云");
console.log(p1.friends); // ["张三", "李四", "凌云"]
console.log(p2.friends); // ["张三", "李四", "凌云"]
当我修改p1的friends属性时,由于数据共享,p2的friends属性也被改变,这种情况使我们不想看到的,于是引入组合构造函数和原型模式
4. 组合构造函数和原型模式
function Person(name, age) {
this.name = name;
this.age = age;
this.friends = ["张三","李四"];
}
Person.prototype = {
constructor: Person,
sayName: function() {
alert(this.name);
}
};
var p1 = new Person();
var p2 = new Person();
p1.friends.push("凌云");
console.log(p1.friends); // ["张三", "李四", "凌云"]
console.log(p2.friends); // ["张三", "李四"]
这种方法是使用最广泛的,认同度最高的一种方法,也是默认的创建自定义类型的方法。又因为构造函数和原型分开,所以会使有oo语言经验的开发人员感到困惑,由此引入动态原型模式
5. 动态原型模式
function Person(name, age) {
this.name = name;
this.age = age;
if (typeof this.sayName != "function") {
Person.prototype.sayName = function () {
alert(this.name);
};
}
}
if语句部分,只会在第一次实例化对象时运行。
6. 寄生构造函数模式
function Person(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function() {
alert(this.name);
};
return o;
}
7. 稳妥构造函数模式
function Person(name, age) {
var o = new Object();
// 可以在这里定义私有变量和函数
o.sayName = function () {
alert(name);
};
return o;
}
实例化出来的是稳妥对象,除了调用sayName方法之外,没有其他办法访问数据成员,使得它非常适合在安全执行环境下使用。