CoffeeScript 中的继承实现

Inheritance implementation in CoffeeScript

我正在学习 javascript 中的不同继承实现,主要是根据 Stoyan Stefanov 的 Javascript Patterns 书。

现在我正在检查 Coffescript 是如何实现它的。所以给定一个 parent 和一个 child classes 或构造函数:

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

sam = new Snake "Sammy the Python"
sam.move()

它们被编译为:

var Animal, Horse, Snake, sam,
    _extends = function(child, parent) {
        for (var key in parent) {
            if (_hasProp.call(parent, key)) child[key] = parent[key];
        }

        function ctor() {
            this.constructor = child;
        }
        ctor.prototype = parent.prototype;
        child.prototype = new ctor();
        child.__super__ = parent.prototype;
        return child;
    },
    _hasProp = {}.hasOwnProperty;

Animal = (function() {
    function Animal(_name) {
        this.name = _name;
    }

    Animal.prototype.move = function(meters) {
        return alert(this.name + (" moved " + meters + "m."));
    };

    return Animal;

})();

Snake = (function(_super) {
    _extends(Snake, _super);

    function Snake() {
        return Snake.__super__.constructor.apply(this, arguments);
    }

    Snake.prototype.move = function() {
        alert("Slithering...");
        return Snake.__super__.move.call(this, 5);
    };

    return Snake;

})(Animal);

sam = new Snake("Sammy the Python");
sam.move();

据我了解,coffescript 中继承的实现是不同模式组合的结果:

1。经典代理构造函数

在这种情况下,我们还重置了 constructor pointer 并存储了超类引用。 Stefanov 定义了什么 'Holy Grail'。 使用此模式,child 仅继承原型的属性。

// the proxy function
function ctor() {
  this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;

2。通过复制属性继承

使用这种模式,我们只需将一个 object 的属性复制到另一个

_hasProp = {}.hasOwnProperty;
for (var key in parent) {
  if (_hasProp.call(parent, key)) child[key] = parent[key];
}

3。经典模式 - Rent-a-Constructor(或借用构造函数)

function Snake() {
  return Snake.__super__.constructor.apply(this, arguments);
}

问题:

  1. 我的假设是否正确? coffescript 编译器使用 1+2+3 吗?
  2. 通过复制继承似乎使用了浅拷贝,这意味着它没有检查是否属性 是一个 object/array 并开始递归。即使结果看起来很完美深拷贝(objects/arrays是拷贝,不是参考)。 Why/how?
  3. rent-a-constructor不就是重复继承吗?复制属性然后再次调用 parent 构造函数?
  4. _extends 函数也可以在 object 之间使用而不是构造函数吗?

谢谢

  1. Isn't the rent-a-constructor creating a repetition of the inheritance? Properties copied and then parent constructor called again?

正在将属性复制到此处...

    for (var key in parent) {
        if (_hasProp.call(parent, key)) child[key] = parent[key];
    }

... 不是原型属性,它们是 "class level" 属性,在函数本身上定义的方法。它将属性从函数 Animal 复制到函数 Horse.

区别是:

class Animal
  # Not part of prototype, part of Animal, must be copied
  @build: (name) ->
    new @(name)

  constructor: (name) ->
    @name = "An animal named #{name}"

  # Part of prototype
  sayName: ->
    alert(@name)

class Bird extends Animal
  constructor: (name) ->
    @name = "A bird named #{name}"


# Both Animal and Bird have build because of the copying of properties:
a = Animal.build('sam') # an animal named sam
b = Bird.build('bob') # a bird named bob

编译后的一些注释JavaScript:

var Animal, Bird, a, b,
  __extends = function(child, parent) {
      for (var key in parent) {
          # Copies Animal.build to Bird.build
          if (__hasProp.call(parent, key)) child[key] = parent[key];
      }

      function ctor() {
          this.constructor = child;
      }
      # Makes sayName available to Bird via prototypal inheritance
      ctor.prototype = parent.prototype;
      child.prototype = new ctor();
      child.__super__ = parent.prototype;
      return child;
  },
  __hasProp = {}.hasOwnProperty;

Animal = (function() {
  Animal.build = function(name) {
    return new this(name);
  };

  function Animal(name) {
    # This still (theoretically) needs to be invoked, regardless of whether
    # the properties are copied over, though it isn't invoked in this example
    this.name = "An animal named " + name;
  }

  Animal.prototype.sayName = function() {
    return alert(this.name);
  };

  return Animal;

})();

Bird = (function(_super) {
  __extends(Bird, _super);

  # There is no "Bird.build" defined here, it is copied from Animal

  function Bird(name) {
    this.name = "A bird named " + name;
  }

  # There is no "move" defined here, it is provided by our prototyep
  return Bird;

})(Animal);

a = Animal.build('sam');

b = Bird.build('bob');

无论如何,复制属性然后 "the parent constructor being called again" 并不是真正会发生的事情。

属性未在父构造函数中定义,父构造函数只是需要 运行 的可执行代码块。它可能不定义任何属性,或者它可能定义一堆属性,但这些属性不会由原型或 _hasOwnProperty 循环设置。