前端圈

分享与交流前端开发相关知识

Visual可视化的JavaScript:原型继承

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

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

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

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

《Visual可视化的JavaScript:原型继承》

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

《Visual可视化的JavaScript:原型继承》

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

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

《Visual可视化的JavaScript:原型继承》

让我们登录dog1到控制台,并扩展其属性!

《Visual可视化的JavaScript:原型继承》

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

《Visual可视化的JavaScript:原型继承》

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

《Visual可视化的JavaScript:原型继承》

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

《Visual可视化的JavaScript:原型继承》

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

《Visual可视化的JavaScript:原型继承》

现在这只是一个步骤,但是可以包含多个步骤!如果继续进行下去,您可能会注意到,在扩展__proto__显示为的对象时,我没有包含一个属性Dog.prototypeDog.prototype本身是一个对象,这意味着它实际上是Object构造函数的实例!这意味着Dog.prototype还包含一个__proto__属性,该属性是对Object.prototype!的引用。

《Visual可视化的JavaScript:原型继承》

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

例如.toString()方法。它是在dog1对象上本地定义的吗?嗯。。是否在对象dog1.__proto__上定义了一个引用,即Dog.prototype?也没有!是否在对对象Dog.prototype.__proto__进行了引用时定义了Object.prototype?是! 🙌🏼

《Visual可视化的JavaScript:原型继承》

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

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

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

《Visual可视化的JavaScript:原型继承》

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

假设我们要展示几只相同品种的狗,即奇瓦瓦狗!奇瓦瓦狗(还是……)仍然是狗。为了保持这个例子简单,我只通过name属性狗类,而不是现在的namebreedcolor。但是这些吉娃娃也可以做些特别的事情,它们的树皮很小。Woof!吉娃娃不用说也可以说Small woof!🐕

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

《Visual可视化的JavaScript:原型继承》

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

《Visual可视化的JavaScript:原型继承》

由于Chihuahua.prototypesmallBark功能,Dog.prototypebark功能,我们就可以同时访问smallBarkbarkmyPet

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

《Visual可视化的JavaScript:原型继承》


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

通过将现有对象作为参数传递给Object.create方法,我们可以做到这一点。该对象是我们创建的对象的原型!

《Visual可视化的JavaScript:原型继承》

让我们记录me刚刚创建的对象。

《Visual可视化的JavaScript:原型继承》

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


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

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注