一道简单的构造函数题目,让我失眠了...
上个礼拜,我的老师给我们布置了一道与构造函数、原型对象相关的代码题目。
抱着不想被老师点名回答时尴尬的“罚站”的心态😅,自认为已经学会了构造函数的原型关系,应该一下子就画出老师想要的。
结果,没想到我肝到半夜一点半!!!😮
经过我不断地调试、向老师解惑,让我来嗦嗦我一直困惑的问题吧~/(ㄒoㄒ)/~~
一、构造函数与原型对象、原型链的关系
js 面向对象由以下概念组成:
1. 构造函数有prototype 属性, 值是构造函数的对象, prototype对象归构造函数的所有实例共享。
2. 实例都拥有一个__proto__ 私有属性,对象在查找完自身的属性和方法后,会继续 沿着.__proto__ 指向的原型对象去查找。如果有,返回;如果没有,会继续沿着原型对象去查找。
任何对象都有__proto__ ,不指定的话就指向Object。
这些原型对象形成了一条链,叫做原型链。
3. 构造函数的实例(cai实例)
Pserson 构造函数
Person.prototype 原型对象
Person.prototype.__proto__ 原型对象 (Object) js 内置的基于对象
cai.toString() 原型链上的方法, 对象查找时会沿着原型链一直查找
实例的方法 不要放到构造函数里声明,因为给每个实例分配一个函数,内存是吃不消的
Object 是(函数也是)对象, 也是构造函数
Object.prototype.__proto__ == null 到顶了,都没有, undefined
对象的终点是null
cai.__proto__->Person.prototype
Person.prototype.__proto__->Object.prototype
Object.prototype.__proto -> null
实例的.__proto__
属性指向原型对象,构造函数的prototype
属性也指向原型对象。
如图的结果显示,构造函数的原型对象的.__proto__
指向的原型对象是Object
而原型链的顶端是null
二、发现问题
老师给的代码如下:
function superType() {
this.property = true
}
superType.prototype.getsuperValue = function () {
return this.property;
}
function SubType() {
this.subproperty = false
}
SubType.prototype = new superType()
SubType.prototype = {
getsubValue() {
return this.subproperty
},
someOtherMethod() {
return false
}
}
let instance = new SubType()
console.log(instance.getsuperValue())
打印的结果为什么是错误的❓
一开始,我也被上面的代码迷惑😟:SubType.prototype = new superType()
SubType的原型对象不是superType实例嘛,instance为什么会找不到方法呢❓
let instance = new SubType()
console.log(instance.__proto__)
console.log(instance.__proto__.constructor)
我开始打印这个instance实例的原型对象和构造函数,结果发现:
- SubType.prototype被重写了?
- 那为什么它的构造函数是
Object(){ }
?
三、解决问题根源
为了搞清楚上面几个问题,我写了以下代码进行详细讲解。
//所有的构造函数,都是基于object(){} 建立的
function superType() {
this.property = true;
}
//superType原型对象上声明了一个函数
superType.prototype.getsuperValue = function () {
return this.property;
};
console.log(superType.prototype.constructor)//结果:superType(){}
function SubType() {
this.subproperty = false;
}
console.log( SubType.prototype.constructor,"?????????")// SubType(){}
// SubType的prototype原型对象是superType实例,指向superType构造函数
SubType.prototype = new superType();
console.log( SubType.prototype.constructor)// superType(){}
//SubType的实例,找到SubType的prototype属性,是指向superType构造函数的实例,能够找到superType原型上的方法
let instance1 = new SubType(); // 会重写constructor属性
console.log(instance1.getsuperValue(),instance1.constructor);//结果:true, superType(){}
// 重写原型对象后,首先原型对象的constructor属性值(constructor的指向)会发生改变。
// SubType的prototype属性重写,原型对象上声明两个函数,构造函数是Object();
SubType.prototype = {
getsubValue() {
return this.subproperty;
},
someOtherMethod() {
return false;
}
}
console.log(SubType.prototype.constructor,"/////////");//结果:Object(){}
let instance2 = new SubType();//SubType的实例
console.log(instance2.getsubValue(),instance2.constructor)//结果:false,Object(){}
1. superType.prototype.getsuperValue = function () { };
是在构造函数的prototype属性指向的原型对象上添加方法,这样并不是重写构造函数的原型对象,不会改变原构造函数原型对象的默认constructor
属性,所以打印的superType.prototype.constructor
的结果是superType(){}。
2.SubType.prototype = new SubType()
,SubType的prototype原型对象是superType实例,在这个过程中,原来的constructor
属性被重新指向了superType构造函数,所以打印的结果会是 superType(){}
。
3.SubType.prototype = { ... }
是在重新给subType的原型对象写一个新的函数对象,而这个新的函数对象是基于Object建立的,所以此时的原型对象constructor不会指向subType,而是指向Object()
。
所以,在上面的解释下,我首先画出来一个关系图:
回到老师给我布置的课后作业中,我们看看问题在哪?
从图中我们可以看出,instance实例是在subType的原型对象重写之后,是没有办法找到superType的原型对象上的方法的,所以打印会出错。
如果使用的是如图这种方式,不重写subType
的原型对象,就可以通过它的原型链访问getsuperValue()
方法。
通过上面这个让我折磨到半夜的小作业,我突然明白了:在学习过程中还是要不断深入挖掘一下知识点之间的联系,也许我们自认为的学会了的知识,在一些时候突然就卡壳讲不出来为什么,那就说明我们学的并不扎实。😟
在这种关系中,我们要善用console.log()
去进行打印出想要的结果,说不定在一遍遍的打印结果中,你又能发现更多奥秘😊!
喜欢这篇文章的话,就给作者留下一个小赞吧~❤
转载自:https://juejin.cn/post/7210225864356806715