关于JS的prototype和constructor

本文深入剖析JavaScript中prototype和constructor的概念,通过实例演示它们之间的关系及如何在代码中正确应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从学校出来四年多了,四年来一直在公司里是个酱油的角色...


关于prototype和constructor,以前一直搞不清楚,云里雾里的,就算是在网上看了很多视频,看了很多一些牛人写的分析,但还是觉得有些东西被他们带过了,并没有分析清楚,而且有些方法名较长,先是从最简单的说起,越往后越难,东西也越深,逻辑也越混乱,以至于前面看的以为自己好像明白了,可是后面只给你大段代码,然后告诉你,这段代码你看懂了,你就明白了,可偏偏就是后面没有解释的代码看不懂,唉,到这里只能承认自己的理解能力跟这些大牛有代沟了。

以下就我对prototype和constructor的认识写点东西吧,以便一直搞不清白的初学者和以后的学懵了的自己便于理解。

废话不多说了,从最简单的开始。

function A(){
	this.name = 'named A';
}

var a = new A();

alert(a.prototype);   //返回 undefined
alert(A.prototype);   //返回 [object Object]
alert(a.constructor);  //返回  function A()......
alert(A.prototype.constructor);   //返回  function A()......

这是一个最简单的函数了,如果要生成它的对象,就new A(),

从上面的实验可以说明几点问题。

1、new出来的A的对象a是没有prototype属性的,而这个方Function函数A有这个prototype属性。(从前面二个alert的结果可以看出来,如果这都看不明白,那么真该去补补JS的基础了)。


2、对象a和函数A的prototype属性都有constructor(构造函数),并且是同一个函数,都指向function A()......


3、函数A的prototype指向了另一个对象(我们称他对A的原型对象),而这个对象的构造函数又指向了函数A,这里比较绕,画个图看看。



其实在new A()生成a对象时,会经过三步

1、建立一个新的对象 a

2、把对象的a的内置原型对象设置为 函数A的prototype属性引用的那个对象(即是把对象a 的内置原型对象设置为 函数A的原型对象,这里不明白先不用管,往下看)

3、将该对象作为this,传入A中,完成初始化


在第二步里面,出现了一个新词置原型对象,这里的置原型对象跟上图的函数A的原型对象不是同一个对象。

这里我们暂且就这么叫,看下图:



这里再分析一段代码:

function A(){
	this.name = 'named A';
}

A.prototype.Method = function(){
	alert('This is Method A')
}

var a = new A();

a.Method(); //返回 This is Method A
我给函数A的原型对象上加了一个Method 方法,依照之前所说,a对象里面的内置原型对象是指向这个函数A的原型对象的,那么这个a对象也应该可以使用这个Method方法。

不过这里会有个疑问,就是内置原型对象是a对象里面的对象,那么调用它的方法应该是这样啊,a.内置原型对象.Method(),怎么是a.Method(),这个我开始也没弄明白,不过后来明白了它是__proto__,你可以用a.__proto__===A.prototype(===表示绝对相等,包括值和类型),结果为true,每个对象其内部都会有一个__proto__,当我们访问一个对象的属性 时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。按照标准,__proto__是不对外公开的,也就是说是个私有属性,但是Firefox可以追踪到它


我们继续看代码:

function A(){
	this.name = 'named A';
}

function B(){
	this.age = 11;
}

A.prototype = new B();

var a = new A();

alert(a.name); //返回 named A
alert(a.age);  //返回 11

如果上面你都明白了,其实这里也好理解,就是我们把上面那个图中的  函数A的原型对象,给换成了B的一个对象了,因为a是能访问函数A的原型对象,所以这里的a也能访问B对象里的对象。

结果是否如此呢,我们看另一个简单的代码(只是在上面的代码里加了三段,应该还算简单吧):

function A(){
	this.name = 'named A';
}

function B(){
	this.age = 11;
}

A.prototype = new B();

