【问题标题】:Inheritance implementation in CoffeeScriptCoffeeScript 中的继承实现
【发布时间】:2015-04-05 14:50:25
【问题描述】:

我正在学习 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 并存储超类引用。 Stefanov 定义的“圣杯”。 使用这种模式,孩子只继承原型的属性。

// 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. 我的假设正确吗? coffescript编译器是用1+2+3的吗?
  2. 复制继承 似乎使用了浅复制,这意味着它不会检查属性是否为对象/数组并开始递归。即使是艰难的结果似乎也是一个完美的深拷贝(对象/数组是副本,而不是引用)。为什么/如何?
  3. rent-a-constructor 不是在创建继承的重复吗?复制属性然后再次调用父构造函数?
  4. _extends 函数也可以在对象之间而不是构造函数之间使用吗?

谢谢

【问题讨论】:

    标签: javascript oop inheritance coffeescript


    【解决方案1】:
    1. rent-a-constructor 不是在创建继承的重复吗?复制属性然后再次调用父构造函数?

    这里复制的属性...

        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');
    

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

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

    【讨论】:

    • 我明白了,谢谢。但是函数是对象,据我所知,对象是通过引用复制的。因此,如果我们在创建b 之后将@buildAnimal 更改,是否意味着Birdbuild 版本也会被修改?
    • 没有。你不能“改变”动物的功能build。您可以替换它,通过Animal.build = function () {...} 分配一个新功能,但这不会修改仍附加到Bird 的原始build
    • 属性是一个通用对象呢?我们可以在 Animal 中修改它不是吗?在这种情况下,孩子会发生什么
    • 是的。如果我们添加class Animal; @build = { },那么AnimalBird 将共享同一个对象。
    猜你喜欢
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 2013-03-01
    • 2014-08-02
    • 2015-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多