什么是好的最低限度Javascript继承方法?
我正在重写一个JavaScript项目,我希望能够使用面向对象的方法来组织当前代码的混乱。主要的问题是这个JavaScript应该作为第三方网站内的小部件运行,我不能让它与其他网站可能使用的其他JavaScript库冲突 因此,我正在寻找一种在JavaScript中编写“类式”继承的方法,该方法具有以下要求:什么是好的最低限度Javascript继承方法?,javascript,inheritance,oop,Javascript,Inheritance,Oop,我正在重写一个JavaScript项目,我希望能够使用面向对象的方法来组织当前代码的混乱。主要的问题是这个JavaScript应该作为第三方网站内的小部件运行,我不能让它与其他网站可能使用的其他JavaScript库冲突 因此,我正在寻找一种在JavaScript中编写“类式”继承的方法,该方法具有以下要求: 没有外部库或与外部库冲突的内容(禁止从外部库复制和粘贴) 极简主义-我不希望支持代码比几行代码大,也不希望开发人员每次定义新类或方法时都需要大量的模板 应该允许动态扩展父对象,以便子对象看
super
类型调用function Shape(x,y) {
this.x = x;
this.y = y;
this.draw = function() {
throw new Error("Arbitrary shapes cannot be drawn");
}
}
function Square(x,y,side) {
this.x = x;
this.y = y;
this.side = side;
this.draw = function() {
gotoXY(this.x,this.y); lineTo(this.x+this.side, this.y); ...
}
}
Square.prototype = new Shape();
这解决了需求1、2和6,但id不允许超级调用(新函数覆盖父函数),构造函数链接和动态扩展父类不会为子类提供新方法
欢迎您提出任何建议。道格拉斯·克罗克福德(Douglas Crockford)在Javascript中提供了关于继承和继承的优秀文章,这应该是一个很好的起点。我建议使用以下模式,使用a从原型继承,而不是从实例继承:
function Shape(x, y) {
this.x = x;
this.y = y;
}
Shape.prototype.draw = function() {
throw new Error('Arbitrary shapes cannot be drawn');
};
function Square(x,y,side) {
Shape.call(this, x, y); // call super constructor
this.side = side;
}
// inherit from `Shape.prototype` and *not* an actual instance:
Square.prototype = clone(Shape.prototype);
// override `draw()` method
Square.prototype.draw = function() {
gotoXY(this.x,this.y); lineTo(this.x+this.side, this.y); // ...
};
方法驻留在原型中是很重要的(出于性能原因,它应该是这样的),因此您可以通过调用超类的方法
SuperClass.prototype.aMethod.call(this, arg1, arg2);
使用一些,您可以使JS看起来像一种经典的基于类的语言:
var Shape = Class.extend({
constructor : function(x, y) {
this.x = x;
this.y = y;
},
draw : function() {
throw new Error('Arbitrary shapes cannot be drawn');
}
});
var Square = Shape.extend({
constructor : function(x, y, side) {
Shape.call(this, x, y);
this.side = side
},
draw : function() {
gotoXY(this.x,this.y); lineTo(this.x+this.side, this.y); // ...
}
});
Crockford也受到了启发,但我在使用“构造函数”进行他所谓的“函数继承”方面有很好的经验。YMMV 更新:很抱歉,我忘了:您仍然需要使用
高级
方法来扩充对象,以便更好地访问超级方法。可能不太适合你
var makeShape = function (x, y) {
that = {};
that.x = x;
that.y = y;
that.draw = function() {
throw new Error("Arbitrary shapes cannot be drawn");
}
return that;
};
var makeSquare = function (x, y, side) {
that = makeShape(x, y);
that.side = side;
that.draw = function() {
gotoXY(that.x,that.y); lineTo(that.x+that.side, that.y); ...
}
return that;
};
好的,在JavaScript中复制类/实例样式系统的诀窍是,您只能在实例上使用原型继承。因此,您需要能够创建一个仅用于继承的“非实例”实例,并使一个initialiser方法与构造函数本身分离 这是我使用的最小系统(在添加装饰之前),将一个特殊的一次性值传递给构造函数,使其在不初始化对象的情况下构造对象:
Function.prototype.subclass= function() {
var c= new Function(
'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
'if (arguments[0]!==Function.prototype.subclass._FLAG && this._init) this._init.apply(this, arguments); '
);
if (this!==Object)
c.prototype= new this(Function.prototype.subclass._FLAG);
return c;
};
Function.prototype.subclass._FLAG= {};
使用newfunction()
可以避免对子类()形成不必要的闭包。如果愿意,可以用更漂亮的function(){…}
表达式替换它
用法相对干净,通常与Python样式的对象相似,只是语法稍显笨拙:
var Shape= Object.subclass();
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
Shape.prototype.draw= function() {
throw new Error("Arbitrary shapes cannot be drawn");
};
var Square= Shape.subclass();
Square.prototype._init= function(x, y, side) {
Shape.prototype._init.call(this, x, y);
this.side= side;
};
Square.prototype.draw= function() {
gotoXY(this.x, this.y);
lineTo(this.x+this.side, this.y); // ...
};
猴子修补一个内置(函数)有点可疑,但它使阅读起来很愉快,而且没有人可能想在函数上为…编写
。你可以使用Crockford在《JavaScript好的部分》一书中提出的函数模式。其思想是使用closore创建私有字段,并使用previleged函数访问这些字段。以下是满足您6项要求的解决方案之一:
var people = function (info) {
var that = {};
// private
var name = info.name;
var age = info.age;
// getter and setter
that.getName = function () {
return name;
};
that.setName = function (aName) {
name = aName;
};
that.getAge = function () {
return age;
};
that.setAge = function (anAge) {
age = anAge;
};
return that;
};
var student = function (info) {
// super
var that = people(info);
// private
var major = info.major;
that.getMajor = function () {
return major;
};
that.setMajor = function (aMajor) {
major = aMajor;
};
return that;
};
var itStudent = function (info) {
// super
var that = student(info);
var language = info.language;
that.getLanguage = function () {
return language;
};
that.setLanguage = function (aLanguage) {
language = aLanguage;
};
return that;
};
var p = person({name : "Alex", age : 24});
console.debug(p.age); // undefined
console.debug(p.getAge()); // 24
var s = student({name : "Alex", age : 24, major : "IT"});
console.debug(s.getName()); // Alex
console.debug(s.getMajor()); // IT
var i = itStudent({name : "Alex", age : 24, major : "IT", language : "js"});
console.debug(i.language); // Undefined
console.debug(i.getName()); // Alex
console.debug(i.getMajor()); // IT
console.debug(i.getLanguage()); // js
我在研究这个问题时发现的最常见的模式在。我已经更新了他们的示例,包括对超类方法的调用,并在警报消息中显示日志:
//形状-超类
函数形状(){
这个.x=0;
这个。y=0;
}
//超类方法
Shape.prototype.move=函数(x,y){
这个.x+=x;
这个.y+=y;
日志+='形状已移动。\n';
};
//矩形子类
函数矩形(){
Shape.call(this);//调用超级构造函数。
}
//子类扩展了超类
Rectangle.prototype=Object.create(Shape.prototype);
Rectangle.prototype.constructor=矩形;
//覆盖方法
Rectangle.prototype.move=函数(x,y){
Shape.prototype.move.call(this,x,y);//调用超类方法
日志+='矩形已移动。\n';
}
var log=“”;
var rect=新矩形();
log+=('rect是矩形的实例吗?'+(rect instanceof Rectangle)+'\n');//真的
log+=('rect是形状的实例吗?'+(rect instanceof Shape)+'\n');//真的
直线移动(1,1);//输出“形状移动”
警报(日志)我对Crockford的文章很熟悉,我不喜欢它们。他要么需要大量的锅炉板代码(看看在your Prototype继承链接中构造对象的第一个示例),要么需要几十行代码来表示“sugar”。或者两者兼而有之。而且,使用各种uber
和beget
的东西,结果看起来根本不像JavaScript——目的是让训练有素的JavaScript开发人员可以查看代码并立即了解发生了什么。不过,很难不使用一些锅炉板,尝试实现您想要的功能。和。。公平地说:每个受过培训的JavaScript开发人员都应该熟悉Crockford的工作:)您可以调用超级方法。如果要定义“绘制三角形”,可以通过Triangle.prototype.draw获得super draw。问题是“super”方法无法访问当前对象的字段-此
是绘制的原型,而不是此
。我可以做Square.prototype.draw.apply(这个,参数)
,但这很难,我通常不喜欢方法按名称调用它们的容器类(它们应该在任何地方都使用this
),那么这个.constructor.prototype.draw.apply(这个,参数)呢@艾尔达:woon不工作,因为覆盖原型会使构造函数不可用;它实际上也不算不那么响亮,Square是一个构造函数Square.proot