深入理解JavaScript原型链:从构造函数到原型对象
本文深入解析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()的结果。
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
