
.length、.split()、.join()这些内置方法呢?我们从来没有明确指定过它们,它们到底是从哪里来的呢?现在别说“哈哈,没人知道,这就是神奇的JavaScript🧚🏻♂️”。这实际上是因为一种叫做原型继承(prototypal inheritance)的玩意儿。它很棒,而且我们用到它的次数比意识到它存在的次数要多得多!
我们经常要创建很多相同类型的对象。假设我们有一个网站,在这个网站上,人们可以浏览狗!
对每一只狗,我们都需要对象来表示它!🐕 我们用不着每次都写一个新对象,而是用一个构造器函数(我知道你在想什么,稍后我将介绍ES6类!),用new关键字创建Dog实例(不过,本文并非是要解释构造器函数,所以我不想谈太多)。
每只狗都有名字(name)、品种(breed)、颜色(color)以及一个bark()函数!

Dog构造器函数时,它并不是我们创建的唯一对象。我们还自动创建了另一个对象,称为prototype(原型)!默认情况下,这个对象包含一个constructor属性,它只是对原始构造器函数的引用,在本例中是Dog。<br />
Dog构造器函数上的prototype属性是不可枚举的,也就是说,当我们试图访问对象的属性时,它是不会出现。但它依然存在!
好吧,那么为什么我们会有这个属性对象呢?首先,我们来创建一些我们想展示的狗。为简单起见,我叫它们dog1和dog2。dog1是Daisy,一只可爱的黑色拉布拉多犬!dog2是Jack,一只无畏的白色杰克罗素犬!😎

dog1输出到控制台,并展开其属性!<br />
name、breed、color和bark。但是__proto__ 属性是什么玩意!它是不可枚举的,也就是说当我们试图获取对象的该属性时,它通常不会出现。下面我们把它展开!😃<br />
Dog.prototype对象!你猜怎么着,__proto__就是对Dog.prototype对象的一个引用。这就是原型继承的目的:构造器的每个实例都可以访问构造器的原型!🤯<br />
bark函数:它对每个实例都是完全相同的,那么与其每次创建一个新的dog时都创建一个新函数,每次都消耗内存,还不如将其添加到Dog.prototype对象!🥳<br />
__proto__属性沿着原型链遍历!<br />
__proto__属性沿着原型链遍历。<br />现在这只是一个步骤,但它可以包含几个步骤!如果继续往下看,我们就可能会注意到,当展开__proto__对象时,并没有包含一个显示Dog.prototype的属性。Dog.prototype本身是一个对象,也就是说它实际上是Object构造器的一个实例!这意味着Dog.prototype也包含一个__proto__属性,这个属性是对Object.prototype的一个引用!<br />
比如.toString()方法。它是在dog1对象上本地定义的吗?嗯,不是的。。它是定义在dog1.__proto__的引用,即Dog.prototype对象上的吗?也不是!它是定义在Dog.prototype.__proto__的引用,即Object.prototype对象上的吗?是的!🙌🏼<br />
function Dog() { ... }),它仍然是有效的JavaScript。不过,ES6实际上为构造器函数以及处理原型引入了一种更简单的语法:类!
类只是构造器函数的语法糖。其工作机制还是一样的!
我们用class关键字编写类。类有一个constructor函数,它基本上就是我们用ES5语法编写的构造器函数!我们要添加到原型中的属性是在类主体本身上定义的。<br />

假设我们要展示几只相同品种的狗,即吉娃娃狗(chihuahua)!不管咋样,吉娃娃依然是狗。为简单起见,我们现在只保留一个name属性给Dog类。不过这些吉娃娃也可以做些特别的事情,它们的叫声很小(smallBark),它们的叫声不是Woof!,而是Small woof!。🐕
在继承的类中,我们可以使用super关键字访问父类的构造器。父类的构造器期望的参数,我们必须传递给super:在本例中是name。<br />

myPet既可以访问Chihuahua.prototype,又可以访问Dog.prototype(并且由于Dog.prototype是个对象,又可以自动访问Object.prototype)。<br />
Chihuahua.prototype有smallBark函数,而Dog.Prototype有bark函数,因此在myPet上,我们既可以访问smallBark,也可以访问bark!
现在我们可以料想得到,原型链不会永远持续下去。最终有一个原型等于null的对象:在本例中就是Object.prototype对象!如果我们尝试访问在本地或原型链上找不到的属性,就会返回undefined。<br />

Object.create()方法。用这个方法,我们可以创建一个新对象,并可以准确指定该对象的原型!💪🏼
为此,我们将已有对象作为参数传递给Object.create方法。该对象就是我们创建的对象的原型!<br />

me对象。<br />
我们没有向对象me添加任何属性,它仅包含不可枚举的__proto__属性!__proto__属性引用了我们定义为原型的对象:person对象,它有一个name和一个age属性。由于person对象是一个对象,因此person对象上的__proto__属性值就是Object.prototype(不过为了使更容易阅读,我没有在gif上展开该属性!)。
希望你现在了解了为什么原型继承在JavaScript的美好世界中如此重要!如有疑问,请随时与我联系!😊
原文 by Lydia Hallie:https://dev.to/lydiahallie/javascript-visualized-prototypal-inheritance-47co
