Eisen's Blog

© 2023. All rights reserved.

javascript 中的类继承

March 12, 2013

classjavascript

其实这部分我觉得最好的实践就是 coffeescript 的代码。coffee 自带了类继承。那么,解析成 js 后其代码就一目了然了。不过我还是自己练习了一下。解释都在注释里,废话不多说了。coffee 关于 extend 的部分在 https://coffeescript.org/

var __hasProp = {}.hasOwnProperty;
var __extend = function(child, parent) {
  // property inherit
  for(var prop in parent) {
    if (__hasProp.call(parent, prp)) {
      child[prop] = parent[prop];
    }
  }

  // method inherit
  var cot = function() {
  };
  // use the method without call parent constructor
  cot.prototype = parent.prototype;
  child.prototype = new cot();
  // set constructor to child itself
  child.prototype.constructor = child;
  // set the super with parent.prototype
  // so we can call child.__super__.method.call
  // to invoke parent method.
  child.__super__ = parent.prototype;

  return child;
};

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

  Animal.prototype.move = function() {
    console.log(this.name + ' move');
  };

  return Animal;
})();

var animal = new Animal('test');
animal.move();

var Snake = (function(_super) {
  function Snake() {
    // here is an example to call super method.
    Snake.__super__.constructor.apply(this, arguments);
  }
  __extend(Snake, _super);

  return Snake;
})(Animal);

var snake = new Snake('snk');
snake.move();

在 backbone 中的 extend 方法如下

  // Helper function to correctly set up the prototype chain, for subclasses.
  // Similar to `goog.inherits`, but uses a hash of prototype properties and
  // class properties to be extended.
  var extend = function(protoProps, staticProps) {
    var parent = this;
    var child;

    // The constructor function for the new subclass is either defined by you
    // (the "constructor" property in your `extend` definition), or defaulted
    // by us to simply call the parent's constructor.
    if (protoProps && _.has(protoProps, 'constructor')) {
      child = protoProps.constructor;
    } else {
      child = function(){ return parent.apply(this, arguments); };
    }

    // Add static properties to the constructor function, if supplied.
    _.extend(child, parent, staticProps);

    // Set the prototype chain to inherit from `parent`, without calling
    // `parent`'s constructor function.
    var Surrogate = function(){ this.constructor = child; };
    Surrogate.prototype = parent.prototype;
    child.prototype = new Surrogate;

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) _.extend(child.prototype, protoProps);

    // Set a convenience property in case the parent's prototype is needed
    // later.
    child.__super__ = parent.prototype;

    return child;
  };

代码里我有一个比较晕的地方

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) _.extend(child.prototype, protoProps);

这是要把 protoProps 一坨东西全部给 子类的 prototype 啊,那如果我修改了子类一个实例中继承来的属性或者方法,会不会导致其他子类示例方法的变动呢?答案是不会。

var View = function() {
};

View.prototype['type'] = 'abc';
var v1 = new View();
var v2 = new View();
console.log(v1.type); // abc
console.log(v1.hasOwnProperty('type'));// false
// 这种赋值会导致 v1 不会追朔到 View.prototype.type
// 而是重新建立自己的 property. 并不影响 prototype
v1.type = 'bcd';
console.log(v1.hasOwnProperty('type'));// true
console.log(v2.type);// abc

最后 _.extend

  // Extend a given object with all the properties in passed-in object(s).
  _.extend = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      if (source) {
        for (var prop in source) {
          obj[prop] = source[prop];
        }
      }
    });
    return obj;
  };