为什么在扩展对象时要使用Object.create来创建原型?

6

我有Java编程背景,最近一直在尝试使用JavaScript继承。我开始编写一些对象,并阅读了一些示例后,找到了适合我的代码风格。

以下是我的代码:

var Class = function() {};
Class.extend = function(p_constructor) {
    var Superclass = this;
    // the following line confuses me
    p_constructor.prototype = Object.create(Superclass.prototype);
    p_constructor.prototype.constructor = p_constructor;
    p_constructor.extend = Superclass.extend;
    return p_constructor;
};

所以现在,当我想定义一个扩展Class的对象时,我只需要输入:
var Person = Class.extend(function(p_firstName, p_lastName) {
    this.firstName = p_firstName;
    this.lastName = p_lastName;
});

以下内容将成立:

var p = new Person('john', 'smith');
p instanceof Person; // true
p instanceof Class; // true

我只是不太明白在Class.extend函数中以下行之间的有效区别:

/* 1) */ p_constructor.prototype = new Superclass();
/* 2) */ p_constructor.prototype = Superclass.prototype;
/* 3) */ p_constructor.prototype = Object.create(Superclass.prototype);
/* 4) */ p_constructor.prototype = Object.create(Superclass);

我知道如果一些超级构造函数抛出错误,使用第一行并不是很明智,但第2、3和4行之间的区别是什么?

1个回答

7
回答自己的问题:

为了回答这个问题:

/* 1) */ p_constructor.prototype = new Superclass();

就像我之前说的那样,使用这种方法并不明智,因为它实际上会创建整个超类对象。原型继承的重点在于共享原型对象,以避免重复的函数(有时也包括对象)定义。

/* 2) */ p_constructor.prototype = Superclass.prototype;

如果您可以确保构造函数的原型不会被第三方修改,那么这将是可以接受的。假设某人使用您的类并希望向其原型添加其他功能。更改p_constructor的原型将直接影响超类的原型,并可能在超类的行为中引入错误。因此,下一行代码将代替:

/* 3) */ p_constructor.prototype = Object.create(Superclass.prototype);

这将创建一个新的对象(实例),其原型(实际上是__proto__变量)被设置为超类的原型。对p_constructor.prototype的任何更改现在都不会影响超类的行为。

/* 4) */ p_constructor.prototype = Object.create(Superclass);

上述代码是可以正常运行的,但并没有实际意义,因为它仅将原型设置为超类的构造函数。

另外,下面这行代码对我来说也很困惑:

p_constructor.prototype.constructor = p_constructor;

在p_constructor的行为上并没有任何区别,它只是使构造函数的引用在实例化对象中可用。一个巧妙的技巧是也将超类添加到原型中:

p_constructor.prototype.superclass = Superclass;

然后以下代码将会起作用:

function Constructor(p_params) {
    // call constructor of the superclass
    this.superclass(p_params);
}

很棒,你在这里回答了你的问题。在你的最后一个代码块中,请考虑在调用超类构造函数时上下文;我认为你想做的是 this.superclass.apply(this, arguments); - 然后,如果超级构造函数执行了 this.sumthin = ...,该值将附加到子类实例。 - dalgard
超类的值本来就会附加到子类的当前实例上,因为它是从“this”调用的,所以它的上下文实际上将是子类的同一个新实例。 - jeremija
哦,你说得对。我认为我经常看到这种访问超类的模式:subclass.prototype.super = superclass.prototype(可能还加上subclass.super = superclass)。也许你会有兴趣看看我的解决方案(请参见评论中的示例)。 - dalgard
非常好的解释方式。 - Jeremy Chone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接