「Learning」JavaScript创建对象的七种方式

学习内容: JavaScript 创建对象的七种方式

七种经典的创建对象的方式

  • 工厂模式
  • 构造函数模式
  • 原型模式
  • 组合模式
  • 动态原型模式
  • 寄生构造函数模式
  • 稳妥构造函数模式

我的理解:

这几种模式写法上的核心区别就是*是用新的对象创建还是用this。如果是创建新的对象new Object()的方法来创建,对象的属性和方法啊都是加载这个新的对象o上的,在函数的末尾也需要return o来返回这个对象值。

也就是说,这种方法是每次创建一个新的对象,而不是创建的实例,即新的对象无法使用instanceof

而使用this来创建实例的方法,它们的区别在于方法和属性分别添加到原型上还是实例上。添加到原型上的优势在于方便共享,缺点在于一变皆变;添加到实例上的优势在于每个实例都拥有这些属性方法,缺点在于每个实例上都要创建一遍方法,浪费资源。

目前最普遍的做法是组合使用构造模式和原型模式,即属性放在实例上,方法放在原型上,或者说我们可以更灵活一些,所有需要实例个性化的内容都放在实例上,所有的公共属性和方法都放在原型上,这样使用起来会更方便。

工厂模式

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

var person1 = createPerson("aaa", "student");
var person2 = createPerson("bbb", "doctor");
优点:
  • 可以无数次调用这个工厂函数
  • 每次返回的对象都包含两个属性和一个方法(属性和方法都可以自行往工厂函数里添加)
缺点:
  • 没有解决对象识别的问题,即不能知道一个对象的类型

构造函数模式

1
2
3
4
5
6
7
8
9
10
function Person(name, job) {
this.name = name;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
}

var person1 = new Person("aaa", "student");
var person2 = new Person("bbb", "doctor");
使用new关键字创建对象的步骤:
  • 创建一个新的对象
  • 这个新对象会被执行prototype链接
  • 这个新对象会绑定到函数调用的this
  • 返回这个对象
优点:
  • 构造函数创建的对象可以检测对象类型

    1
    2
    person1 instanceof Object //true
    person1 instanceof Person //true
缺点:
  • 构造函数中的方法会在每个实例上重新创建一次

原型模式

1
2
3
4
5
6
7
8
function Person() {}
Person.prototype.name = 'Tom';
Person.prototype.job = 'student';
Person.prototype.sayName = function() {
console.log(this.name);
}

var person1 = new Person();

更简单的写法:

1
2
3
4
5
6
7
8
9
10
11
function Person() {}
Person.prototype = {
constructor: Person,
name: 'Tom',
job: 'student',
sayName: function() {
console.log(this.name)
}
}

var person1 = new Person();
优点:
  • 信息直接添加在原型对象上,所有的实例都能共享原型对象包含的属性和方法
  • 不用再构造函数中定义实例对象的信息
缺点:
  • 无法自定义创建的对象
  • 因为实例对象的属性是共享的原型对象的属性,那么一个实例对象修改属性,其他实例对象的属性都会跟着改变

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

这是使用最广泛、认同度最高的一种创建自定义类型的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name) {
this.name = name;
this.friends = ["Alice", "Bob"];
}
Person.prototype.sayName = function() {
console.log(this.name);
}

var person1 = new Person("Rose");
var person2 = new Person("Jack");
person1.friends.push('Van');
console.log(person1.friends); //["Alice", "Bob", "Van"]
console.log(person2.friends); //["Alice", "Bob"]
优点:
  • 每个实例都有自己的一份实例属性副本,同时又共享对原型方法的阴影
  • 一个实例属性的修改不会影响其他实例的属性值

动态原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, job) {
this.name = name;
this.job = job;

if(typeof this.sayName !== 'function') {
Person.prototype.sayName = function() {
console.log(this.name);
}
}
}

var person1 = new Person('Tom', 'student');
person1.sayName();

只有在sayName方法不存在时,才会将它添加到原型中。

寄生构造函数模式

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

var person1 = new Person('Tom', 'student');
person1.sayName();

稳妥构造函数模式

稳妥对象指的是没有公共属性,方法也不引用this的对象,适合在一些安全环境中(禁用thisnew)时使用

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

var person1 = Person("Tom", "student");
person1.sayName();