A.prototype.isExists = function(){
	return '存在这个方法';
}
var a = new A();
var b = new B();
alert(a.name); //返回 named A
alert(a.age);  //返回 11
alert(a.constructor); //返回 function B() ...
alert(a.isExists()); //返回 存在这个方法
alert(b.isExists()); //TypeError: b.isExists is not a function


仔细看前二个图,不对呀!a的构造函数应该是指向function A(),这里怎么变成function B()了,而且刚才不是说A的原型对象换成B了么,那么第11行代码不就是相当于给B的对象加上了一个isExists方法(像这样 ,B.isExists = function(){......}),为什么最后一行又说b对象的isExists方法不存在,这不是在耍我们呢,越搞越糊涂了。

其实这里有个错误的认识和一个错误的逻辑导致了跟我们预期的结果不一样。

通过上面的例子可以看出,a中是没有constructor的,而之前之所以能用a.constructor得到返回结果function A()......全是因为a里面的那个看不见的内置原型对象,它是指向了函数A的原型对象的,而函数A的原型对象中有A的构造方法constructor,对象a能访问到函数A的原型对象中所有的方法属性,也必然能访问到它的构造方法。

代码第9行A.prototype = new B(),由于这行代码的出现,把a里面的内置原型对象的指向换掉了,换成了B的一个对象,那么由于a.constructor原来是访问A原型对象中的constructor,现在改为了访问B对象的构造方法,所以导致了a.constructor的结果返回为function B()......,

即然如此,那么根据第11行代码不就相当于B.isExists = function(){......},为什么又说这个方法不存在,其实这是一个错误的逻辑问题,一个很多新手包括老手都会看走眼的问题。

问题出在哪呢?

仔细看第15行,这个b是后来new 出来的,这个b是原原本本的B的一个对象,没有做过任何改变的,因为B.prototype是没有受到任何改动的,所以b中也就不存在isExists这个方法了,如果把代码改成下面这样,那么才是我们想看到的结果:

function A(){
	this.name = 'named A';
}

function B(){
	this.age = 11;
}
var b = new B();
A.prototype = b;

A.prototype.isExists = function(){
	return '存在这个方法';
}
var a = new A();

alert(a.name); //返回 named A
alert(a.age);  //返回 11
alert(a.constructor); //返回 function B() ...
alert(a.isExists()); //返回 存在这个方法
alert(b.isExists()); //返回 存在这个方法
那么它的图应该是这样子的:



 为了验证之前所说是正确的,我们在后面加三段代码(也很简单):

function A(){
	this.name = 'named A';
}

function B(){
	this.age = 11;
}
var b = new B();
A.prototype = b;

A.prototype.isExists = function(){
	return '存在这个方法';
}
var a = new A();

alert(a.name); //返回 named A
alert(a.age);  //返回 11
alert(a.constructor); //返回 function B() ...
alert(a.isExists()); //返回 存在这个方法
alert(b.isExists()); //返回 存在这个方法

A.prototype.constructor = A;
alert(a.constructor);    //返回  function A()...
alert(b.constructor);    //返回  function A()...

如果前面都看明白了的话,这里的问题也就不大了

其实看到这里A.prototype.constructor = A;就知道23行的结果了

24行之所以b对象构造函数也为function A()...是因为第22行中的代码相当于b.constructor = A,已给它赋了值,注意理解第8,9行代码。


最后一点,如果在上面的function A()...后面加上这么一段,A.prototype.name="other";那么这时的a.name值是什么呢?

这里的值应该是named A,这是因为对象找他的属性和方法时,会先在他本身的函数中寻找,如果找不到,才会去它的内置对象__proto__中找,很显然,A中已经定义了name,所以A.prototype.name="other";这样再定义一次name的值是无效的。


如果以上你都明白了,说明你真懂了。

如果转载,请别忘了带上原文出处哦,亲。http://blog.youkuaiyun.com/li64591505/article/details/20038527



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值