前言

构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Person(name,age,family){ // 构造函数定义私有属性
this.name = name;
this.age = age;
this.family = family;
}

Person.prototype = { // 原型模式定义共享方法
// 每个函数都有prototype属性,指向该函数原型对象,原型对象都有 constructor属性,这是一个指向prototype属性所在函数的指针
constructor: Person,
say: function(){
alert(this.name);
}
}

var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
console.log(person1);
var person2 = new Person("wangwu",21,["lida","lier","lisi"]);
console.log(person2);

可以看出,混合模式共享着对相同方法的引用,又保证了每个实例有自己的私有属性。最大限度的节省了内存。

1、new Object

1
2
3
4
5
6
7
var person = new Object();
person.name = "lisi";
person.age = 21;
person.family = ["lida","lier","wangwu"];
person.say = function(){
alert(this.name);
}

2、字面量

1
2
3
4
5
6
7
8
var person ={
name: "lisi",
age: 21,
family: ["lida","lier","wangwu"],
say: function(){
alert(this.name);
}
};

以上两种方法在使用同一接口创建多个对象时,会产生大量重复代码,为了解决此问题,工厂模式被开发。

3、工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createPerson(name,age,family) {
var o = new Object();
o.name = name;
o.age = age;
o.family = family;
o.say = function(){
alert(this.name);
}
return o;
}

var person1 = createPerson("lisi",21,["lida","lier","wangwu"]); //instanceof无法判断它是谁的实例,只能判断他是对象,构造函数都可以判断出
var person2 = createPerson("wangwu",18,["lida","lier","lisi"]);
console.log(person1 instanceof Object); //true

工厂模式相当于是将new Object创建的方式通过一个function包起来了。

工厂模式解决了重复实例化多个对象的问题,但没有解决对象识别的问题(但是工厂模式却无从识别对象的类型,因为全部都是Object,不像Date、Array等,本例中,得到的都是o对象,对象的类型都是Object,因此出现了构造函数模式)。

4、构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name,age,family) {
this.name = name;
this.age = age;
this.family = family;
this.say = function(){
alert(this.name);
}
}
var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
var person2 = new Person("lisi",21,["lida","lier","lisi"]);
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
console.log(person2 instanceof Object); //true
console.log(person2 instanceof Person); //true
console.log(person1.constructor); //constructor 属性返回对创建此对象的数组、函数的引用

对比工厂模式相当于是把function中的new Object通过this来替换了。

对比工厂模式有以下不同之处:

(1)没有显式地创建对象

(2)直接将属性和方法赋给了 this 对象

(3)没有 return 语句

以此方法调用构造函数步骤:

(1)创建一个新对象

(2)将构造函数的作用域赋给新对象(将this指向这个新对象)

(3)执行构造函数代码(为这个新对象添加属性)

(4)返回新对象 ( 指针赋给变量person ??? )

可以看出,构造函数知道自己从哪里来(通过 instanceof 可以看出其既是Object的实例,又是Person的实例)

构造函数也有其缺陷,每个实例都包含不同的Function实例( 构造函数内的方法在做同一件事,但是实例化后却产生了不同的对象,方法是函数 ,函数也是对象)因此产生了原型模式

5、原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person() {}

Person.prototype.name = "lisi";
Person.prototype.age = 21;
Person.prototype.family = ["lida","lier","wangwu"];
Person.prototype.say = function(){
alert(this.name);
};
console.log(Person.prototype); //Object{name: 'lisi', age: 21, family: Array[3]}

var person1 = new Person(); //创建一个实例person1
console.log(person1.name); //lisi

var person2 = new Person(); //创建实例person2
person2.name = "wangwu";
person2.family = ["lida","lier","lisi"];
console.log(person2); //Person {name: "wangwu", family: Array[3]}
// console.log(person2.prototype.name); //报错
console.log(person2.age); //21

原型模式的好处是所有对象实例共享它的属性和方法(即所谓的共有属性),此外还可以设置实例自己的属性(方法)(即所谓的私有属性),可以覆盖原型对象上的同名属性(方法)。

6、混合模式(构造函数模式+原型模式)

构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person(name,age,family){
this.name = name;
this.age = age;
this.family = family;
}

Person.prototype = {
constructor: Person, //每个函数都有prototype属性,指向该函数原型对象,原型对象都有 constructor属性,这是一个指向prototype属性所在函数的指针
say: function(){
alert(this.name);
}
}

var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
console.log(person1);
var person2 = new Person("wangwu",21,["lida","lier","lisi"]);
console.log(person2);

可以看出,混合模式共享着对相同方法的引用,又保证了每个实例有自己的私有属性。最大限度的节省了内存。

感觉最常用的是6混合模式。

此外,高程中还提到了动态原型模式,寄生构造函数模式,稳妥构造函数模式。

7、动态原型模式

1
2
3
4
5
6
7
8
9
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype.getName = function () {
console.log(this.name);
}
}
}
var person1 = new Person();

注意:使用动态原型模式时,不能用对象字面量重写原型。

解释下为什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
}
}
}


var person1 = new Person('kevin');
var person2 = new Person('daisy');


// 报错 并没有该方法
person1.getName();


// 注释掉上面的代码,这句是可以执行的。
person2.getName();

当 new Person() 的时候,是先建立的原型关系,即 person .proto = Person.prototype,而后修改了 Person.prototype 的值。之前的person.__proto__已经不会改变。

解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
}

return new Person(name);
}
}


var person1 = new Person('kevin');
var person2 = new Person('daisy');

person1.getName(); // kevin
person2.getName(); // daisy

8、寄生构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name) {
var o = new Object();
o.name = name;
o.getName = function () {
console.log(this.name);
};

return o;
}

var person1 = new Person('kevin');
console.log(person1 instanceof Person) // false
console.log(person1 instanceof Object) // true

9、稳妥构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function person(name){
var o = new Object();
o.sayName = function(){
console.log(name);
};
return o;
}

var person1 = person('kevin');
person1.sayName(); // kevin
person1.name = "daisy";
person1.sayName(); // kevin
console.log(person1.name); // daisy