继承原型

3
拥有父类 class:
function Animal() {
  // do something
}

Animal.prototype.walk = function() {
  alert('I am walking.');
};

和子类class

function Lion() {
  // do something
}

如果我想让 Lion 继承 Animal 的原型,常见的做法是这样的:
Lion.prototype = new Animal();

// Set the constructor to Lion, because now it points to Animal
Lion.prototype.constructor = Lion;

这是否与此有任何不同(作为结果而非性能)?
$.extend(Lion.prototype, Animal.prototype);

对于非jQuery开发人员:$.extend会逐个从Animal复制所有原型方法和属性到Lion。
我不是JavaScript继承方面的大牛。我通常在前端开发中使用MVC框架,一切都很顺利,但现在我也想了解原型继承如何工作。
注意! 我阅读了许多关于这个主题的文章,我知道有许多"插件"实现了Class功能。这不是我需要的。请回答问题,而不仅仅是链接相关文章(除非那篇文章能回答我的问题)。
谢谢!

1
在jQuery中,$.extend实现了一种名为mixin的JavaScript模式,主要用于克隆对象并添加新的属性和方法;其中修改原始对象不会影响新对象。可以将其视为Harmony中的新Object.create,但有一个区别- newObj = Object.create(oldObj)使oldObj成为newObj__proto__ - Om Shankar
5个回答

3

两个原型之间复制属性是不会使它们“连接”在一起的,即:

$.extend(Lion.prototype, Animal.prototype);

// add one more method to the prototype
Animal.prototype.test = function() {
  alert('test');
};

var x = new Lion();
x.test(); // TypeError: Object <Lion> has no method 'test'

如果您使用new Animal()作为Lion的原型,就不会出现这种情况,这在这里有所展示。


3

我最近写了一篇文章,解释了为什么原型继承很重要。它有点长但非常值得一读。

直接回答你的问题,是的Lion.prototype = new Animal$.extend(Lion.prototype, Animal.prototype)不同,因为:

  1. 在第一个情况下,您正在使用委派
  2. 在第二种情况下,您正在使用连接

我给你链接的帖子解释了真正的原型继承(即使用原型模式进行原型继承)。

这就是如果您在JavaScript中使用真正的原型继承时您的程序会看起来如何:

var animal = {
    create: function () {
        return this.extend();
    },
    walk: function () {
        alert("I am walking.");
    }
};

var lion = animal.extend({
    create: function () {
        return animal.create.call(this);
    }
});

这里的extend函数是我给你链接的文章中的那个。

与你使用构造函数模式进行原型继承的代码相比,可以看出它们之间的差异。

这里有一个演示真正的原型继承的fiddle:http://jsfiddle.net/6x4BU/


2
不错的文章(虽然我不完全同意你的观点)。至少,在Object.prototype上放置可枚举属性是不好的做法!而那些clones数组将阻止垃圾回收。 - Bergi
@Bergi - 这是一个很好的观察。我会尝试找到解决方案。谢谢。 - Aadit M Shah
@Bergi - 你介意在我的博客上发表同样的评论,这样其他人也可以阅读它吗? - Aadit M Shah

1
不,它们不同。
第一个例子中,Lion原型成为Animal的实例。该实例继承自Animal原型,并将链接回该原型。如果修改Animal原型,则实例将受到影响。 第二个例子只是从Animal原型中复制属性,而Lion不会链接到实际的Animal原型本身。

0

正如其他人已经提到的,是的,它们之间有区别。

在第一个示例中,对Animal原型的更改将改变Lion实例的行为。

在第二个示例中,Lion在$.extend()调用时继承了Animal的功能。

然而,第一种方式可能会产生一些有害的副作用,特别是:

  • 对 Animal 进行更改可能会影响 Lion 及其后代。这种动态更改委托原型的属性通常被认为是一件好事,但当它应用于多级继承时,经常会引起问题。

  • 第一种方法不允许从 Animal 进行选择性继承。要么全部继承,要么不继承。你想要香蕉,但你得到的是大猩猩、香蕉和整个丛林。(这个著名的问题被称为大猩猩/香蕉问题)。后一种方法允许你从 Animal 中进行选择性继承:$.extend(Lion.prototype, { run: Animal.prototype.run, sleep: Animal.prototype.sleep });

  • instanceof 在连接时会出现问题——但 instanceof 在任何情况下都不可靠。如果重新分配 Animal 原型,它将会出错。如果你尝试在执行上下文之间使用它,它也会出错。换句话说,无论你选择哪种继承方法,都不要使用 instanceof

构造函数根本不是必需的。你可以只创建一个名为 animal 的对象字面量并执行以下操作:

lion = Object.create(animal);

更好的做法是将其放在一个工厂函数中:
function lion(options) {
  return $.extend(Object.create(lion), options);
}

工厂函数比构造函数有很多优点,这里我不再赘述。您可以在我的回答中查看更多细节:构造函数 vs 工厂函数


0

这是纯JavaScript中的基本原型继承

// base class declaration
function Animal (arg) {
    this.arg = arg;
}

// just a method in the Animal class, it can be whatever you want
Animal.prototype.roar = function () {
    alert(this.arg);
};

// object that inherits the base class
function Lion (arg) {
    // the call to the super constructor
    Animal.call(this, arg);
}

// the prototypical inheritance
Lion.prototype = Object.create(Animal.prototype);
// the constructor needs to be set so that it does not show as the base constructor
// otherwise the constructor for all instances of Lion will show as Animal
Lion.prototype.constructor = Lion;

var symba = new Lion('the lion is roaring');

symba.roar();

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