[译]JavaScript️可视化:原型继承

1,239 阅读5分钟

本系列

正文

有没有想过,为什么我们可以使用内置的方法,例如.length,.split(),.join()我们的字符串,数组或对象?我们从未明确指定它们,它们来自何处?现在,不要说“这是JavaScript,没人知道,这是魔术🧚🏻‍♂️”,这实际上是由于所谓的原型继承。它非常棒,而且您使用它的次数比您想象的要多!

我们经常必须创建许多相同类型的对象。假设我们有一个网站,人们可以浏览狗!

对于每只狗,我们都需要代表那只狗的对象!🐕我将使用构造函数(我知道您在想什么,稍后将介绍ES6类!),而不是每次都编写一个新对象。我们可以使用new创建Dog实例(本文并不是真的要解释构造函数,因此,我不会对此进行过多讨论)。

每只狗都有名字,品种,颜色和吠叫功能!

创建Dog构造函数时,它不是我们创建的唯一对象。我们还自动创建了另一个对象,称为原型(prototype)!默认情况下,此对象包含一个构造函数(constructor),该属性只是对原始构造函数的引用,在本例中是Dog。

Dog的prototype属性是不可枚举的,这意味着当我们尝试访问对象的属性时,该属性不会显示。但是它仍然在那里!

好吧..为什么会有这个属性对象?首先,让我们创建一些我们想要展示的狗。为简单起见,我将其称为dog1和dog2。dog1是黛西,一个可爱的黑色拉布拉多犬!dog2是杰克,无所畏惧的白人杰克罗素😎

我们到控制台打印dog1,并展开其属性!

我们看到我们添加,如性能name,breed,color,和bark..但__proto__是什么!它是不可枚举的,这意味着当我们尝试获取对象的属性时,通常不会显示它。让我们展开它!😃

哇,看起来就像Dog.prototype!好吧,__proto__是对Dog.prototype对象的引用。这就是原型继承的全部内容:构造函数的每个实例都可以访问构造函数的原型!🤯

那么为什么这很酷?有时我们拥有所有实例共享的属性。例如本例中的bark:每个实例都完全相同。为什么每次创建新狗时都创建一个新函数?这样很消耗内存!!!相反,我们可以将其添加到Dog.prototype对象中!🥳

每当我们尝试访问实例上的属性时,引擎都会首先在本地搜索以查看该属性是否在对象本身上定义。但是,如果找不到我们要访问的属性,引擎将沿着该__proto__属性沿着原型链走下去!

现在这只是一个步骤,但是可以包含多个步骤!您可能已经注意到,当我展开__proto__显示的对象时,我没有包括一个属性Dog.prototype。Dog.prototype本身是一个对象,这意味着它实际上是Object构造函数的实例!这意味着Dog.prototype还包含一个__proto__属性,该属性是对Object.prototype!的引用。

最后,我们对所有内置方法的来源都有一个答案:它们在原型链上!😃

例如.toString()方法。它不是在dog1上定义的,也不是在dog1.proto(即Dog.prototype)上定义的,是在Dog.prototype.proto(即Object.prototype)上定义的! 🙌🏼

现在,我们一直在使用构造函数(function Dog() { ... }),它仍然是有效的JavaScript。但是,ES6实际上为构造函数和原型使用了一种更简单的语法:类!

类仅是构造函数的语法糖。一切仍然以相同的方式工作!

我们用class关键字编写类。一个类有一个constructor函数,与我们用ES5语法编写的构造函数一样!我们要添加到原型的属性是在类主体本身上定义的。

关于类的另一个好处是,我们可以轻松扩展其他类。

假设我们要展示几只相同品种的狗,即吉娃娃犬!吉娃娃犬还是狗。为了保持这个例子简单,我只通过传递name来初始化,而不是现在的name,breed和color。但是这些吉娃娃也可以做些特别的事情,它们的叫声很小。除了叫Woof!,吉娃娃也可以叫Small woof!🐕

在扩展类中,我们可以使用super关键字访问父类的构造函数。父类的构造函数期望的参数,在这种情况下,我们必须传递给super:name。

myPet可以访问Chihuahua.prototype和Dog.prototype(并且自动访问Object.prototype,因为Dog.prototype是一个对象)。

由于Chihuahua.prototype有smallBark,Dog.prototype有bark,我们就可以同时访问smallBark和bark上myPet!

现在,您可以想象,原型链不会永远持续下去。最终有一个原型等于null的Object.prototype对象:在这种情况下就是对象!如果我们尝试访问在本地或原型链上找不到的属性,undefined则会返回。

尽管我在这里用构造函数和类解释了所有内容,但是将原型添加到对象的另一种方法是使用Object.create方法。使用此方法,我们可以创建一个新对象,并可以准确指定该对象的原型!💪🏼

我们通过将现有对象作为参数传递给Object.create方法来实现。该对象是我们创建的对象的原型!

让我们打印me刚刚创建的对象。

我们没有向该me对象添加任何属性,它仅包含__proto__属性!__proto__属性包含对我们定义为原型的person对象的引用:该对象具有name和age属性。由于person对象是对象,因此对象上的__proto__属性的值person是Object.prototype(但为了使其更容易阅读,我没有在gif中扩展该属性!)


希望您现在了解为什么原型继承在JavaScript的奇妙世界中如此重要!如有疑问,请随时与我联系!😊

原文

个人博客

github.com/abc-club/fr…