从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不支持经典继承作为默认选项?
答案 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