一、什么是原型对象
原型对象: 函数中的一个属性,prototype,这个属性就是用来指向原型对象的,我们把这个对象称为原型对象
function Fn() {}
console.log(Fn.prototype);
通过控制台可以看见Fn的原型对象上有一个属性 constructor(构造器) 而它的作用就是 指回构造函数本身
有了原型对象(prototype),我们就可以在原型上定义方法,问题点来了 为什么不在构造函数中直接定义方法呢?
我们通过构造函数创建对象的时候开辟了一个新的空间,如果构造函数内部中又有一个方法 又会在内存中开辟了一个空间,缺点:浪费内存
而我们通过给原型对象(prototype)上添加方法,通过创建实例对象都可以访问得到 作用:共享方法 节省内存
如何给原型上添加方法呢?
和给普通对象添加方法没什么区别,平常怎么添加,只不过在对象前加一个prototype属性 就可以了,我们通常把属性放在构造函数里,把方法放在原型对象身上
function Fn(name, age) {
this.name = name
this.age = age
}
Fn.prototype.eat = function () {
console.log(eat);
}
接下来我们去验证它 到底创建的实例对象有没有这个eat方法呢
let obj = new Fn('奥特曼', 20)
console.log(obj);
从上面啊发现了__proto__ (指针)中可以得到eat方法 所以啊 定义是在原型上定义的 而实例对象又通过__proto__拿到了eat()方法 所以 Fn.prototype===obj.__proto__
注意点:我们是通过.的方式进行添加方法,如果通过对象赋值去往原型上添加方法 需要 指回原来的构造函数 原因就是原来的constructor被替换掉了
Fn.prototype = {
eat: () => {
console.log('爱吃宫保鸡丁');
}
}
Fn.prototype.constructor = Fn //原型对象指回原来的构造函数
构造函数、实例对象、原型对象 三者关系
回过头来看 __proto__(隐式原型 ) 每个对象中 都有一个属性叫__proto__ 他是用来指向原型对象的,(并且他是非标准属性,所以说我们不能操作它,只能提供一个指向)
当查找成员(属性和方法) 的时候它会优先从自身去查找,如果自身没有他回去__proto__的指向继续查找,我们知道了实例化对象的__proto__指向了原型对象,那么原型对象的__proto__指向谁呢?并且再__proto__呢?
console.log(obj.__proto__.__proto__.constructor);//constructor指向构造函数本身
console.log(obj.__proto__.__proto__.__proto__);
最后发现Object的__proto__为null 所以说原型对象到了Object就已经到顶了 接下来把他们串起来就形成了原型链
原型链
成员查找机制
function Fn() {}
Fn.prototype.name = '怪兽'
let obj = new Fn()
obj.name = "奥特曼"
console.log(obj.name);
分别在原型对象上挂和obj一个属性同样的name属性 如果自身有则输出自身,
如果自身没有则输出原型上的属性(查找离自己最近一级的)
如果原型和自身上都没有 输出undefined
this指向
Fn.prototype.eat = function () {
console.log(this);
}
console.log(obj);
obj.eat()
构造函数中的this和原型对象上的this 都指向的是实例对象
总结 通过上面的一些例子来看,如果实例对象本身没有的属性则会去离自己最近的原型对象去找如果还没有则会去原型对象上的原型对象去找,直到找不到该属性,形成一种链状的结构,从而也达到了继承的作用