Javascript继承:调用超级构造函数还是使用原型链?

Jer*_* S. 78 javascript inheritance call chaining prototype-programming

最近我读到了MDC中的JavaScript调用用法

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

如下所示的一个例子,我仍然不明白.

他们为什么在这里使用继承呢?

Prod_dept.prototype = new Product();
Run Code Online (Sandbox Code Playgroud)

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

Prod_dept()
Run Code Online (Sandbox Code Playgroud)

无论如何,像这样

Product.call
Run Code Online (Sandbox Code Playgroud)

这只是出于常见的行为吗?何时使用超级构造函数调用或使用原型链更好?

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");
Run Code Online (Sandbox Code Playgroud)

谢谢你让事情更清楚

Jua*_*des 107

真正问题的答案是你需要做两件事:

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

因此,在设置继承时不应调用父的构造函数.仅在实例化从另一个继承的对象时.

Chris Morgan的答案几乎完成,遗漏了一个小细节(构造函数属性).我建议一种设置继承的方法.

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
Run Code Online (Sandbox Code Playgroud)

在创建类时,请查看我的博客文章,了解更多语法糖.http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

从Ext-JS和http://www.uselesspickles.com/class_library/复制的技术以及来自/sf/users/97811801/的评论

  • 在EcmaScript5 +(所有现代浏览器)中,如果你将它定义为`Object.defineProperty(sub.protoype,'constructor',{enumerable:false,value:sub}),你可以使它不可枚举;`这样你' ll得到与javascript创建函数的新实例时完全相同的"行为"(构造函数自动设置为enumerable = false) (6认同)
  • 难道你不能简单地将extend方法简化为两行吗?即:sub.prototype = Object.create(base.prototype); sub.prototype.constructor = sub; (2认同)

Chr*_*gan 30

这样做的理想方法是这样做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;
Run Code Online (Sandbox Code Playgroud)

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

如果您查看Google Closure Library之类的内容,您会看到他们是如何做到这一点的.


小智 6

如果您已在JavaScript中完成面向对象编程,您将知道可以按如下方式创建类:

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

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

Person.prototype = {
    /** wake person up */
    wake_up: function() {
        alert('I am awake');
    },

    /** retrieve person's age */
    get_age: function() {
        return this.age;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们已经定义了我们的类Person.如果我们想要定义另一个名为Manager的类,它从Person继承了一些属性,该怎么办?在定义Manager类时,没有必要再次重新定义所有这些属性,我们可以将其设置为继承Person类.JavaScript没有内置继承,但我们可以使用一种技术来实现继承,如下所示:

Inheritance_Manager = {};//我们创建一个继承管理器类(名称是任意的)

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

subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

现在我们已经实现了继承,我们可以开始使用它来扩展我们的类.在这种情况下,我们将把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.');
}
Run Code Online (Sandbox Code Playgroud)

我们让它继承形式的人

Inheritance_Manager.extend(Manager, Person);
Run Code Online (Sandbox Code Playgroud)

如果你注意到,我们刚刚调用了Inheritance_Manager类的extend方法,并在我们的例子中传递了subClass Manager,然后传递了baseClass Person.请注意,订单在这里非常重要.如果你交换它们,继承将无法按预期工作.另请注意,在实际定义subClass之前,需要指定此继承.现在让我们定义我们的子类:

我们可以添加更多方法,如下所示.我们的Manager类将始终具有Person类中定义的方法和属性,因为它继承自它.

Manager.prototype.lead = function(){
   alert('I am a good leader');
}
Run Code Online (Sandbox Code Playgroud)

现在要测试它,让我们创建两个对象,一个来自Person类,另一个来自继承的类Manager:

var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');
Run Code Online (Sandbox Code Playgroud)

欢迎在以下网址获取完整的代码和更多评论:http: //www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx