理解 JavaScript 中的类 (ES5) 和原型继承
function Person(name, age) {
this.name = name;
this.age = age;
}
const me = new Person('Joe', 20);
console.log(me); // {name: 'Joe', age: 20}
简而言之,上面的代码片段创建了一个可以拥有多个实例的 Person 类。按照惯例,JavaScript 中的函数式类名以大写字母开头。
让我们深入探讨一下这里发生了什么?如何将普通函数用于类?🤔🤔
这个Person函数和其他函数一样,直接调用时会直接返回,undefined因为我们没有显式地从中返回任何值。但真正的奥妙在于new第 1 行的关键字var me = new Person('Joe', 20)。让我们来理解这个奥妙——当我们使用关键字初始化一个函数
时,会发生以下几个步骤:new
{}创建一个空对象。Person调用方法是将对象的引用传递给它:Person.call({}, 'Joe', 20)- 现在“内部”
Personthis指的是上一步中传递的对象。 - 使用以下方法将对象的 proto 设置为函数的原型:
{}.__proto__ = Person.prototype. - 最后返回对象,这就是我们接下来要讨论的内容。
me
概念说明:JavaScript 中的每个函数都有一个原型对象。这就是它的用法Array.prototype.map。而且每个对象__proto__本身也有一个对象。想了解更多信息,请观看精彩视频《探索 JavaScript》。
由于 `and`prototype和 `or`__proto__指的是同一个对象,因此无论何时向 `or` 添加一个新函数,prototype它都会在所有实例上可用。
Person.prototype.greet = function() {
console.log('Hi', this.name);
}
me.greet(); // Hi Joe
const you = new Person('Alice', 22);
you.greet(); // Hi Alice
到目前为止,我们已经了解了如何在 JavaScript 中创建类。接下来,让我们了解如何在 JavaScript 中继承类。
让我们创建一个名为 Employee 的新类,它继承自 Person 类。
function Employee(name, age, title) {
Person.call(this, name, age);
this.title = title;
}
// create Employee prototype from Person prototype
Employee.prototype = Object.create(Person.prototype);
const joe = new Employee('Joe', 22, 'Developer');
console.log(joe.name); // Joe
joe.greet(); // Hi Joe
哇,我们终于继承了 Person 类来创建 Employee 类,而且我们不必重写greet函数。
让我们看看刚才发生了什么?
- 我们创建这个
Employee班级的方式和我们创建班级的方式一样Person。- 在我们的员工类中,我们通过传递引用来调用 Person 类
this。这就像super在 ES6 类中使用关键字一样。
- 在我们的员工类中,我们通过传递引用来调用 Person 类
- 这是最重要的部分。我们正在根据Person 原型重新创建Employee 原型,以便访问该类中所有可用的方法。
Person
现在你可能会问,为什么Object.create不直接将 Person 原型分配给 Employee,而是要使用继承 Person 呢?
这是因为我们不希望 Person 和 Employee 共享同一个原型,因为在 JavaScript 中,对象是被引用的。这正是继承 Person 的意义所在。
这就是我们在 JavaScript 中使用原型继承的方式。新的 ES6 类本质上是对其的一种语法糖。这才是底层实际发生的原理。
PS:您可以在这里找到完整的代码:GitHub 仓库
文章来源:https://dev.to/_hridaysharma/understanding-classes-es5-and-prototypal-inheritance-in-javascript-n8d