如何在 Crockford 的新构造函数模式中共享“构造函数”功能

How do I share "constructor" functionality in Crockford's new constructor pattern?

本文关键字:构造函数 共享 功能 模式 Crockford      更新时间:2023-09-26

我已经在无类 OOP 上多次观看了这个问题的视频,但我在将其应用于现实世界的示例时遇到了麻烦。

Crockford 的新构造函数模式如下所示:

function constructor(spec) {
  let {member} = spec,
      {other}  = other_constructor(spec),
      method   = function () {
        // accesses member, other, method, spec
      };
  return Object.freeze({
      method,
      other,
  });
}

其中spec是一个选项哈希,结果对象公开关闭所有内部成员的方法。忽略解构(因为这可以在当今的 JS 中以长格式完成),我如何在现实世界的示例中应用此模式?

我目前有一个带有基类Module(model, id)的库,其中model是一些引导数据。

// constructor, original flavor
function Module(model, id) {
  this.id = id;
  this.model = model;
}

然后,我有几种从此父Module继承的模块风格。在 Crockford 的模式下,我会把它作为一个"构造函数":

// constructor, Crockford's Cool Ranch
function module(spec) {
    let id = spec.id,
        model = spec.model;
    return Object.freeze({});
}

如何使用 Crockford 的模式(它似乎根本不使用继承,而是从多个来源组合一个对象)在多种风格的模块之间共享这种基本结构

我知道idmodel将成为每个模块"构造函数"中的局部变量;我本质上是在问如何避免使用 Crockford 模式对每种风格的模块重复model = spec.model

Crockford所说的"无类继承"实际上仍然是原型继承。实际上,有两种类型的原型遗传机制:

  1. 通过委托进行原型继承(又称差异继承)。
  2. 通过克隆进行原型继承(又称串联遗传)。

差异原型遗传的一个例子

这就是我传统上编写面向对象的JavaScript代码的方式:

var Aircraft = defclass({
    constructor: function (model, speed) {
        this.model = model;
        this.speed = speed;
    },
    describeAircraft: function () {
        alert("The " + this.model + " flies at " + this.speed + " speed.");
    }
});
var FighterAircraft = extend(Aircraft, {
    constructor: function (model, speed, radar) {
        Aircraft.call(this, model, speed);
        this.radar = radar;
    },
    describeFighterAircraft: function () {
        this.describeAircraft();
        alert("It has a " + this.radar + " radar signature.");
    }
});
var superFlanker = new FighterAircraft("Super Flanker", "Mach 2.25", "low");
superFlanker.describeFighterAircraft();
<script>
function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}
function extend(constructor, properties) {
    var prototype = Object.create(constructor.prototype);
    for (var name in properties) prototype[name] = properties[name];
    return defclass(prototype);
}
</script>

串联原型遗传的示例

这就是 Crockford 提倡编写面向对象的 JavaScript 代码的方式:

var superFlanker = FighterAircraft({
    model: "Super Flanker",
    speed: "Mach 2.25",
    radar: "low"
});
superFlanker.describeFighterAircraft();
<script>
function Aircraft(spec) {
    var model = spec.model;
    var speed = spec.speed;
    function describeAircraft() {
        alert("The " + model + " flies at " + speed + " speed.");
    }
    return Object.freeze({
        model: model,
        speed: speed,
        describeAircraft: describeAircraft
    });
}
function FighterAircraft(spec) {
    var aircraft = Aircraft(spec);
    var model    = spec.model;
    var speed    = spec.speed;
    var radar    = spec.radar;
    function describeFighterAircraft() {
        aircraft.describeAircraft();
        alert("It has a " + radar + " radar signature.");
    }
    return Object.freeze({
        model: model,
        speed: speed,
        radar: radar,
        describeFighterAircraft: describeFighterAircraft
    });
}
</script>

使用mixins更好地连接原型遗传

罗克福德的拼接原型遗传方法有很多重复。另一种选择是:

var aircraft = mixin({
    describeAircraft: function () {
        alert("The " + this.model + " flies at " + this.speed + " speed.");
    }
});
var fighterAircraft = mixin(aircraft, {
    describeFighterAircraft: function () {
        this.describeAircraft();
        alert("It has a " + this.radar + " radar signature.");
    }
});
var superFlanker = fighterAircraft({
    model: "Super Flanker",
    speed: "Mach 2.25",
    radar: "low"
});
superFlanker.describeFighterAircraft();
<script>
function mixin() {
    var length = arguments.length;
    var index  = 0;
    while (index < length) {
        var properties = arguments[index++];
        for (var name in properties)
            constructor[name] = properties[name];
    }
    return Object.freeze(constructor);
    function constructor(object) {
        for (var name in constructor)
            object[name] = constructor[name];
        return Object.freeze(object);
    }
}
</script>

无需this使用混音

是的,您可以在不使用 this 的情况下使用 mixin。但是,我不明白你为什么要:

var aircraft = mixin({
    describeAircraft: function (aircraft) {
        alert("The " + aircraft.model + " flies at " +
            aircraft.speed + " speed.");
    }
});
var fighterAircraft = mixin(aircraft, {
    describeFighterAircraft: function (fighterAircraft) {
        fighterAircraft.describeAircraft();
        alert("It has a " + fighterAircraft.radar + " radar signature.");
    }
});
var superFlanker = fighterAircraft({
    model: "Super Flanker",
    speed: "Mach 2.25",
    radar: "low"
});
superFlanker.describeFighterAircraft();
<script>
function mixin() {
    var length = arguments.length;
    var index  = 0;
    while (index < length) {
        var properties = arguments[index++];
        for (var name in properties)
            constructor[name] = properties[name];
    }
    return Object.freeze(constructor);
    function constructor(object) {
        for (var name in constructor) {
            var value    = constructor[name];
            object[name] = typeof value === "function" ?
                value.bind(null, object) : value;
        }
        return Object.freeze(object);
    }
}
</script>

串联继承的优点

选择组合而不是继承的原因有很多:

  1. 简单的多重继承。
  2. 更快的物业访问。

我能想到的唯一缺点是,如果修改了原型,那么更改将不会反映在其实例上。但是,无论如何都没有充分的理由更改原型。因此,我的混音都被冷冻了。