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
结论五:所有函数本身都是通过函数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