4

我不明白为什么每个人都在使用Boy.prototype = new Human;模拟继承。看,我们要的是A的函数对吗?我们可以在不实例化新 A 的情况下做到这一点(事实上,实例化新 A 确实会给我们带来不希望的结果,因为我们实际上正在运行我们不想要的实例化函数)

那么这不是更好的解决方案吗?

for (var prop_name in Human.prototype) {
Object.defineProperty(
          Boy.prototype,
          prop_name,
          Object.getOwnPropertyDescriptor(Human.prototype,prop_name)
        );
}

假设我们是如此特殊并且不仅想要 Human.prototype 中的可枚举属性,我们仍然可以通过在原型链上使用Object.getOwnPropertyNames和调用它来实现它,而原型链反过来又可以通过Object.getPrototypeOf.

Boy.prototype = new Human;那么当我们有更好的选择时,模拟继承到底有什么好处呢?

4

2 回答 2

4
  • 它正确设置了原型链,这意味着instanceof它将isPrototypeOf()起作用
  • 它不那么冗长

当您只是使用它来创建原型对象时,有一些简单但丑陋的解决方法可以防止构造函数执行其通常的初始化。例如,您可以检查参数:

function Boy(name) {
    if (arguments.length == 1) {
        this.name = name;
        // Do other initialization
    }
}

...或将初始化移动到必须显式调用的单独方法中:

function Boy(name) {}

Boy.prototype.init = function(name) {
    this.name = name;
    // Do other initialization
}

这有一个明显的缺点,即要求您记住init()在创建新的Boy.

在 ECMAScript 5 环境中(例如大多数浏览器的当前版本),更好的选择可能是 ECMAScript 5's Object.create(),它允许您创建一个直接从另一个对象继承的对象并为您设置原型链。这可以在非 ES5 环境中模拟(但只是近似:参见Object.defineProperty in ES5? ):

if (!Object.create) {
    Object.create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }; 
}
于 2011-05-13T11:34:49.703 回答
4

更好的选择是创建一个中间件来保存原型。

function extend(clazz, superclass) {
    var intermediate = function() {};
    intermediate.prototype = superclass.prototype;
    clazz.prototype = new intermediate();
    // Following line is optional, but useful
    clazz.prototype.constructor = clazz;
}

这避免了不必要的复制,但仍然意味着您不需要实例化将在其构造函数中工作的对象。它还设置了原型链,以便您可以使用 instanceof。它也不会导致某些继承反模式可能造成的超类原型污染。

为了完整起见,您的子类应在其构造函数中调用超类构造函数,您可以使用Superclass.call(this);.

编辑:从 ES5 开始,您可以将调用替换extend

Subclass.prototype = Object.create(Superclass.prototype);

它做同样的事情。

于 2011-05-13T11:35:32.557 回答