【问题标题】:Is John Resig's Javascript inheritance snippet deprecated?John Resig 的 Javascript 继承片段是否已弃用?
【发布时间】:2013-02-09 15:26:09
【问题描述】:

我正在寻找一种创建两个类的简单方法,一个继承自另一个,子重新定义父方法之一,并在新方法中调用父方法。

例如,有一个类AnimalDog,其中Animal 类定义了一个方法makeSound(),该方法确定了如何输出声音,然后Dog 在它自己的makeSound() 方法中覆盖该方法以生成一个“ woof" 的声音,但同时也调用 Animal 的 makeSound() 来输出那个 woof。

我查看了 John Resig 的模型 here,但它使用了本机 arguments.callee 属性,该属性在 ECMA 脚本 5 中显然已被贬值。这是否意味着我不应该使用 John Resig 的代码?

使用 Javascript 的原型继承模型编写我的动物/狗代码的一种简洁的方法是什么?

【问题讨论】:

标签: javascript oop inheritance prototypal-inheritance


【解决方案1】:

这是否意味着我不应该使用 John Resig 的代码?

正确,不是在严格模式下使用 ES5 时。但是,它可以很容易地适应:

/* Simple JavaScript Inheritance for ES 5.1
 * based on http://ejohn.org/blog/simple-javascript-inheritance/
 *  (inspired by base2 and Prototype)
 * MIT Licensed.
 */
(function(global) {
  "use strict";
  var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  function BaseClass(){}

  // Create a new Class that inherits from this class
  BaseClass.extend = function(props) {
    var _super = this.prototype;

    // Set up the prototype to inherit from the base class
    // (but without running the init constructor)
    var proto = Object.create(_super);

    // Copy the properties over onto the new prototype
    for (var name in props) {
      // Check if we're overwriting an existing function
      proto[name] = typeof props[name] === "function" && 
        typeof _super[name] == "function" && fnTest.test(props[name])
        ? (function(name, fn){
            return function() {
              var tmp = this._super;

              // Add a new ._super() method that is the same method
              // but on the super-class
              this._super = _super[name];

              // The method only need to be bound temporarily, so we
              // remove it when we're done executing
              var ret = fn.apply(this, arguments);        
              this._super = tmp;

              return ret;
            };
          })(name, props[name])
        : props[name];
    }

    // The new constructor
    var newClass = typeof proto.init === "function"
      ? proto.hasOwnProperty("init")
        ? proto.init // All construction is actually done in the init method
        : function SubClass(){ _super.init.apply(this, arguments); }
      : function EmptyClass(){};

    // Populate our constructed prototype object
    newClass.prototype = proto;

    // Enforce the constructor to be what we expect
    proto.constructor = newClass;

    // And make this class extendable
    newClass.extend = BaseClass.extend;

    return newClass;
  };

  // export
  global.Class = BaseClass;
})(this);

【讨论】:

  • 这很好用,为了确保它在旧版 IE 中仍然有效,你可以包含这个 polyfill if (!Object.create) { Object.create = (function(){ function F(){} return function(o){ if (arguments.length != 1) { throw new Error('Object.create implementation only accepts one parameter.'); } F.prototype = o; return new F() } })(); }
  • 这会导致使用Class.extend() 生成的实例显示类型为Class.extend.init,子类显示类型为proto.(anonymous function)。 Resig 的原始 sn-p 为所有实例提供了Class 的显示类型。也就是说,运行* instanceof Class 会为这次重写返回true,但它在浏览器控制台中仍然令人分心且丑陋。
  • @Adrian:如果您不喜欢调试器的类型,请使用named functions(对于init 属性)。我现在还命名了默认构造函数。
  • 啊哈。傻我。我不应该在累的时候尝试阅读 JS 代码。感谢更新。 :)
  • 我的理解是 init 就像你的构造函数一样,是否有一个析构函数方法在对象被销毁之前被调用?
【解决方案2】:

带有 Object.create() + 分配构造函数的原型链

function Shape () {
    this.x = 0;
    this.y = 0;
}

Shape.prototype.move = function (x, y) {
    this.x += x;
    this.y += y;
};

function Rectangle () {
    Shape.apply(this, arguments); // super constructor w/ Rectangle configs if any
}

