【问题标题】:Trying simple approach to OPP inheritance in Javascript (ES5)在 Javascript (ES5) 中尝试简单的 OPP 继承方法
【发布时间】:2015-08-27 19:14:55
【问题描述】:

出于好奇,我在 Javascript 中使用原型继承和 OOP 继承。大多数结果涉及使用函数模拟“类”和“扩展”概念,而其他结果则使用原型和构造函数。

我写了这段代码:

function Warrior(weaponName) {
    var weapon = weaponName;
    this.getWeapon = function() {
        return weapon;
    };
    this.setWeapon = function(value) {
        weapon = value;
    };
    this.displayInfo = function() {
        return {
            "weapon": this.getWeapon(),
        };
    };
}

function Archer() {
    var accuracy = "86%";
    this.parent = Archer.prototype; // Inheritance workaround
    this.getAccuracy = function() {
        return accuracy;
    };
    this.setAccuracy = function(value) {
        accuracy = value;
    };
    this.displayInfo = function() {
        var form = this.parent.displayInfo();
        form.accuracy = this.getAccuracy();
        return form;
    };
}
Archer.prototype = new Warrior("bow");
var w = new Warrior("sword");
var a = new Archer();
console.log(w.displayInfo());
console.log(a.displayInfo());

我这样做是为了在显示来自 Warrior 类的信息时,它会将对象显示为

{ weapon: "sword" }

当显示来自Archer的信息时,对象是:

{ weapon: "sword", accuracy: "86%" }

“子类”从“超类”获取信息并添加到其中。从 Archer 调用“getWeapon()”或“setWeapon”也可以。即使我添加了第三类“Kyudoka”,它扩展了“Archer”并拥有它自己的属性,链条也没有问题。

但是与我在研究时发现的更复杂的代码相比,我觉得这可能是一个幼稚的实现(“继承解决方法”行)并且我遗漏了一些东西(考虑到 JS 有很多微妙之处)。

这是一个理论问题,我没有在任何系统中使用此代码。

【问题讨论】:

  • 您可以使用Object.getPrototypeOf(this) 而不是硬编码Archer.prototype 作为属性。
  • Warrior 的每个实例创建 3 个函数。因此,例如,如果您有 3 个弓箭手,那么它将产生 300 个功能。通过原型继承,您只创建 3 个函数,无论您有多少实例。这方面可以做一些优化,我不知道。
  • 这篇文章帮助了我:JavaScript object creation
  • @WhiteHat - 此类文章的问题在于,它们过于复杂了他们试图向您展示的内容的本质。他为一个敷衍的任务展示了太多的概念。
  • @LyeFish - 谢谢!更少的硬编码总是好的!

标签: javascript oop inheritance prototype


【解决方案1】:

javascript中主要有3种继承,根据Javascript the Good Parts一书:PseudoclassicalPrototypal 和 功能性

您刚刚发布的内容适合 Pseudoclassical 继承,您可以在其中使用构造函数模拟 Class 行为。

我发现 Functional 模式更加有用和灵活,它允许您保护变量(将它们设为私有)。

var constructor = function (spec, my) {
  var that, other private instance variables;
  my = my || {};
  //Add shared variables and functions to my
  that = a new object;
  //Add privileged methods to that
  return that;
}

原型基本上是让您的对象直接从其他有用的对象继承,这就像将它们(有用的对象)作为您的新对象构造函数原型。

Object.beget = function (o) {
  var F = function () {};
  F.prototype = o;
  return new F();
};

var a = {}
//Add shared variables to a
var b = Object.beget(a);
//Add new methods to b

每个模式都有很多考虑因素,例如 Crockford 在他的书中说“函数式模式具有很大的灵活性。它比伪经典模式需要更少的努力, 并为我们提供了更好的封装和信息隐藏以及对超级方法的访问。”,但我也看到过相反的争论,例如 http://bolinfest.com/javascript/inheritance.php

编辑 ------

如果您想了解实现超级方法的不同方法,在 函数式 模式中,您可以执行以下操作:

Function.prototype.method = function (name, func) {
  this.prototype[name] = func;
  return this;
};

Object.method('superior', function (name) {
  var that = this,
  method = that[name];
  return function ( ) {
    return method.apply(that, arguments);
  };
});

var archer = function (spec, accuracy) {
  var that = warrior(spec),
  super_displayInfo = that.superior('displayInfo');
  that.getAccuracy = function() {
    return accuracy;
  };
  that.setAccuracy = function(value) {
    accuracy = value;
  };
  that.displayInfo = function (n) {
    var form = super_displayInfo()
    form.accuracy = that.getAccuracy();
    return form;
  };
  return that;
};

【讨论】:

  • 谢谢!事实上,“伪古典”听起来更像是我想要的。我在这个设置中考虑了私有变量。默认情况下,我只会在“类”中使用“var varable_name”,然后根据需要使用 Object、defineProperty / Object、defineProperties getter 和 setter 公开它。我仍然需要对这个想法进行更多修改,我想它不会涵盖“受保护”的变量情况。
  • 没问题!如果您有兴趣,我添加了一种不同的解决方法来达到更好的方法 =)
【解决方案2】:

把函数放到原型上...

function Warrior(weaponName) {
    this.weapon = weaponName;
}

Warrior.prototype = {

    getWeapon : function() {
        return this.weapon;
    },

    setWeapon : function(value) {
        this.weapon = value;
    },

    displayInfo : function() {
        return { "weapon" : this.getWeapon() };
    }
};

//----------------------------------

function Archer(weaponName) {
    Warrior.call(this, weaponName);
    this.accuracy = "86%";
}

Archer.prototype = Object.create(Warrior.prototype);
Archer.prototype.constructor = Archer;

Archer.prototype.getAccuracy = function() {
    return this.accuracy;
};

Archer.prototype.setAccuracy = function(value) {
    this.accuracy = value;
};

Archer.prototype.displayInfo = function() { 
    return "weapon: " + this.getWeapon() + ", accuracy: " + this.getAccuracy();
};


//----------------------------------

var w = new Warrior("sword");
var a = new Archer("axe");
console.log(w.displayInfo()); // Object {weapon: "sword"}
console.log(a.displayInfo()); // weapon: axe, accuracy: 86%

编辑:固定递归

【讨论】:

  • 您在Archer 上的displayInfo 实现会创建无限递归,因为它不调用Weapon.displayInfo
  • @Mike C - 很好,我只是复制了 OP 代码,没有进入细节。
  • @Data - 感谢您对原型的洞察!我知道我可以用我需要的数据覆盖弓箭手的 displayInfo(),但我试图想出一种方法来实际使用“超级”方法。但是我会尝试一下 Object.create 和构造函数,就像你描述的那样,看看会发生什么!
  • @adrield - 我不会理会super 之类的东西,它不适合 JS 原型结构。您创建具有自己属性的对象并将原型链接到另一个原型。 JS 只能向下看,不能向上看,所以试图破解超级引用会破坏原型继承/委托的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
相关资源
最近更新 更多