原型、原型链

100 阅读3分钟

每个函数都有一个prototype属性,它默认指向一个Object空对象(即原型对象)

原型对象中有一个constructor属性,指向函数对象

没有我们自己的属性:
function fn (){
​
}
console.log(fn)
console.log(fn.prototype)

image-20220420161221994.png

给原型对象添加方法,实例对象可以访问
function fn1(){
​
}
fn1.prototype.myTest = function (){
    console.log('原型对象中的方法')
}
console.log(fn1.prototype)
// 1.创建实例对象
let FUN1 = new fn1() 
// 2.调用这个方法
FUN1.myTest()

image-20220420162312760.png

原型对象中的constructor属性指向该函数对象
function fn1(){
​
        }
        fn1.prototype.myTest = function (){
            console.log('原型对象中的方法')
        }
        console.log(fn1.prototype)
        // 1.创建实例对象
        let FUN1 = new fn1() 
        // 2.调用这个方法
        FUN1.myTest()
        console.log(fn1.prototype.constructor === fn1)

image-20220420162511436.png

image-20220420163807368.png 构造函数和它的原型对象有个相互引用的关系:

  • 即构造函数中有个属性指向原型对象
  • 原型对象中有个属性指向该构造函数

显式原型与隐式原型

每个函数都有一个prototype,即显式原型对象
每一个实例对象都有一个_proto_ 属性,即隐式原型属性
实例对象的隐式原型属性指向其对应构造函数的显示原型的值
现在打印实例对象的隐式原型对象值为:undefiend

个人推测:

做了优化,实例对象的原型对象就是指向构造函数的原型对象,构造函数的原型对象上的方法就是自己加上去的,这样实例对象调用构造函数原型对象上的方法时,先回在实例对象身上找找不到再去原型对象上找(这个就是构造函数原型上加的方法),按照这个逻辑也是能够走通的

实例对象的隐式原型属性值与其对应构造函数的显示原型的值是相等的,是地址值,指向栈空间中同一块内存地址

实例对象的隐式原型属性不是我们加上去的,是new构造函数的时候产生的,说明new操作符内部的执行过程应该有一步:新对象的proto属性 = 构造函数的prototype属性,详情参考new操作符的执行过程

image-20220420190813079.png

image-20220420190742122.png

原型链

实例对象有隐式原型属性,实例对象隐式原型属性的值等于他构造函数显示原型的值

过程:

用来查找一个对象的属性=

  • 先在自身属性中查找,找到则返回
  • 如果没有,再沿着_proto_(隐式原型)这条链向上查找,找到则返回
  • 如果最终没有找到,返回undefined
所有函数的隐式原型属性都指向 Function 的原型对象
Function 是 new Function() 得到的
因此 Function.proto = Function.prototype,Function的隐式原型指向自己的显式原型
Object的隐式原型指向Function的原型对象
Object是 new Object() 出来的,new Object() 其实就是在执行函数:function Object(){},而所有的函数的隐式原型都指向Function的原型对象(任何函数都是new Function() 产生的)
原型继承
构造函数的实例会自动拥有构造函数原型对象的属性(利用的就是原型链)