1 什么是原型对象

1.1 从一段代码开始认识显式原型对象

function FU(){

}
console.log(FU.prototype)

  在上面代码中,定义了一个叫做FU的函数

  然后我们打印了这个函数的一个属性,属性名字叫做prototype,打印结果如下图,prototype指向的对象就是这个函数的原型对象(先不关心它的内容),这个属性prototype就叫做显式原型对象

  

  prototype这个属性指向的对象实际上是一个Object的实例对象,如下代码,输出为true

console.log(FU.prototype instanceof Object)  //true

  结论一:每一个函数都有一个prototype属性,这个属性是一个Object的实例对象,它就是这个函数的原型对象,这个属性prototype叫做显示原型对象

 

1.2 从一段代码开始认识隐式原型对象

let f = new FU()
console.log(f.__proto__)

上面代码创建一个FU()函数的实例f,同时打印了f的一个叫做__proto__的属性,这个属性指向的对象也是函数的原型对象,这个个属性__proto__叫就做隐式原型对象,如下图

   结论二:每一个实例对象都有一个__proto__属性,这个属性是一个Object的实例对象,它就是这个函数的原型对象,这个属性__proto__叫做隐示原型对象

 

1.3 对比显式和隐式原型对象

  可以对比下显式原型对象和隐式原型对象的内容,发现象内容一样,再通过下面代码输出为true,证明显式原型对象和隐式原型对象指向的是同一个对象

console.log(FU.prototype === f.__proto__)  //true

  结论三:显式原型对象和隐式原型对象指向的是同一个实例对象,也就是说函数或者说函数实例的原型对象只有一个,函数的prototype属性(显式)和函数实例的__proto__属性(隐式)都指向它

 

1.4 其它事项

 prototype 属性和__proto__在什么时候创建的

  我们在定义一个函数的时候,js会自动为这个函数添加一个属性叫做prototype ,指向一个Object对象(也是js根据这个函数创建的)

function FU(){
  //prototype = {...}
}

  在创建函数实例的时候,js会自动为这个实例添加一个__proto__属性,并且这个属性的值等于函数的prototype 

let f = new FU()
//f.__proto__ =FU.prototype

 

2 原型对象和构造函数的关系

  还是和上面一样,定义一个函数FU,打印它的原型对象,发现原型对象里面有一个属性叫做constructor,如下图

function FU(){

}

console.log(FU.prototype)

   再执行以下代码,发现这个constructor 等于这个函数

console.log(FU.prototype.constructor === FU)

  同理,f.__proto__.constructor也等于这个函数

let f = new FU()
console.log(f.__proto__.constructor === FU)
console.log(f.__proto__.constructor === f.constructor)

  结论四:函数的原型对象的constructor执行这个函数本身,也就是说,函数和原型对象是相互指向的,函数的prototype属性指向原型对象,原型对象的constructor 有指向这个函数

 

3 关于函数的几个知识

3.1 函数本身也是一个对象

  函数都是通过Function函数创建的  

  我们通常定义一个函数,采用如下写法

let fu  = function FU(){

}

  它等同于

let fu = new Function()

  

3.2 函数这个对象的__proto__属性

  下面代码执行结果为true

  首先函数FU也是个对象,且函数FU这个对象是函数Function的实例对象,所以函数FU这个对象的__proto__属性指向函数Function的prototype,都指向的是Function的原型对象,所以下面代码执行为true

let fu = function FU(){

}
console.log(fu.__proto__ === Function.prototype) //true

  再看一个特殊的函数对象Function

  下面代码执行结果为true,说明函数Function本身同时通过函数FUNCTION创建的,是不是特别神奇

let func = Function
console.log(func.__proto__ === Function.prototype) //true
 console.log(fu.constructor === fu) //true

  结论五:所有函数本身都是通过函数Function创建的(包括函数Function本身),所有函数本身是函数Function的实例对象,所以所有函数的__proto__属性都指向Fucntion的prototype属性

 

4 原型链

  上面说了,原型对象是一个Object对象,既然是一个Object对象,那么也会有__proto__属性

    let fu = function(){

    }
    
    let p1 = fu.__proto__
    console.log(p1)  //函数FU的原型对象
    let p2 = p1.__proto__
    console.log(p2) //函数Object的原型对象
    let p3 = p2.__proto__
    console.log(p3) //null  函数Object的原型对象的原型对象

  如上代码,函数FU的原型对象p1,它是一个Object对象,所以它的原型对象p2就是Object函数的原型对象,p2作为函数Object的原型对象,也是一个Object对象,它的原型对象指向null,这形成了一个链条,且这个链条到函数Object的原型对象就结束了。

 

5 实例及原型对象的属性问题

5.1 定义函数时的属性及实例对象上添加的属性

  定义函数时定义的属性不会出现在原型对象上

  实例上添加的属性也不会出现在原型对象上

    function FU(name,age){
        this.name = name
        this.age = age
    }

    let f = new FU('HAHA',18)
    f.sex = ''
    console.log(f) //FU {name: 'HAHA', age: 18, sex: '男'}
    console.log(FU.prototype.name)  //undefined
    console.log(FU.prototype.age)  //undefined
    console.log(FU.prototype.sex)  //undefined

  

5.2 属性和原型链

  原型对象上的属性不会直接出现在实例对象上

  操作实例对象的某个属性时,会先在实例对象上找这个属性,若是找不到,则会沿着原型链去找

    FU.prototype.a = 111
    let f = new FU()
    console.log(f) //FU {}
    console.log(f.a) //111
    console.log(FU.prototype.a)  //111

    f.a = 222
    console.log(f) //FU {a: 222}
    console.log(f.a) //222
    console.log(FU.prototype.a)  //111

    FU.prototype.a = 333
    console.log(f) //FU {a: 222}
    console.log(f.a) //222
    console.log(FU.prototype.a)  //333