深入理解JavaScript原型链:从构造函数到原型对象

作者:闭包旅人

时间:2026-10-27

浏览:291

本文深入解析JavaScript中的原型链机制。通过剖析函数作为构造函数的特性,详细阐述prototype指针与__proto__引用的关系,解释属性查找的“顺藤摸瓜”过程。结合jQuery源码示例,说明如何通过原型链实现对象继承及无new关键字的对象创建,帮助开发者彻底理解JS面向对象编程的核心原理。

文章目录

JavaScript函数与原型的基础关系

要深入理解原型链,首先必须厘清函数(function)类型的本质。在JavaScript语言体系中,并不存在传统面向对象语言中的“类”概念,一切皆函数,因此它被视为一门函数式编程语言。在其它语言中,类具备通过构造函数创建基于该模板对象的重要特性;而在JavaScript中,函数同样具备这两种核心功能。



第一种功能是作为普通函数进行调用。


第二种功能是作为其原型对象的构造函数,即配合new操作符使用。



接下来通过具体示例进行说明。


示例代码如下:

 
function a(){
this.name = 'a';
} 

当定义一个函数时,JavaScript引擎内部会执行以下操作:



首先,创建一个函数对象本身,即变量a所指向的对象。



其次,创建一个原型对象,记为@a(此处用@符号标识)。



接着,函数对象a内部会生成一个prototype指针,该指针指向对应的原型对象@a。



最后,原型对象@a内部包含一个constructor指针,指向其构造函数,即指向a。



http://img.blog.csdn.net/20140222125611500?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGpsMTU3MDEx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast



那么,prototype属性具体起到什么作用?



prototype属性标识了当前函数能够控制的范围,或者说指明了当前函数是哪个对象的构造函数。例如,a是@a原型对象的构造函数,因此我们常看到如下写法:



代码如下:

 
function a(){
this.name = 'a';
} 
var a1 = new a();


这与常见面向对象语言的行为相似。new操作符的作用是通过prototype指针找到原型对象,再调用其中的constructor构造函数,从而创建一个新的对象实例。



如果修改了prototype指向对象内部的属性,所有基于该模板创建的实例都会受到影响。我们可以通过以下代码验证这一现象:



代码如下:

 
function a(){
this.name = 'a';
} 
var a1 = new a();
a.prototype.age = 1;
alert(a1.age);

运行结果为:1



既然a1对象内部并未定义age属性,为何能直接访问到它?



原因在于所有实例对象内部都包含一个引用__proto__(在Firefox和Chrome中可直接访问,IE不支持),该引用指向原型对象,即上文提到的@a。


验证代码如下:

 
function a(){
this.name = 'a';
} 
var a1 = new a();
alert(a1.__proto__ == a.prototype)

运行结果为:true



在访问属性时,JavaScript引擎会先在a1对象内部查找;若未找到,则顺着__proto__指向的对象(即@a)继续查找。找到则返回值,未找到则返回undefined。这个过程形象地说是“顺藤摸瓜”。



原型链的形成与属性查找机制

至此,原型链的概念便清晰了。由于原型对象自身也有一个__proto__指针,指向另一个原型对象,如此层层连接,便形成了原型链。Object.prototype位于原型链的最顶层,因此修改Object.prototype的属性会影响所有对象。



基于原型链的对象继承实现

再来看一段涉及继承的代码示例:


代码如下:

 
function a(){
this.name = 'a';
} 
function b(){
this.age = 1;
} 
b.prototype = new a();
alert(new b().name);

我们将b的原型显式指向了a的一个实例,使得b的实例也能访问a的属性。这就是JavaScript中的继承机制。为何b.prototype指向的是a的实例,而非直接指向a.prototype?


假设代码如下:

 
b.prototype = new a.prototype;

若按上述写法,修改b.prototype中的属性会导致a的原型随之改变,相当于子类修改了父类,且子类和父类的属性混杂在一起,这显然不合理。换言之,b也会成为@a的构造函数,a和b将处于平级关系。



我们可以得出如下定义:



函数a继承函数b,即让函数a成为函数b原型的一个实例的构造函数。构造函数内部声明的属性属于函数a自身,而原型实例中的属性则是从b继承而来的。


jQuery源码中的原型链应用

jQuery源码示例如下:

 
var $ = jQuery = function(selector,context){
//不可能在自己的构造函数中又一次构造自己,所以返回了另外一个构造函数的实例
return new init(selector,context);
} 
jQuery.fn = jQuery.prototype = {
size:function(){
return this.length;
} 
} 
function init (selector,context){
} 
init.prototype = jQuery.fn;;
} 

这是jQuery的一段源码。在使用jQuery时,我们并未使用new关键字,它是如何构造对象的?



利用前述知识可以解释:jQuery这里仅作为普通函数调用,它返回了由jQuery原型的另一个构造函数(init)创建的对象,即执行new init()的结果。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章