CoffeeScript 中的继承实现

Inheritance implementation in CoffeeScript

本文关键字:实现 继承 CoffeeScript      更新时间:2023-09-26

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

现在我正在检查 Coffescript 如何实现它。因此,给定父项和子项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();

正如我所理解的那样,咖啡脚本中继承的实现是由不同模式的组合产生的:

1. 经典代理构造函数

在这种情况下,我们还重置constructor pointer并存储超类引用。斯特凡诺夫对"圣杯"的定义。使用此模式,子项仅继承原型的属性。

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

2. 通过复制属性进行继承

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

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

3. 经典模式 - 租用构造函数(或借用构造函数(

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

问题:

  1. 我的假设正确吗?咖啡脚本编译器是否使用 1+2+3?
  2. 通过复制进行的继承似乎使用浅拷贝,这意味着它不会检查该属性是否是对象/数组并开始递归。即使结果也很艰难,似乎是一个完美的深层副本(对象/数组是副本,而不是引用(。为什么/如何?
  3. 租一个构造器不是在重复继承吗?复制属性,然后再次调用父构造函数?
  4. _extends函数也可以在对象之间而不是构造函数之间使用吗?

谢谢

  1. 租一个构造器不是在重复继承吗?复制属性,然后再次调用父构造函数?

此处复制的属性...

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

。不是原型属性,它们是"类级"属性,是在函数本身上定义的方法。它将属性从函数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');

无论如何,复制属性然后"再次调用父构造函数"并不是真正会发生的情况。

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