JavaScript Factory Functions vs Constructor Functions vs Classes
JavaScript Factory Functions vs Constructor Functions vs Classes
Source: JavaScript Factory Functions vs Constructor Functions vs Classes
中英文对照:
- Factory Functions:工厂函数
- Constructor Functions:构造器函数
- Classes:类
JavaScript 中的“工厂函数”与“构造器函数”以及“类”
以下代码分别展示出以上三种函数的创建方式
// class
class ClassCar {
driver() {
console.log('Vroom!');
}
}
const car1 = new ClassCar();
console.log(car1.driver());
// construnctor
function ConstrunctorCar() {}
ConstrunctorCar.prototype.drive = function() {
console.log('Vroom!');
}
const car2 = new ConstructorCar();
console.log(car2.driver());
// factory
const proto = {
driver() {
console.log('Vroom!');
}
};
const factoryCar = () => Object.create(proto);
const car3 = factoryCar();
console.log(car3.driver());
在 JavaScript 中,任何函数都可以返回一个新对象。当它不是构造器函数或类,那么它被称为工厂函数。
ES6 中 的 class 实际上是构造器函数的一种语法糖,简而言之,我们可以认为 ES6 中的 class 与构造器函数是一样的。
工厂函数和构造器函数的区别
最大的也是唯一的区别是:构造器函数强制要求调用者使用 new
关键字;而工厂函数并不需要;
当使用 new
关键字后都发生了什么呢?
- 实例化一个新的实例对象,同时绑定
this
到构造器函数的内部; - 绑定
isntance.__proto__
到Constructor.prototype
; - 因为第 2 步带来的副作用,需要绑定
instance.__proto__.constructor
到Construnctor
; - 隐式返回
this
,这个this
指向instance
;
也就是说工厂函数在第 2 步的时候所产生的副作用没有被自动修正,也就是 instance.__proto__.constructor
此时并未指向 Construnctor
。
构造器函数和类的优点
- 大部分书都会教你用这种方法;
this
指向/引用 新对象;- 人们习惯这种方式;
构造器函数和类的缺点
- 需要使用
new
关键字。此点在当前的开发环境中已经不是问题了; - 实例化的细节被泄露到调用 API 中。这句不好理解,大体的意思是所有的调用者被构造器的实现所裹挟,也就是紧耦合状态。这种情况下非常不便于重构工作;
- 违反了开闭原则。大体意思是随着项目的深入,最终都会走上将类重构成工厂的路,此时由于 JavaScript 的语言结构问题,以前调用类的客户端均使用了
new
关键字,这导致无法将类重构成工厂,会带来很多麻烦。个人理解,不违反开闭原则最好的办法是使用interface
,即面向接口编程,但由于 JavaScript 采用的是对象原型继承方式,并没有类这个概念,因此也就没有interface
的概念,不过利用工厂函数可以变相的实现interface
比如:
const Interface = (proto) => Object.create(proto)
/**
* 此时我们可以约定 proto 的 shape
* 比如:必须包含 a 属性,b 方法
* 再结合 typescript 可以实现编译器校验
*/
instanceOf
具有欺骗性。这是因为instanceOf
本质是不是类型检测的方法,而是检测instance.__proto__
是否等于Constructor.prototype
function foo() {}
const bar = {a: 'a'};
foo.prototype = bar;
console.log(bar instanceof foo); //false
const baz = Object.create(bar);
/**
* baz.__proto__ = bar
* foo.prototype = bar
* 因此 (baz instanceof foo) 返回 true
* 这是错误的
*/
console.log(baz instanceof foo); // true
使用工厂函数的优点
工厂函数比构造器函数和类更具备扩展性,它有很多更安全的代码重用机制。