# 还有人不会原型链我都会伤心的好吗

140 阅读3分钟

前言

很多小伙伴(包括我自己)在初学原型的时候老是被绕晕,但是其实认认真真的画一张图,仔细琢磨琢磨就发现真的没什么难度。

好了,不啰嗦了,开始正题吧。

构造函数

在说原型之前提一下构造函数吧,我们都知道构造函数是用于创建特定类型对象的,有原生构造函数(像Object,Array之类的),还有自定义构造函数,比如

function Person(name,age){
    this.name = name
    this.age = age
    this.sayName = function(){
        console.log(this.name)
    }
}

然后就可以通过new来创建一个实例啦

那么构造函数有什么优缺点呢?

优点:构造函数可以批量工厂化的产出具有相同属性或者方法的对象,简单点说就是简化代码。

缺点:比如上面的代码里面有个sayName方法,如果我创建n个该构造函数的实例,那么每一次我都要在这个实例上重新创建一次sayName方法,这每一个实例上的sayName方法还不是同一个方法,因为函数也是对象嘛,你仔细想想,有没有这个必要呢,都是做同一件事情,为啥我每次都要重新创建一次这个函数呢?

如果你没有听懂(肯定是我表达不对),可以这样设想:

在一个工厂里面有非常多的工人,这些工人每次进入工厂以后都要把手用消毒酒精消个毒。明明我把酒精放在门口让他们共享就行了,我非得喊他们自己一人拿一瓶酒精去消毒,有必要不,这不纯纯闹心么。

所以,原型登场了

原型

每个函数都会创建一个prototype属性,这个属性就指向它的原型,使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享,由于是共享的,所以实例访问原型上的属性和方法都是相同的。

举个栗子🌰:

function Person (name, age) {
    this.name = name
    this.age = age
}
Person.prototype.sayName = function () {
    console.log(this.name)
}

const person1 = new Person('lxy', 19)
const person2 = new Person('zl', 21)
person1.sayName()//lxy
person2.sayName()//zl
console.log(person1.sayName === person2.sayName)//true

理解原型

无论什么时候,只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype属性(指向原型对象)。默认情况下,所有原型对象会自动获取一个名为constructor的属性,指向与之关联的构造函数。每次调用构造函数创建一个新实例,这个实例内部__proto__属性就会指向它的构造函数的原型

image.png

原型链

我们再来重温一下构造函数,原型和实例的关系:每个构造函数都有一个原型对象,原型有一个属性(constructor)指回构造函数,而实例有一个__proto__指向原型。那么原型是不是一个类的实例呢,如果是,那不就意味着原型也有__proto__指向它的原型了呢。这其实就是原型链的基本思想了。

就以上面的图为例,思考一下,原型是谁的实例呢?对啦,是对象的,那么这张图就可以再延申一下啦。

image.png

上面举的例子是非常简单的例子,其实我们可以再思考一下Person又是谁的实例呢?Person是不是应该是Function的实例呀,那是不是又可以拓展一下这个图呢,如果你动手画了,你就会明白为什么说js里面一切皆为对象了,比如上面这个简单的例子,Person指向它的原型,而它的原型又指向对象的原型,所以Person应该具有对象原型上的方法(希望这么说你可以理解)。

我在网上找了一张有意思的图,很值得揣摩一下:

image.png

很有意思,可以静下心来看一看,如果看懂了,记得自己动手画一画哦,今天的分享就到这里啦,感谢看完哦 (补充:我也是学习阶段,如果有什么不对的地方,欢迎大佬们更正)