来自C ++ / Objective-C背景,我试图学习如何正确有效地再现Javascript中的继承和封装模式。我已经做了大量的阅读(Crockford等),虽然有很多例子可以说明如何实现其中一个,但我在努力解决如何在不引入重大负面影响的情况下将它们结合起来。
目前,我有这段代码:
var BaseClass = (function() {
function doThing() {
console.log("[%s] Base-class's 'doThing'", this.name);
}
function reportThing() {
console.log("[%s] Base-class's 'reportThing'", this.name);
}
return function(name) {
var self = Object.create({});
self.name = name;
self.doThing = doThing;
self.reportThing = reportThing;
return self;
}
}());
var SubClass = (function(base) {
function extraThing() {
console.log("[%s] Sub-class's 'extraThing'", this.name);
}
function doThing() {
console.log("[%s] Sub-class's replacement 'doThing'", this.name);
}
return function(name) {
// Create an instance of the base object, passing our 'name' to it.
var self = Object.create(base(name));
// We need to bind the new method to replace the old
self.doThing = doThing;
self.extraThing = extraThing;
return self;
}
}(BaseClass));
它主要是做我想要的:
// Create an instance of the base class and call it's two methods
var base = BaseClass("Bert");
base.doThing(); // "[Bert] Base-class's 'doThing'"
base.reportThing(); // "[Bert] Base-class's 'reportThing'"
var other = BaseClass("Fred");
// Create an instance of the sub-class and call it's three methods (two from the base, one of it's own)
var sub = SubClass("Alfred");
sub.doThing(); // "[Alfred] Sub-class's replacement 'doThing'"
sub.extraThing(); // "[Alfred] Sub-class's 'extraThing'"
sub.reportThing(); // "[Alfred] Base-class's 'reportThing'"
但是,至少有两个问题:
我正在替换像这样的函数的原型实现:
Object.getPrototypeOf(oneInstance).reportThing = function() { ... }
otherInstance.reportThing() // Original version is still called
这可能不是一个重大问题,但它让我怀疑我的理解。
私有变量是我想要有效实现的东西。变量隐藏的模块模式在这里没有帮助,因为它导致函数定义存在于每个对象。我可能错过了一种组合模式的方法,那么有没有一种方法可以在不重复功能的情况下实现私有变量?
答案 0 :(得分:1)
简单的配方如下:
function BaseClass(someParams)
{
// Setup the public properties, e.g.
this.name = someParams.name;
}
BaseClass.prototype.someMethod = function(){
// Do something with the public properties
}
现在继承以这种方式发生
function SubClass(someParams)
{
// Reuse the base class constructor
BaseClass.call(this, someParams);
// Keep initializing stuff that wasn't initialized by the base class
this.anotherProperty= someParams.anotherProperty;
}
// Copy the prototype from the BaseClass
SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;
// Start extending or overriding stuff
SubClass.prototype.someMethod = function(){
// In case you still wanna have the side effects of the original method
// This is opt-in code so it depends on your scenario.
BaseClass.prototype.someMethod.apply(this, arguments);
// Override the method here
}
取自: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
P.S。所有旧浏览器可能都不支持Object.create,但不要担心,此链接中有一个polyfill。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
答案 1 :(得分:1)
这通常是我在JavaScript中处理继承和封装的方式。 defclass
函数用于创建一个不从任何其他类继承的新类,extend
函数用于创建一个扩展另一个类的新类:
var base = new BaseClass("Bert");
base.doThing(); // "Bert BaseClass doThing"
base.reportThing(); // "Bert BaseClass reportThing"
var sub = new SubClass("Alfred");
sub.doThing(); // "Alfred SubClass replacement doThing"
sub.extraThing(); // "Alfred SubClass extraThing"
sub.reportThing(); // "Alfred BaseClass reportThing"
var other = new SubClass("Fred");
SubClass.prototype.reportThing = function () {
console.log(this.name + " SubClass replacement reportThing");
};
other.reportThing(); // Fred SubClass replacement reportThing
<script>
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function extend(constructor, keys) {
var prototype = Object.create(constructor.prototype);
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
var BaseClass = defclass({
constructor: function (name) {
this.name = name;
},
doThing: function () {
console.log(this.name + " BaseClass doThing");
},
reportThing: function () {
console.log(this.name + " BaseClass reportThing");
}
});
var SubClass = extend(BaseClass, {
constructor: function (name) {
BaseClass.call(this, name);
},
doThing: function () {
console.log(this.name + " SubClass replacement doThing");
},
extraThing: function () {
console.log(this.name + " SubClass extraThing");
}
});
</script>
阅读以下答案,了解继承如何在JavaScript中运行:
What are the downsides of defining functions on prototype this way?
它解释了原型和构造函数之间的区别。此外,它还展示了原型和类是如何同构的以及如何在JavaScript中创建“类”。
希望有所帮助。
答案 2 :(得分:0)
如果要保留原型链,则必须覆盖并使用.prototype: 例: 主类:
function BaseClass(){
}
BaseClass.prototype.doThing = function(){...}
亚类:
function SubClass(){
}
SubClass.prototype= new BaseClass();
SubClass.prototype.extraThing = function(){};
现在,无论何时更改extraThing或doThing,它都会在任何地方被替换。 name属性可以作为公共变量访问(它不是静态的)。
如果你想要它是静态的,你必须把它放在原型中。
如果你想把它变成私人的,你就可以使它在本地运作:
function BaseClass(nameParam){
var name = nameParam;
}
要创建对象,只需调用函数:
var testObj = new BaseClass("test");
testObj.doThing();
如果要将私有变量与可重写函数组合在一起,您可能会找到answer here。但是如果你能够重写有权访问私有变量的函数,它就不再是私有变量了。