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
上时的撕裂感。