Rectangle.prototype = Object.create(Shape.prototype); // inherit Shape functionality
// works like Rectangle.prototype = new Shape() but WITHOUT invoking the constructor

Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle && rect instanceof Shape // returns true

来自Object.create documentation

关于new keyword的信息

【讨论】:

  • 这是2015年的方法
【解决方案3】:

这是我想出的使用链式继承以及允许 _super 工作的方法。

/**
 * JavaScript simple inheritance
 * by Alejandro Gonzalez Sole (base on John Resig's simple inheritance script)
 * MIT Licensed.
 **/
(function (){
    var initializing = false,
      fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.* /;

    function Class(){};

    function inheritClass(superClass){
      var self = this;
      function Class(){
        if (!initializing && typeof this._constructor === 'function')
          this._constructor.apply(this, arguments);
      }

      Class.prototype = superClass.prototype;
      Class.prototype._constructor = superClass;
      Class.prototype.constructor = Class;
      Class.extend = extendClass;
      //currenlty if you inhert multiple classes it breaks
      Class.inherit = inheritClass;
      return Class;
    };

    function extendClass(prop) {
      var self = this;
      var _super = self.prototype;

      function Class(){
        if (!initializing && typeof this._constructor === 'function')
          this._constructor.apply(this, arguments);
      }

      initializing = true;
      var prototype = new self();
      initializing = false;

      for (var name in prop) {
        prototype[name] = typeof prop[name] == "function" &&
          typeof _super[name] == "function" && fnTest.test(prop[name]) ?
          (function(name, fn){
            return function() {
              var tmp = this._super;
              this._super = _super[name];
              var ret = fn.apply(this, arguments);
              this._super = tmp;
              return ret;
            };
          })(name, prop[name]) : prop[name];
      }

      Class.prototype = prototype;
      Class.prototype.constructor = Class;
      Class.extend = extendClass;
      Class.inherit = inheritClass;

      return Class;
    };

    Class.extend = extendClass;
    Class.inherit = inheritClass;

})();


//EXAMPLE

function Person(){
  this.name = "No name";
  console.log("PERSON CLASS CONSTRUCTOR")
}
Person.prototype.myMethod = function (t){
  console.log("MY PERSON", t, this.name);
  return -1;
}

var TestPerson = Class.inherit(Person).extend({
    _constructor: function(){
      this._super();
      this.name = "JOhn";
      console.log("TEST PERSON CONSTRUCTOR");
    },
    myMethod: function (t){
      console.log("TEST PERSON", t, this.name);
      return this._super(t)
    }
});


var test = new TestPerson();

console.log(test.myMethod("BA"));

我一直在我的 pixi 包装器 https://github.com/guatedude2/pixijs-cli 上对其进行测试,到目前为止它对我来说效果很好。

我在这种方法中遇到的唯一问题是您只能继承一次。如果再次运行继承,它将覆盖以前的继承。

【讨论】:

  • 我确实写了一个例子,只是这只是一种不同的方法。我更新了我正在使用的最新版本。它与 Resig 编写的类似,但它向原型类添加了继承并适用于 ES6。
【解决方案4】:

我更喜欢 TypeScript 生成继承形式的方式(从下拉列表中选择 Simple Inheritance)。那个不使用arguments.callee,而是使用__extends prototype

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.move = function (meters) {
        alert(this.name + " moved " + meters + "m.");
    };
    return Animal;
})();
var Snake = (function (_super) {
    __extends(Snake, _super);
    function Snake(name) {
        _super.call(this, name);
    }
    Snake.prototype.move = function () {
        alert("Slithering...");
        _super.prototype.move.call(this, 5);
    };
    return Snake;
})(Animal);
var Horse = (function (_super) {
    __extends(Horse, _super);
    function Horse(name) {
        _super.call(this, name);
    }
    Horse.prototype.move = function () {
        alert("Galloping...");
        _super.prototype.move.call(this, 45);
    };
    return Horse;
})(Animal);
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-01
    • 2012-06-19
    • 1970-01-01
    • 2023-03-06
    • 2010-09-27
    • 2011-09-19
    • 2016-10-17
    • 1970-01-01
    相关资源
    最近更新 更多