为什么Javascript默认不支持继承?

时间:2013-04-28 08:27:42

标签: javascript inheritance

从OOPS基础开始,我总是使用继承作为代码重用的强大工具,

例如,如果我在OOPS中写下国际象棋程序,当我实现is-a关系时,

Class Piece{
  int teamColor;
  bool isLive;
  Positon pos;
  int Points; 
  .......
  int getTeamColor(){....}
  .......
};

Class Rook extend Piece{  //`is-a`
...... // No getTeamColor() definition here.. because the parent has the definition.
};

Class Pawn extend Piece{  //`is-a`
......// No getTeamColor() definition here.. because the parent has the definition.
};

我可以在javascript中使用has-a关系执行此操作,但我看到的缺点是, 我也必须重新定义派生类中的每个函数。

示例:在每个车,骑士,典当,国王......等中再次重新定义getTeamColor()。

     var Pawn = function(teamColor,pos){
     var piece = new Piece(teamColor,pos);
     .......

     this.getTeamColor = function(){        
          return piece.getTeamColor();
    };
    }

我的问题是, 为什么javascript不支持经典继承作为默认选项?

5 个答案:

答案 0 :(得分:17)

[2018年更新] 现在,JavaScript显然支持使用本机语言功能进行继承。

class A { }
class B extends A { }

[/更新]

JavaScript确实支持原型方式的继承。你需要的不是类,而是行为的封装和覆盖的能力。

function Piece() { }

Piece.prototype.color = "white";
Piece.prototype.getColor = function() { return this.color }
Piece.prototype.move = function() { throw "pure function" };

function Pawn() { }
Pawn.prototype = new Piece();    
Pawn.prototype.move = function() { alert("moved"); }

现在:

var p = new Pawn(); p.color = "black";

> p instanceof Piece

 p instanceof Pawn

p.getColor()

“黑色”

p.move()

...警报

这是基本的方法,并且有许多库将这变成了对想要课程的人来说熟悉的东西 - 所以说。

例如,使用JayData,您可以以更加封装的方式编写前一个(在链上自动构造函数调用的优势:

var Piece = $data.Base.extend("Piece", {
  move: function()  {  throw "pure class" } 
});

var Pawn =  Piece.extend("Pawn", {
  move: function() { ... }
});

var p = new Pawn();

答案 1 :(得分:7)

因为Javascript不是class-based object-oriented language,而是prototypal。这只是一个设计决定。

此外,对于我们今天使用它(从Node.js到ASM.js)所做的所有事情,Javascript从未真正“意味着”。它仍然相关的事实是Brendan Eich和Co.的遗嘱。所以你可能想知道为什么X或Y从未在JS中实现,但事实是我们使用JS来解决20年前无法预见的问题。

答案 2 :(得分:1)

许多关于各种传统OO语言(包括Java,C#和C ++)的好书都特别建议不要在可能的情况下使用“实现继承”。示例:Joshua Bloch的有效Java。

奇怪的事实是,虽然实现继承似乎给代码提供了常规的“形状”,但它并没有真正帮助你解决问题;更常见的是,从长远来看会引发问题。

那些作者倾向于将他们的祝福转移到“接口继承” - 但是在诸如JavaScript之类的鸭式语言中,没有必要明确声明这种继承。

在JS中,您可以通过简单地将其作为多个对象的属性来“重用”相同的函数。无论你需要什么样的遗产,你都可以从无到有的方式召唤它。

答案 3 :(得分:1)

JavaScript甚至没有真正的OOP风格的类,你只能模拟类似的东西。

在您的示例中,您可以通过执行

来实现继承
var Pawn = function(teamColor, pos) {
    Piece.call(this, teamColor, pos);
}

但是,您通常应该将方法附加到函数原型而不是任何新创建的对象。在这种情况下,您可以通过设置原型链来模拟继承,例如像CoffeeScript那样:

var a, b, _ref,
  __hasProp = {}.hasOwnProperty,
  __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; };

a = (function() {
  function a() {}

  return a;

})();

b = (function(_super) {
  __extends(b, _super);

  function b() {
    _ref = b.__super__.constructor.apply(this, arguments);
    return _ref;
  }

  return b;

})(a);

答案 4 :(得分:0)

以下代码块将"扩展"一个来自另一个的JavaScript原型,并确保" instanceof"运算符可以正确地为基类和派生类工作。

据我所知,将TestB的原型设置为新的TestA可以实现" instanceof"运营商。

将此传递给TestA会为新实例提供所有需要的属性和方法。

这样做是风格欲望与实用欲望之间的平衡。这将适用于大多数浏览器,甚至Internet Explorer,如果您被迫支持它。它也有帮助,因为让方法包含在同一块#34;感觉"很像你传统的OO语法。

此外,JavaScript不支持传统的面向对象风格的主要原因是它的创始理念。创始哲学是自由。这种自由旨在允许开发人员设计自己的样式以满足项目的需求。

function TestA(){
  var me = this;

  me.method = function(){
  }
}

function TestB(){
  TestA.call(this);
  var me = this;

  me.otherMethod = function(){
  }
}
TestB.prototype = new TestA();

var test = new TestB();

console.log(test);

console.log(test instanceof TestB);
console.log(test instanceof TestA);

//TestA { method: [Function], otherMethod: [Function] }
//true
//true