像Java一样,Object.call也应该是Javascript构造函数中的第一个调用吗?

时间:2015-06-30 07:13:03

标签: javascript oop inheritance constructor

我在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?
};

3 个答案:

答案 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)或参数化的(例如AdjoinObjectUnionObject)。

特征与abstract base classes类似。因此,您无法直接创建特征实例。相反,您需要创建一个实现特征接口的具体对象。

请注意,在这种情况下,您永远不需要调用超类构造函数,因为没有超类构造函数。所有超类(特征)都是抽象的。它们没有状态,因此不需要初始化。对象的状态封装在对象构造函数中。这很重要,因为它促进了data abstraction,这是面向对象程序的一个重要特性。

那么这个Fortress代码在JavaScript中看起来怎么样?

在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;
&#13;
&#13;

JavaScript中简单的solid面向对象编程,具有对多重继承的开箱即用支持。此外,您不必担心超类构造函数。

答案 2 :(得分:0)

具体取决于效果构造函数的作用。

当您到达构造函数中的第一行代码时,已经设置了对象及其原型链。也就是说,在设置XEffect的构造函数之前,this已经存在,其__proto__已经设置为XEffect.prototype。因此,您的对象已经是XEffect,它继承自Effect

但是,到目前为止,EffectXEffect中的所有代码都尚未运行XEffect代码将被运行,因为它是构造函数的一部分,但除非您告诉JavaScript这样做,否则 Effect将无法运行 。如果您希望明确地致电Effect,您可以。如您所述,Effect.call(this, ...)Effect.apply(this, [])可能就是您想要这样做的方式。

问题在于Effect在您告诉它运行时运行:不迟,不早。如果Effect写入对象的某些属性,然后稍后XEffect中的代码写入这些相同的属性,则XEffect获胜:这些是对象在其结束时将具有的值。如果您将此顺序 - 写入XEffect内的属性,则调用Effect,写入相同的属性,然后Effect获胜。

如果您习惯于在基于课程的#34;样式,然后首先调用Effect可能最接近你想要的。它写入对象的属性,然后XEffect(可能)覆盖它们。但如果您有某种理由希望Effect获胜,那么您不能 以这种方式执行此操作。