Javascript的inheritance:调用超级构造函数或使用原型链?

最近我读了关于MDC中JavaScript调用的使用

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

下面显示的一个例子,我仍然不明白。

为什么他们这样使用inheritance

Prod_dept.prototype = new Product(); 

这是必要的吗? 因为有一个对超级构造函数的调用

 Prod_dept() 

无论如何,这样的

 Product.call 

这是不是常见的行为? 什么时候使用超级构造函数调用或使用原型链更好?

 function Product(name, value){ this.name = name; if(value >= 1000) this.value = 999; else this.value = value; } function Prod_dept(name, value, dept){ this.dept = dept; Product.call(this, name, value); } Prod_dept.prototype = new Product(); // since 5 is less than 1000, value is set cheese = new Prod_dept("feta", 5, "food"); // since 5000 is above 1000, value will be 999 car = new Prod_dept("honda", 5000, "auto"); 

感谢您使事情更清楚

真正的问题的答案是,你需要做的两个:

  • 将原型设置为父类的一个实例,初始化原型链(inheritance),只做一次(因为原型对象是共享的)。
  • 调用父对象的构造函数初始化对象本身,这是通过每个实例化来完成的(每次构build它时都可以传递不同的参数)。

因此,在设置inheritance时,不应该调用父项的构造函数。 仅在实例化从另一个inheritance的对象时。

Chris Morgan的答案几乎完整,缺less一个小细节(构造函数属性)。 让我build议一种设置inheritance的方法。

 function extend(base, sub) { // Avoid instantiating the base class just to setup inheritance // Also, do a recursive merge of two prototypes, so we don't overwrite // the existing prototype, but still maintain the inheritance chain // Thanks to @ccnokes var origProto = sub.prototype; sub.prototype = Object.create(base.prototype); for (var key in origProto) { sub.prototype[key] = origProto[key]; } // The constructor property was set wrong, let's fix it Object.defineProperty(sub.prototype, 'constructor', { enumerable: false, value: sub }); } // Let's try this function Animal(name) { this.name = name; } Animal.prototype = { sayMyName: function() { console.log(this.getWordsToSay() + " " + this.name); }, getWordsToSay: function() { // Abstract } } function Dog(name) { // Call the parent's constructor Animal.call(this, name); } Dog.prototype = { getWordsToSay: function(){ return "Ruff Ruff"; } } // Setup the prototype chain the right way extend(Animal, Dog); // Here is where the Dog (and Animal) constructors are called var dog = new Dog("Lassie"); dog.sayMyName(); // Outputs Ruff Ruff Lassie console.log(dog instanceof Animal); // true console.log(dog.constructor); // Dog 

创build类时,请参阅我的博客文章,以获取更多的语法糖。 http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

从Ext-JS和http://www.uselesspickles.com/class_library/复制的技术以及从https://stackoverflow.com/users/1397311/ccnokes发表的评论;

理想的做法是不要Prod_dept.prototype = new Product(); ,因为这调用了Product构造函数。 所以理想的方法是除了构造函数之外克隆它,像这样:

 function Product(...) { ... } var tmp = function(){}; tmp.prototype = Product.prototype; function Prod_dept(...) { Product.call(this, ...); } Prod_dept.prototype = new tmp(); Prod_dept.prototype.constructor = Prod_dept; 

然后超级构造函数在构造时被调用,这是你想要的,因为那么你也可以传递参数。

如果你看看Google Closure Library这样的东西,你会看到他们是这样做的。

如果你在JavaScript中完成了面向对象编程,你将会知道你可以创build一个类,如下所示:

 Person = function(id, name, age){ this.id = id; this.name = name; this.age = age; alert('A new person has been accepted'); } 

到目前为止,我们的class级只有两个属性,我们会给它一些方法。 一个干净的方法是使用它的“原型”对象。 从JavaScript 1.1开始,原型对象是在JavaScript中引入的。 这是一个内置的对象,它简化了向对象的所有实例添加自定义属性和方法的过程。 让我们使用它的“原型”对象为我们的类添加2个方法,如下所示:

 Person.prototype = { /** wake person up */ wake_up: function() { alert('I am awake'); }, /** retrieve person's age */ get_age: function() { return this.age; } } 

现在我们定义了我们的Person类。 如果我们想要定义另一个叫做Manager的类,它从Personinheritance了一些属性呢? 当我们定义我们的Manager类时,没有必要重新定义所有这些属性,我们可以将其设置为inheritancePerson类。 JavaScript没有内置inheritance,但我们可以使用一种技术来实现inheritance,如下所示:

Inheritance_Manager = {}; //我们创build一个inheritancepipe理器类(名字是任意的)

现在让我们inheritance一个名为extend的方法,它接受baseClass和subClassas参数。 在extend方法中,我们将创build一个名为inheritance函数inheritance(){}的内部类。 我们使用这个内部类的原因是为了避免baseClass和subClass原型之间的混淆。 接下来,我们将inheritance类的原型指向baseClass原型,如下代码所示:inheritance.prototype = baseClass。 原型; 然后,我们将inheritance原型复制到子类原型中,如下所示:subClass.prototype = new inheritance(); 接下来就是为我们的子类指定构造函数,如下所示:subClass.prototype.constructor = subClass; 一旦完成了我们的子类原型,我们可以指定接下来的两行代码来设置一些基类指针。

 subClass.baseConstructor = baseClass; subClass.superClass = baseClass.prototype; 

以下是我们扩展函数的完整代码:

 Inheritance_Manager.extend = function(subClass, baseClass) { function inheritance() { } inheritance.prototype = baseClass.prototype; subClass.prototype = new inheritance(); subClass.prototype.constructor = subClass; subClass.baseConstructor = baseClass; subClass.superClass = baseClass.prototype; } 

现在我们已经实现了我们的inheritance,我们可以开始使用它来扩展我们的类。 在这种情况下,我们将把Person类扩展为Manager类,如下所示:

我们定义Manager类

 Manager = function(id, name, age, salary) { Person.baseConstructor.call(this, id, name, age); this.salary = salary; alert('A manager has been registered.'); } 

我们使它inheritanceformsPerson

 Inheritance_Manager.extend(Manager, Person); 

如果你注意到了,我们刚刚调用了Inheritance_Manager类的extends方法,并在我们的例子中传递了subClass Manager,然后传入了baseClass Person。 请注意,这里的顺序非常重要。 如果你交换了它们,inheritance将不会像你打算的那样工作。 另外请注意,在实际定义我们的子类之前,您将需要指定这个inheritance。 现在让我们定义我们的子类:

我们可以添加更多的方法,如下所示。 我们的Manager类将始终拥有Person类中定义的方法和属性,因为它inheritance了它。

 Manager.prototype.lead = function(){ alert('I am a good leader'); } 

现在要testing它,让我们创build两个对象,一个来自Person类,另一个来自inheritance的类Manager:

 var p = new Person(1, 'Joe Tester', 26); var pm = new Manager(1, 'Joe Tester', 26, '20.000'); 

随意获得完整的代码和更多评论: http : //www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx