JavaScript 基础 - 创建复用对象

创建复用对象

1,工厂模式

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

var person1 = createPerson('Nicholas', 29, 'Software Engineer');
var person2 = createPerson('Greg', 27, 'Doctor');

工厂模式没有解决获取对象类型的问题,所有生成的对象都来自于 Object 构造函数。

2,构造函数模式

1
2
3
4
5
6
7
8
9
10
11
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('Greg', 27, 'Doctor');

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,
这正是构造函数模式胜过工厂模式的地方。

构造函数模式的问题:

构造函数中的方法都会在实例上重新创建一遍,不同实例上的同名函数实际上是不相等的。

3,原型模式

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

Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function () {
alert(this.name);
};

var person1 = new Person();
person1.sayName(); // Nicholas

var person2 = new Person();
person2.sayName(); // Nicholas

alert(person1.sayName === person2.sayName); // true

函数的 prototype 属性指向函数的原型对象,其中包含了所有可以共享给实例的属性和方法。
实例被创建后,其内部属性 [[prototype]] 会指向该原型对象。

用对象字面量来重写原型对象:

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

Person.prototype = {
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function () {
alert(this.name);
}
};

// 因为重写原型对象会丢失原本的 contructor 属性,
// 根据其原本的数据属性,使用 defineProperty 方法重设。
Object.defineProperty(Person.prototype, 'constructor', {
value: Person,
writable: true,
configurable: true,
enumerable: false // 默认为 false,所以可以省略
});

注意:

如果在创建实例后,重写构造函数的原型对象,实例的内部属性 [[prototype]] 仍然是指向重写前的原型对象,所以在重写原型对象时请慎重。

原型模式的问题:

原型模式省略了为构造函数传递初始化参数这一环节,导致所有实例在默认情况下都将取得相同的值。

原型中所有属性被实例共享,如果属性值为简单类型,尚可以通过屏蔽属性来重定义。

如果属性值为引用属性,例如数组,则会产生数据共享问题,这可能就不是我们的初衷了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Person() {}

Person.prototype = {
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
friends: ['Shelby', 'Court'], //这里 friends 属性的值是个数组类型
sayName: function () {
alert(this.name);
}
};

Object.defineProperty(Person.prototype, 'constructor', {
value: Person,
writable: true,
configurable: true,
enumerable: false
});

var person1 = new Person();
var person2 = new Person();

person1.friends.push('Van');

alert(person1.friends); // "Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court,Van"
alert(person1.friends === person2.friends); // true

4,混合模式(构造函数+原型模式)

构造函数用于定义实例属性,支持向构造函数传递参数。

原型模式用于定义方法和共享的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['Shelby', 'Court'];
}

Person.prototype.sayName = function () {
alert(this.name);
}

var person1 = new Person('Nicholas', 29, 'Software Engineer');
var person2 = new Person('Greg', 27, 'Doctor');

person1.friends.push('Van');

alert(person1.friends); // "Shelby,Court,Van"
alert(person2.friends); // "Shelby,Court"
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true

5,动态原型模式

把所有信息都封装在了构造函数中,通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。

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

// 方法,只在方法不存在的情况下,才会添加到原型中。
if(typeof this.sayName !== 'function') {
Person.prototype.sayName = function () {
alert(this.name);
};
}
}

var person1 = new Person('Nicholas', 29, 'Software Engineer');
var person2 = new Person('Greg', 27, 'Doctor');
alert(person1.sayName === person2.sayName); // true

注意:

使用动态原型模式时,不能使用对象字面量重写原型对象,否则会切断现有实例与新原型之间的联系。

6,寄生构造函数模式(工厂+构造函数模式)

该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

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

return o;
}

var person1 = new Person('Nicholas', 29, 'Software Engineer');
Object.getPrototypeOf(person1); // Object

注意:

返回的对象与构造函数或者构造函数的原型对象之间没有关系。

用处:

可以在特殊的情况下用来为对象创建构造函数。

自定义构造函数 继承 内置构造函数:

再注意:

其实 SpecialArray 并没有继承只是 Array 的包装,而函数中带了 return 语句,那么 new 也可以不用。

返回的 values 对象仍然是 Array 的一个实例对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function SpecialArray() {
// 创建数组
var values = new Array();

// 添加值
values.push.apply(values, arguments);

// 添加方法
values.toPipedString = function () {
return this.join('|');
};

// 返回数组
return values;
}

var colors = new SpecialArray('red', 'blue', 'green');
colors.toPipedString(); // "red|blue|green"

7,稳妥构造函数模式

所谓稳妥对象,是指没有公共属性,而且其方法也不引用 this 的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person(name, age, job) {
// 创建要返回的对象
var o = new Object();
// 定义私有变量和函数

// 添加方法
o.sayName = function () {
alert(name);
};

// 返回对象
return o;
}

var person1 = Person('Nicholas', 29, 'Software Engineer');
person1.sayName(); // Nicholas
person1.name // undefined

注意:

同寄生构造函数模式一样,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系。