原型及原型链的概念
1、原型的概念
JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是自身的原型
JavaScript的函数对象,除了原型 [proto] 之外,还有 prototype 属性,当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]
2、原型链的概念
当一个对象调用自身不存在的属性/方法时,就会去自己 [proto] 关联的前辈 prototype 对象上去找,如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”。
总结
JavaScript中的对象,都有一个内置属性[Prototype],指向这个对象的原型对象。当查找一个属性或方法时,如果在当前对象中找不到,会继续在当前对象的原型对象中查找;如果原型对象中依然没有找到,会继续在原型对象的原型中查找(原型也是对象,也有它自己的原型);直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined。这个查找过程是一个链式的查找,每个对象都有一个到它自身原型对象的链接,这些链接组建的整个链条就是原型链。拥有相同原型的多个对象,他们的共同特征正是通过这种查找模式体现出来的。
在上面的查找过程,我们提到了最顶层的原型对象,这个对象就是Object.prototype,这个对象中保存了最常用的方法,如toString、valueOf、hasOwnProperty等,因此我们才能在任何对象中使用这些方法。
class-extends-原型问题及解析
function Fn(){
this.a = 1//实例对象上挂a
this.b = 2
// 实例上挂一个属性,属性名是一个对象
this.getA = function(){
console.log(this.a);
}
}
// 类的原型对象上添加方法
Fn.prototype.getA = function(){
console.log(this.a);
}
Fn.prototype.getB = function(){
console.log(this.b);
}
// 实例
let f1 = new Fn()
let f2 = new Fn //小括号有没有都行
// 问题
console.log(f1.getA === f2.getA);//false
console.log(f1.getB === f2.getB);//true
console.log(f1.__proto__.getB===Fn.prototype.getB);//false
console.log(f1.__proto__.getB === f2.getA);//false
console.log(f1.__proto__.getA === Fn.prototype.getA);//true
console.log(f1.constructor);
// ƒ Fn(){
// this.a = 1//实例对象上挂a
// this.b = 2
// 实例上挂一个属性,属性名是一个对象
// this.getA = function(){
// console.log(this.a);
// }
// }
console.log(Fn.prototype.__proto__.constructor);
// ƒ Object() { [native code] }
f1.getA()//1
f1.__proto__.getA()//undefined
f2.getB()//2
Fn.prototype.getB()//undefined
图像解析:
类:prototype属性指向原型对象
原型:constructor指向类
所有对象都有__proto__属性,属性值是当前实例的所属类的原型
原型链:先查找自己的私有属性=> __proto__ =>共有属性
问题:
01. console.log(f1.getA === f2.getA); 输出://false
解析:f1实例和f2实例开辟出来的空间营地址不一致,判断为false
02. console.log(f1.getB === f2.getB); 输出://true
解析:f1实例和f2实例均没有getB,查找公有属性,在所属性的原型中查找,公有属性相同,判断为true
03. console.log(f1.__proto__.getB===Fn.__prototype__.getB); 输出://true
解析:f1.__proto__.getB => 原型对象的getB <= Fn.__prototype__.getB
04. console.log(f1.__proto__.getA === f2.getA);输出://false
解析:f1.__proto__.getA =>营地址1 f2.getA =>营地址4
05. console.log(f1.__proto__.getA === Fn.prototype.getA);输出://true
解析:f1.__proto__.getA =>营地址1<= Fn.prototype.getA
06. console.log(f1.constructor);
输出:
ƒ Fn(){
this.a = 1//实例对象上挂a
this.b = 2
实例上挂一个属性,属性名是一个对象
this.getA = function(){
console.log(this.a);
}
}
解析:f1实例私有属性中没有constructor,去共有属性原型对象中查找,原型对象constructor指向Fn类
07. console.log(Fn.prototype.__proto__.constructor); 输出:// ƒ Object() { [native code] }
解析:Fn.prototype=>Fn的原型对象
Fn.prototype.__proto__=>Object的原型对象
Fn.prototype.__proto__.constructor=>Object
08. f1.getA() 输出://1
解析:函数调用,实例自己的方法,f1.getA()=1
09. f1.__proto__.getA() 输出://undefined
解析:f1.__proto__=>Fn的原型对象 原型对象中getA方法没有a参数,输出underfind
10. f2.getB() 输出://2
解析:getB的this指向f2,this指向实例f2的b,输出2
注意:函数this指向问题,特点:谁调用this就指向谁
11. Fn.prototype.getB() 输出://underfind
解析:Fn.prototype=>Fn的原型对象 原型对象中getB方法没有参数,输出underfind
控制台输出:
函数原型对象:
console.log(Fn.__proto__);
ƒ () { [native code] }
console.log(Fn.__proto__.__proto__);
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(Fn.__proto__.constructor);
ƒ Function() { [native code] }
console.log(Fn.__proto__.constructor.__proto__);
ƒ () { [native code] }
console.log(Fn.prototype.__proto__.__proto__);
null
输出: