JavaScript 创建对象的一些姿势

工厂模式

在函数中创建 Object 对象,并为对象添加属性。函数返回添加属性之后的对象。

function createPerson(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function () {
console.log(this.name);
};
return o;
}

var p = createPerson('ahonn', 21);

但工厂模式产生的对象依旧为 Object 类型,只是在对象上添加了一些属性。

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)

构造函数模式

由于工厂模式创建的对象没有解决对象识别的问题,出现了另外一种新模式:构造函数模式。

function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
console.log(this.name);
};
}

var p = new Person('ahonn', 21);

这里使用了 new 操作符,即是将 Person 函数通过构造函数的方式去调用来创建对象。

构造函数会进行下面几个步骤:

  • 创建一个新对象
  • 将构造函数的 this 指向新对象
  • 通过 this 给对象添加属性
  • 返回新对象

使用构造函数模式创建的对象可以通过 constructor 属性查看对象的类型。

console.log(p.constructor); // Person

构造函数也是普通函数

构造函数与普通的函数无异,也可以直接调用构造函数。但此时就不会创建新对象,函数中的 this 指向的是函数当前所在的作用域。

Person('ahonn', 21);
this.sayName(); // ahonn

原型模式

通过构造函数模式创建的对象有一个问题,就是创建的对象各自拥有自己的方法,而实际上这些方法都是相同的。通过原型模式即可以将共同的属性方法放在 prototype 上。

function Person() {}

Person.prototype.name = 'ahonn';
Person.prototype.age = 21;
Person.prototype.sayName = function () {
console.log(this.name);
};

var p1 = new Person();
p1.sayName(); // "ahonn"

var p2 = new Person();
p2.sayName(); // "ahonn"

对象在 prototype 上的属性是共享的,即修改一个对象的某个属性,另一个对象对于的属性值也会改变。

p1.name = 'test';
p1.sayName(); // test

p2.sayName(); // test

构造函数与原型模式

使用原型模式创建的对象共享 prototype 上的属性,那么当有些属性不想要对象之间共享的时候,就可以结合构造函数模式与原型模式来使用。这也是最常用的创建对象的方式。

function Person(name, age) {
this.name = name;
this.age = age;
}

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

var p1 = new Person('ahonn', 21);
p1.sayName(); // "ahonn"

var p2 = new Person('test', 12);
p2.sayName(); // "test"

ES6 类

在 ES6 中有类似于 Java 创建对象的方式,即通过类来创建对象。ES6 中提供了 class 关键字,来声明一个类型,并如上面构造函数模式的方式一样使用 new 来声明对象。虽然可以使用类似 Java 中的 class 来声明,但实际上只是给构造函数与原型模式加上了语法糖,使得代码看起来更加易读。

还是 Person 类的例子

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

sayName() {
console.log(this.name);
}
}

var p = new Person('ahonn', 21);
p.sayName(); // "ahonn"

通过 class 关键字声明 Person 类,constructor 函数即为 Person 类的构造函数,类属性的初始化也在其中。需要在各个对象中共享的方法也写在 class 中,避免了原来定义在 prototype 上时的撕裂感。