构造函数、实例、原型三者的关系
在JavaScript中每创建一个函数,就会为这个函数创建一个prototype属性,该属性指向函数的原型对象,而这个原型对象在默认情况下会有一个名为constructor的属性指回构造函数。
//定义构造函数
function Person(){
}
//拿到Person的原型对象
let pro=Person.prototype
//给原型添加属性
pro.name="Kobe"
pro.age=16
//验证Person的原型对象的constructor属性是否指回Person
console.log(pro.constructor===Person) // true
通过构造函数来创建实例,该实例内部的__ proto__属性也会指向构造函数的原型对象
let p1=new Person()
let p2=new Person()
console.log(p1.__proto__===Person.prototype) // true
console.log(p2.__proto__===Person.prototype) // true
看图说话:
通过实例来修改原型上的属性
- 如果修改的是保存引用值的属性上的一个或多个属性,则会修改原型上的属性
//定义构造函数
function Person() {
Person.prototype.msg = {
name: '张三',
age: 18
}
Person.prototype.color=['yellow','blue']
}
let p=new Person()
p.msg.name="李四"
p.msg.age=30
console.log(Person.prototype.msg) // {name: "李四", age: 30}
p.color.push('black')
console.log(Person.prototype.color) // ['yellow','blue','black']
- 如果修改的是保存原始值的属性,或直接将保存引用值的属性“整个儿换掉”,则不会修改原型上的属性,而是直接在实例对象上添加我们修改的属性
//定义构造函数
function Person() {
Person.prototype.name = "Kobe"
Person.prototype.job = {
salary: '30k'
}
}
let p = new Person()
p.name = "Alice"
p.job = {
str:'hello'
}
console.log(p)
console.log(p.__proto__)
打印结果:
很明显,对于这种情况只会把要“修改的属性”添加到自身
原型链
每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。这就是原型链的基本构想 ——《JavaScript高级程序设计第四版》
原型解决了数据共享问题,即多个实例共享原型上的数据。那么如果不同的构造函数所创建的实例要想共享数据呢?原型链就可以解决这个问题。
做个假设:A实例要想要想从毫不相干的B实例身上取数据。
如果想要实现这种效果,我们可以把A类型的prototype属性指向B类型的实例,即重写A的原型对象为B实例对象,这样就实现了一种继承关系。如果想要通过A实例查找某一属性,会先从自身找,找不到会从原型对象(B实例)上找,找不到继续在原型对象的原型对象(B实例的原型对象)上找…这个查找过程就是在原型链上查找的。
function A() {}
function B() {
B.prototype.num = 12
}
//重写A类型的原型
A.prototype = new B()
//创建A实例
let a = new A()
console.log(a.num) // 12
默认原型
任何一个函数的原型默认都是一个Object类型的实例,所以原型的__ proto__会指向 Object.prototype(即Object构造函数的原型),Object的原型的原型为null,即原型链的尽头。
function Cf() {}
let c = new Cf()
//这里c是Cf类型的实例,而c的原型默认是Object类型的实例
console.log(c.__proto__ instanceof Object) //true
console.log(c.__proto__.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__) //null