我在Javascript中编写了一些OOP代码,继承的设计提出了一个问题。像Java一样,超级构造函数调用应该是Javascript中子类构造函数的第一行吗?
var Effect = function (container, height, width) {};
var XEffect = function (container, height, width, resource) {
Effect.call(container, height, width ); // really necessary to be first line?
};
答案 0 :(得分:0)
引用Douglas Crockford:" JavaScript有一个无类对象系统,其中对象直接从其他对象继承属性。这真的很强大,但对经过专业训练的程序员来说,这是不熟悉的。如果您尝试将经典设计模式直接应用于JavaScript,您将会感到沮丧。但是如果你学会使用JavaScript的原型性质,那么你的努力将得到回报。"
查看Object.prototype了解如何在JavaScript中处理继承。
在您的代码段中,Effect.call(container, height, width);
只会评估为undefined
,除非您事先明确定义它。要调用Effect
函数的功能,只需将其调用为Effect()
。
答案 1 :(得分:0)
就个人而言,我不喜欢用Java和C ++等语言实现的面向对象系统。我最喜欢的面向对象系统是由Fortress programming language实现的。例如,请考虑以下Fortress代码,这些代码取自Why Object-Oriented Languages Need Tail Calls(a.k.a.The Great Quux)撰写的着名博客文章Guy Steele:
trait IntSet
getter isEmpty(): Boolean = false
adjoin(x: ZZ): IntSet = AdjoinObject(self, x)
union(other: IntSet): IntSet = UnionObject(self, other)
contains(y: ZZ): Boolean
end
object AdjoinObject(s: IntSet, x: ZZ) extends IntSet
contains(y: ZZ) = (y = x) OR: s.contains(y)
end
object UnionObject(s1: IntSet, s2: IntSet) extends IntSet
isEmpty: Boolean = s1.isEmpty AND s2.isEmpty
contains(y: ZZ) = s1.contains(y) OR: s2.contains(y)
end
object EmptyObject extends IntSet
isEmpty: Boolean = true
contains(y: ZZ) = false
end
Fortress中的对象定义引入了对象构造函数。这些构造函数可以是nullary(例如EmptyObject
)或参数化的(例如AdjoinObject
和UnionObject
)。
特征与abstract base classes类似。因此,您无法直接创建特征实例。相反,您需要创建一个实现特征接口的具体对象。
请注意,在这种情况下,您永远不需要调用超类构造函数,因为没有超类构造函数。所有超类(特征)都是抽象的。它们没有状态,因此不需要初始化。对象的状态封装在对象构造函数中。这很重要,因为它促进了data abstraction,这是面向对象程序的一个重要特性。
在JavaScript中编写面向对象代码的方法有很多种。但是,我最喜欢以下Fortress风格的面向对象的JavaScript代码:
var intset = EmptyObject.adjoin(2).adjoin(3);
alert(intset.contains(2)); // true
alert(intset.contains(3)); // true
alert(intset.contains(5)); // false

<script>
var IntSet = {
isEmpty: false,
adjoin: function (x) { return AdjoinObject(this, x); },
union: function (other) { return UnionObject(this, other); }
};
function AdjoinObject(s, x) {
return extend(IntSet, {
contains: function (y) { return y === x || s.contains(y); }
});
}
function UnionObject(s1, s2) {
return extend(IntSet, {
isEmpty: s1.isEmpty && s2.isEmpty,
contains: function contains(y) { return s1.contains(y) ||
s2.contains(y); }
});
}
var EmptyObject = extend(IntSet, {
isEmpty: true,
contains: function (y) { return false; }
});
function extend() {
var result = {}, i = 0, length = arguments.length, object;
while (i < length && (object = arguments[i++]))
for (var key in object) if (object.hasOwnProperty(key))
result[key] = object[key];
return result;
}
</script>
&#13;
JavaScript中简单的solid面向对象编程,具有对多重继承的开箱即用支持。此外,您不必担心超类构造函数。
答案 2 :(得分:0)
具体取决于效果构造函数的作用。
当您到达构造函数中的第一行代码时,已经设置了对象及其原型链。也就是说,在设置XEffect的构造函数之前,this
已经存在,其__proto__
已经设置为XEffect.prototype
。因此,您的对象已经是XEffect
,它继承自Effect
。
但是,到目前为止,Effect
或XEffect
中的所有代码都尚未运行。 XEffect
代码将被运行,因为它是构造函数的一部分,但除非您告诉JavaScript这样做,否则 Effect
将无法运行 。如果您希望明确地致电Effect
,您可以。如您所述,Effect.call(this, ...)
或Effect.apply(this, [])
可能就是您想要这样做的方式。
问题在于Effect
在您告诉它运行时运行:不迟,不早。如果Effect
写入对象的某些属性,然后稍后XEffect
中的代码写入这些相同的属性,则XEffect
获胜:这些是对象在其结束时将具有的值。如果您将此顺序 - 写入XEffect
内的属性,则调用Effect
,写入相同的属性,然后Effect
获胜。
如果您习惯于在基于课程的#34;样式,然后首先调用Effect
可能最接近你想要的。它写入对象的属性,然后XEffect
(可能)覆盖它们。但如果您有某种理由希望Effect
获胜,那么您不能 以这种方式执行此操作。