4

我正在学习javascript并感到困惑。这里有一个例子,例子如下: -

// define the Person Class
function Person() {}

Person.prototype.walk = function(){
  alert ('I am walking!');
};
Person.prototype.sayHello = function(){
  alert ('hello');
};

// define the Student class
function Student() {
  // Call the parent constructor
  Person.call(this);// <---- Confusion
}

// inherit Person
Student.prototype = new Person(); //<---- Confusion

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;

// replace the sayHello method
Student.prototype.sayHello = function(){
  alert('hi, I am a student');
}

// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
  alert('goodBye');
}

var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();

// check inheritance
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true

现在,我<----对这两行感到困惑( )。当我说Person.call(this);时,这只是说明继承 Person 类的属性......对吗?

那么这是在做什么呢?

  // inherit Person
    Student.prototype = new Person(); //<---- Confusion

据我所知,.prototype还继承了所有属性?

4

1 回答 1

4

为了解释它,首先让我们记住构造函数在 JavaScript 中是如何工作的:

function Guide(a) {
    this.a = a;
}
Guide.prototype.q = "Life, the Universe, and Everything";

var g = new Guide(42);
console.log(g.q); // "Life, the Universe, and Everything"
console.log(g.a); // 42

当我们这样做new Guide(42)时,操作员会创建一个新对象并使用该属性new为其分配一个原型。Guide.prototype然后new调用Guide,将该新对象作为this. Guide用于this向新对象添加不在其原型上的属性。然后new表达式完成,其结果是它创建的新对象。

当我们查看 时g.q,由于g对象没有自己的名为 的属性q,JavaScript 引擎会查看g的原型,它(再次)在创建时分配。该原型有一个q属性,因此引擎使用它的值。

相反,当我们查看 时g.ag对象有自己的属性,称为a,因此直接使用该值。

有了这个基础,让我们看看Studentand Parent

function Student() {
  // Call the parent constructor
  Person.call(this);// <---- Confusion
}

当我们调用 时new Student(),在对 的调用中Studentthis是(再次)由运算符创建的新对象new,它具有Student.prototype作为其底层原型。但是该Parent函数没有机会对这个新对象做任何事情。所以这条线的作用是给Parent它一个机会对它不能通过原型做的新对象做它需要做的任何事情,就像我们Guide之前分配给的函数一样this.a。用技术术语来说,Parent.call(this);调用Parent函数,确保this在调用中Parent传入的是传入的值call(在这种情况下,this就是调用的Student —— 例如,新对象)。如果您熟悉基于类的语言,这就像做super();(那是 Java,但你明白了)在派生构造函数中:它为基构造函数提供了初始化对象的机会。

// inherit Person
Student.prototype = new Person(); //<---- Confusion

由于Student应该继承自Person,因此该代码所做的是创建原型,该原型将分配给通过new Student. 它创建的对象是一个Person对象。

FWIW,该代码并没有完全正确地实现构造链(它调用的Person频率超过了它应该[在创建Student.prototype 调用时Student],并且未能设置constructor)。

更正确的创建Student属性的方法prototype,如果我们Parent要从内部调用Student,看起来像这样:

function derive(child, parent) {
    function ctor() { this.constructor = child; }
    ctor.prototype = parent.prototype;
    child.prototype = new ctor();
}

derive(Student, Parent);

这样,我们得到一个基于Parent.prototype但不调用的原型Parent。这是一个非此即彼的方法:要么调用Parentcreate Student.prototype要么调用Parentinside Student,但不要两者都做。使用构造函数构建层次结构时,通常希望从子构造函数调用父级,而不是在创建子原型时。当使用直接对象继承(没有构造函数)时,当然你用另一种方式。

如果您对 JavaScript 中的继承感兴趣,我已经编写了一个名为Lineage您可能想要查看的帮助脚本,特别是即使您不使用Lineage,其 wiki 页面上的讨论可能有助于理解继承层次结构。

于 2013-10-28T09:34:26.070 回答