我正在重写一个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不允许超级调用(新函数覆盖父函数),构造函数链接和动态扩展父类不会为子类提供新方法。
欢迎任何建议。
答案 0 :(得分:5)
我建议使用以下模式,使用clone
function继承原型而不是实例:
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);
使用一些syntactic sugar,您可以使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); // ...
}
});
答案 1 :(得分:4)
答案 2 :(得分:1)
好的,在JavaScript中复制类/实例样式系统的技巧是你只能在实例上使用原型继承。因此,您需要能够创建一个仅用于继承的“非实例”实例,并将初始化方法与构造函数本身分开。
这是我使用的最小系统(在添加装饰之前),将一个特殊的一次性值传递给构造函数,使其构造一个对象而不进行初始化:
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= {};
使用new Function()
是避免在子类()上形成不必要的闭包的一种方法。如果您愿意,可以用更漂亮的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); // ...
};
对内置函数(函数)进行猴子修补有点疑问,但是阅读起来很愉快,没有人可能想要for...in
函数。
答案 3 :(得分:1)
我在研究这个问题时发现的最常见的模式在Mozilla Developer Network中有所描述。我已经更新了他们的示例以包含对超类方法的调用并在警报消息中显示日志:
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
log += 'Shape moved.\n';
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
// Override method
Rectangle.prototype.move = function(x, y) {
Shape.prototype.move.call(this, x, y); // call superclass method
log += 'Rectangle moved.\n';
}
var log = "";
var rect = new Rectangle();
log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);
答案 4 :(得分:0)
你可以使用Crockford在他的书“JavaScript the good parts”中提出的功能模式。 Idea是使用closore来创建私有字段,并使用普通函数来访问这些字段。以下是满足您的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
答案 5 :(得分:-1)
同样受Crockford启发,但我使用“构造函数”对他所谓的“功能继承”有很好的体验。 YMMV。
更新:抱歉,我忘记了:您仍需要使用superior
方法扩充Object,以便更好地访问超级方法。不太可能适合你。
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;
};