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

时间:2015-01-10 01:13:29

标签: javascript inheritance

我现在已经多次看过来自this question on class-free 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

1 个答案:

答案 0 :(得分:8)

Crockford所说的“无类继承”实际上仍然是原型继承。实际上,有two types原型继承机制:

  1. 通过委托进行原型继承(a.k.a.差异继承)。
  2. 通过克隆进行原型继承(a.k.a. concatenative inheritance)。
  3. 差异原型继承的一个例子

    这就是我传统上编写面向对象的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>
    &#13;
    &#13;
    &#13;

    连接原型继承的一个例子

    Crockford主张编写面向对象的JavaScript代码:

    &#13;
    &#13;
    var superFlanker = FighterAircraft({
        model: "Super Flanker",
        speed: "Mach 2.25",
        radar: "low"
    });
    
    superFlanker.describeFighterAircraft();
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

    使用mixins

    更好的连锁原型继承

    Crockford的连接原型继承方法有很多重复。另一种选择是:

    &#13;
    &#13;
    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();
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

    使用没有this

    的mixins

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

    &#13;
    &#13;
    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();
    &#13;
    <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>
    &#13;
    &#13;
    &#13;

    连锁继承的优点

    有很多理由喜欢composition over inheritance

    1. 简单的多重继承。
    2. 更快的财产访问。
    3. 我能想到的唯一缺点是,如果原型被修改,那么更改将不会反映在其实例上。但是,无论如何都没有理由改变原型。因此,我的mixins都被冷冻了。