JavaScript构造函数模式

sup*_*kas 3 javascript constructor design-patterns

我正在寻找一个理智的解决JavaScript只有一个构造函数的问题.所以假设我们有一个类Point,我们希望允许从坐标创建对象.

我会在所有这些例子中忽略类型检查.

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Run Code Online (Sandbox Code Playgroud)

简单.如何从其他点创建积分?

function Point(x, y) {
   if (!y /* && x instanceof Point */) {
     y = x.y;
     x = x.x;
   }
   this.x = x;
   this.y = y;
}
Run Code Online (Sandbox Code Playgroud)

这很快变成了一场噩梦.所以我想要的是一个设计模式,它将这两个构造函数分离(或者将一个分成两个,而不是).Objective-C有一个很好的模式.ObjC人某物创造物体.

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.withPoint = function(point) {
  return new Point(point.x, point.y);
};
Run Code Online (Sandbox Code Playgroud)

到目前为止,我非常喜欢这个.但现在我们有两种不同的语法.

var a = new Point(4, 2);
var b = Point.withPoint(a);
Run Code Online (Sandbox Code Playgroud)

好吧,这很容易,不是吗?只需添加Point.withCoordinates.但那么构造函数呢?把它藏起来?我不知道.我想这就是你进来的地方.


这就是我决定采用的方式:

var Point = {
  withCoordinates: function(x, y) {
    if (typeof x == 'number' && typeof y == 'number') {
      this.x = x;
      this.y = y;
      return this;
    }
    throw TypeError('expected two numbers');
  },
  withPoint: function(point) {
    if (typeof point.x == 'number' && typeof point.y == 'number') {
      this.withCoordinates(point.x, point.y);
      return this;
    }
    throw TypeError('expected a point');
  }
};

var a = Object.create(Point).withCoordinates(0, 0);
var b = Object.create(Point).withPoint(a);
Run Code Online (Sandbox Code Playgroud)

优点:

  • 没有样板
  • 描述性语法/ API
  • 很好地扩展
  • 实用
  • 易于测试

缺点:

  • 实例不知道它们是否已初始化
  • 不能只为类添加属性(比较Number.MAX_SAFE_INTEGER)

注意类型检查Point.withPoint.它允许点击事件等鸭类点.

function onClick(event) {
  var position = Object.create(Point).withPoint(event);
}
Run Code Online (Sandbox Code Playgroud)

还要注意在某种默认ctor中缺少零初始化.积分实际上是一个非常好的例子,为什么这并不总是一个好主意.

geo*_*org 5

就像在ObjC上一样,你可以有单独的"alloc"和"init"条目,例如:

function Point() {}

Point.prototype.withCoordinates = function(x, y) {
    this.x = x;
    this.y = y;
    return this;
}

Point.prototype.withOffsetFromPoint = function(p, delta) {
    this.x = p.x + delta;
    this.y = p.y + delta;
    return this;
}

p = new Point().withOffsetFromPoint(
    new Point().withCoordinates(5, 6),
    10);

console.log(p) // 15, 16
Run Code Online (Sandbox Code Playgroud)

其中虚拟构造函数基本上是"alloc"的东西.

同样以更现代的方式,没有new:

Point = {
    withCoordinates: function(x, y) {
        this.x = x;
        this.y = y;
        return this;
    },
    withOffsetFromPoint: function(p, delta) {
        this.x = p.x + delta;
        this.y = p.y + delta;
        return this;
    }
}

p = Object.create(Point).withOffsetFromPoint(
    Object.create(Point).withCoordinates(5, 6),
    10);

console.log(p)
Run Code Online (Sandbox Code Playgroud)

另一个(也许是最惯用的)选项是使构造函数接受命名参数(通过"options"对象):

p = new Point({ x:1, y:2 })
p = new Point({ point: someOtherPoint })
Run Code Online (Sandbox Code Playgroud)