JS原型链详解

3 阅读3分钟

例子:

function Person(name,walk){
    this.name=name
    this.walk=walk
    this.getName=function(){
        console.log(this.name)
    }
}
Person.eat=function(){console.log('eat')}
Person.prototype.getWalk=function(){console.log(this.walk)}
const person=new Person('张三','顺拐')

constructor

  1. 当我们找实例的constructor的时候他自己本身是没有的,所以会沿着原型链往上找,而他原型链的上一级是他构造函数的prototype,这个有constructor,所以会被实例继承,而这个constructor指向构造函数本身,即person无constructor,他继承Person.prototype的constructor,而Person.prototype.constructor=Person,所以person.construtor=Person
  1. 构造函数本身也是一个对象,他作为对象的行为和实例也一样,即Person作为对象的行为和person是一样的,所以他也会继承他的构造函数的prototype的constructor,而所有的函数作为对象时的构造函数均是Function,所以Person.constructor=Function.prototype.constructor=Function

prototype(原型对象)与__proto__(对象原型,下面简称原型)

  1. 函数有一个对象属性是prototype(只有函数有,普通对象是没有的),注意箭头函数没有prototype,构造函数也是函数所以也有prototype,这个属性定义了构造函数制造出来的对象的公共祖先,通过该构造函数产生的对象可以继承该原型的属性和函数,即person可以继承Person.prototype上的函数

  2. 实例的原型都指向他的构造函数的原型对象,即person的原型指向Person.prototype

  3. 所有的函数(包括箭头函数,箭头函数只是没有prototype而已,也就是不能构造实例)的构造函数都是Function,即所有的函数都是Function的实例,包括Object,因为Object本身也是构造函数,可以拿来构造对象,所以任何函数的原型都指向Function.prototype,所以Object的原型也指向Function.prototype,而Function是一个构造函数,所以Function自己也是由Function.prototype构造的,即Function本身的原型也指向Function.prototype

  4. 普通对象是Object的实例,即由Object构造的所以Object是普通对象的构造函数,所以普通对象的原型为Object,注意,构造函数是函数,但是构造函数的prototype本身是一个非函数对象,即Person.prototype和Function.prototype的原型都指向Object.prototype

    而Object.prototype理论上来说也是对象那也应该由Object.prototype构造,但是为了避免循环所以Object.prototype指向null

  5. 参考3,4可知原型链即对象的原型,原型的原型...,比如person的原型链为person=>Person.prototype=>Object.prototype=>null,并且要理解Person的原型链是Person=>Function.prototype=>Object.prototype=>null,Function的原型链是Function=>Function.prototype=>Object.prototype

  6. 注意,在构造函数中写this.属性那构造函数不会有这个属性,因为这本质上是一条语句而不是属性,this是指向调用方的,所以实例中会有这个属性,比如

    function Person(){
        this.name=15
    }
    const p=new Person()
    console.log(p.name)//15
    console.log(Person.name)//undefined
    Person.prototype.name=20
    console.log(p.name)//15,因为原型链从底往上找的
    

    一般像这种固定值的属性没必要放在Person中用this给实例定义,因为这样一来每个实例都会创建一个,不如直接放在原型上即Person.prototype.name=15这样如何Person里不再定义this.name

  7. 如果修改Person.prototype的引用即Person.prototype={}有两种情况,若实例是在修改前定义的那实例将无法连接到新的Person.prototype上,因为他已经连接到旧的上了,如果在修改后定义那就连接在新的上面,注意Person能否连接到新的上面取决于新的引用是否将constructor指回Person,永远记得Person.prototype也只是一个对象,他和Person的连接是通过上面的constructor对象属性指向Person完成的,新对象如果直接没定义constructor或者定义成别的那也连接不上Person

  8. 通过实例是无法修改构造函数原型上的属性的,比如如果person.name='李四'那这个是给perosn实例这个对象上添加了一个叫name的属性而不是Person.prototype.name被修改了; delete person.name只能删除person自己的name属性不会删除Person.prototype的name属性,但是注意即使person上没name这个也会返回true,这不代表Person.prototype的name属性被删除了,而是执行删除没找到要删除的也算删除成功 ;