这篇文章主要是要来介绍原型
在介绍原型之前,我们要先来複习物件的内容
当我们要定义一只狗的时候,我们会利用物件的资料结构,来对这只狗进行描述。
例如它的颜色、体型大小,以及可以吠叫的方法。
主要的原因就是因为 JS 基本上都是由物件的方法去构成资料。
而原型呢也是一样的概念喔!
在前一篇文章,类别继承中我们有提到,如果我们要定义一个像是这样的内容,我们会使用 class 来定义它。
但是 JS 中都是使用物件来定义,所以我们在定义原型的时候也是使用物件的方式来定义。
如果狗要透过这个原型来建立实体的话,也是透过继承的方式继承了原型的属性跟方法,因此这就是两个物件的概念。
原型的狗就会有颜色、体型大小,以及可以吠叫的方法。而在实体的狗呢这些属性就会有可以自定义的空间。
那么吠叫的部分就可以继承原本原型的方法。
实际上,当我们运行JS的时候,新增一个物件的实体的时候就会有属于该实体的属性
那么原型的部分依样也会有它自己的属性,那这样的结构上依然是属于两个物件。
另外,除了实体可以继承一个原型之外,原型也可以继承另外一个原型喔!
那么在另外一个原型的部分也可以拥有自己的属性以及方法,那么这样的继承关係可以一段一段的向上继承,这样的继承状态我们又称为原型链。
再来,这个实体我们要取用其中的属性的时候,会使用点运算子的方法进行取用,例如 obj.Prop1
、obj.Prop2
而当我们要取用的属性或是方法没有在这个实体上的时候,就会透过原型链向上查找,直到找到这个原型链的顶端为止。
除此之外呢,原型还有另一个特色,就是
如果用同一个原型新增了两个实体,这两个实体就会共用同一个原型继承,共用相同的属性名称以及相同的方法。
接下来总结一下观念:
那么我们再来就透过程式码的运作来进行刚刚上面的观念验证吧!
var a = [1, 2, 3];console.log(a);
执行之后我们把这个阵列打开来看
可以看到里面有对应 0 1 2的属性,因为我们知道阵列的本质其实也是物件,所以这里的 0 1 2其实也是这个阵列的物件属性。当然 length 也是。
所以物件的属性的话我们有两种取值的方式,一个是 []
一个是 .
那么这个阵列现在是属于一个实体,我们可以透过 proto 的属性 来看看它阵列的原型
可以看到阵列的原型中有许多的方法,那么我们刚刚也提到可以透过点运算子取用他的方法。
那么这边我们选用的是 forEach 的方法,透过这个方法可以将阵列的每个值都遍历过一遍。
那么这个 forEach 就不是属于 a 这个实体的方法,而是属于阵列原型的属性方法。
还有我们刚刚也有讲到原型是共用的,所以我们再来新增 b 这个新的阵列 为 [4, 5 , 6]
我们刚刚也有提到 proto 的属性是指向阵列的原型,所以我们照理说也可以用 proto 的属性新增方法到阵列的原型上,让 b 阵列也可以取用到相同的方法。
PS: proto 的属性虽然可以达成一样的效果,但一般我们还是不建议这样使用,后面的章节会教大家使用 prototype 的属性将要新增的 function 挂载到原型上,让其他实体也可以取用到。
那么这边我们把阵列最后一个值取出来的方法透过 proto 的属性挂载到阵列的原型上。
另外我们就真的实用这个 getLast 的方法看是否有成功
很明显就成功的印出我们想要的内容。
讨论一下原型的层级问题吧!
我们刚刚说到,打开a 或 b 的 proto 的属性能够找到阵列的原型。
那么继续往下找又可以找到另一个 proto 的属性,再把它打开以后会看到的是物件的原型。
因此这个阵列的原型其实是继承了物件的原型。
而继续往下看就可以看到物件的一些方法,但到最底就没有 proto 的属性,代表说这个物件的原型就是原型链的最顶层了!
好,我们再来看一些code。
我们新增了一个新的物件,并且对该物件的物件原型上挂载 getName 的方法,而之后打开 a 跟 b 的原型链最顶端的物件原型,也可以找到 getName 的方法的方法喔!
那么接下来我们就试着在阵列中使用这个 getName 的方法。
首先我们先对 b 这个阵列加入了 name 的属性,之后我们预期用 getName 应该可以抓到我们刚刚设定的 name 的内容
果不其然成功了,但阵列上并没有 getName 的方法,所以透过向上查找的方式,在物件的原型上找到了 getName 的方法并且使用它。
那么这篇文章就介绍了原型以及原型链的概念以及实作。
如果没有问题的话就继续往下巴~!汪汪