创建对象的几种方法

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createPersion(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
};
return o;
}
var person1 = createPersion('a', 29, 'jobA');
var person2 = createPersion('b', 30, 'jobB');
console.log(person1 instanceof createPersion) // false
console.log(person1 instanceof Object) // true

优点:创建多个相似对象
缺点:无法知道对象的类型

构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
//等价于 new Function(...)
}
var person1 = new Person('a', 29, 'jobA');
var person2 = new Person('b', 30, 'jobB');
console.log(person1 instanceof Person) // true
console.log(person1 instanceof Object) // false

构造函数以大写字母开头

new的方式调用构造函数,会经历以下4个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(this指向新对象)
  3. 执行构造函数中的代码
  4. 返回新对象

优点:解决了对象类型识别的问题,可以规定特定的对象类型名称
缺点:每个实例上的同名函数不相等,即每个实例上的方法都是新构造出来的

原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person() {}
Person.prototype.name = 'a';
Person.prototype.age = 29;
Person.prototype.job = 'jobA';
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
person1.sayName(); // a
var person2 = new Person();
person2.sayName(); // a
console.log(person1.sayName === person2.sayName) // true

优点:共享了属性
缺点:共享的引用类型的数据(如:Array),也被所有实例共享了

理解原型对象

理解原型对象

[[Prototype]]的链接存在于实例与构造函数的原型之间,不是存在于实例与构造函数之间

1
2
3
4
5
6
7
8
9
10
11
// isPrototypeOf
console.log(Person.prototype.isPrototypeOf(person1)) // true
// getPrototypeOf
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
// hasOwnProperty
console.log(person1.hasOwnProperty('name')); // false
// in
console.log('name' in person1) // true

在读取对象中的属性时,先搜索实例,后搜索原型。

取得对象上所有可枚举的实例属性

Object.keys()
包括原型中与实例中的属性

更简单的原型语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person() {}
// 相当于重写了原型
Person.prototype = {
name: 'a',
age: 29,
job: 'jobA',
sayName: function() {
console.log(this.name);
}
}
// 将原型对象的'constructor'属性指向Person构造函数
Object.defineProperty(Person.prototype, 'constructor', {
enumberable: false,
value: Person,
});

组合使用构造函数模式和原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 不应该共享的属性,用构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['A', 'B'];
}
// 需要共享的属性,用原型模式共享
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name);
}
}
var person1 = new Person('a', 29, 'jobA');
var person2 = new Person('b', 30, 'jobB');
person1.friends.push('C');
console.log(person1.friends); // 'A', 'B', 'C'
console.log(person2.friends); // 'A', 'B'
console.log(person1.friends === person2.friends); // false
console.log(person1.sayName === person2.sayName); // true

集合了构造函数模式和原型模式的优点

动态原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName !== 'function') {
// 同样不能用对象字面量重写原型
Person.prototype.sayName = function() {
console.log(this.name);
};
}
}
var friend = new Person('a', 39, 'jobA');
friend.sayName();

寄生构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
}
return o;
}
var friend = new Person('a', 29, 'jobA');
friend.sayName(); // 'a'

Person构造函数在原有Object的基础上添加了额外的属性和方法,而没有改变Object本身

优点:可以在不改变原有构造函数的前提下,为新构造函数添加方法
缺点:无法使用instanceof确定对象类型

稳妥构造函数模式

要求:

  • 新创建对象的实例方法不引用this
  • 不使用new操作符调用构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name, age, job) {
var o = new Object();
// 定义私有变量和函数
var name = name;
o.sayName = function() {
console.log(name);
}
return o;
}
var friend = Person('a', 29, 'jobA');
friend.sayName(); // 'a'

优点:除了sayName方法,没有其他方法可以访问传入构造函数中的原始数据
缺点:不能用过instanceof方法确定对象类型

坚持原创技术分享,您的支持将鼓励我继续创作!