JavaScript 原型链机制解析:从构造函数到继承

作者:青柠

时间:2026-10-25

浏览:198

本文深入解析 JavaScript 对象模型,通过构造函数创建对象的过程,阐明 __proto__ 与 prototype 的关系。文章详细推导原型链的形成机制,解释属性查找规则及 Object.prototype 作为基类的作用,揭示 JavaScript 基于原型的继承本质。

文章目录

引言:对象创建问题

文章从一个问题引入讨论。



代码示例如下: function Base(){} var base = new Base()


上述两行代码执行后,究竟创建了几个对象(object)?



要准确回答这个问题,首先需要明确 JavaScript 中 object 的定义与概念。




JavaScript 对象与类型基础

关于 Objects



在 JavaScript 中,几乎所有事物都是对象,包括 Arrays、Functions、Numbers 以及 Objects 本身。JavaScript 没有类似 C# 中的 class 概念。对象的本质是一个名称-值对的集合,其中名称(name)必须是字符串类型,通常被称为“属性”(property);而值(value)可以是各种类型的对象,如 string、number、boolean、array 或 function 等,这些值即为属性的具体取值。




关于 typeof



既然对象包含数组、函数、数字等多种类型,如何区分它们呢?答案是使用 typeof 操作符。typeof 返回一个字符串,例如 typeof(null) 返回 “object”,typeof(false) 返回 “Boolean”,typeof(1) 返回 “number”。由于 typeof 始终返回字符串,因此对于 typeof(typeof x),无论 x 是什么类型,结果总是 “string”。



引言:对象创建问题

构造器与 Function 的创建机制

关于 Constructor

JavaScript 中没有 class,因此也没有传统意义上的类构造函数。那么对象是如何创建的呢?答案是使用构造器(constructor)。构造器实际上就是 Function,因此它本身也是一个 object。开头的 function Base(){} 就是一个构造器,而 var base = new Base() 则是利用这个构造器(通过关键字 new)创建了一个名为 base 的 object。至此可以得出结论:开头的两行代码至少创建了两个 object:一个是 Base,类型为 function 的 object;另一个是 base,类型为 object 的 object。

 

关于 Function() 和 Object()

这是两个重要的预定义构造器。所有的 function(例如开头的 Base())都是由 Function() 构造出来的;而 Object 的 prototype 将被所有 object 继承,这一点将在后文详细讲解。

    引言:对象创建问题

 

Function 的创建过程

当执行 function Base(){this.a = 1} 时,其效果等同于 var Base = new Function(“this.a = 1”)。也就是说,这行代码本身使用了预定义好的 Function() constructor 来构造一个 function 类型的 object(即 Base)。在这个创建过程中,JavaScript 引擎会执行哪些操作呢?

1. 首先,引擎会创建一个 object,Base 指向这个 object。此时 typeof 这个 object 的结果为 “function”。 引言:对象创建问题 2. 给 Base 附上 __proto__ 属性,使其等于 Function 这个构造器的 prototype(这也是预定义好的)。这是非常关键且具规律性的一步。(规律:)在执行任意类似 var x = new X() 时,都会把 X 的 prototype 赋给 x 的 __proto__,也就是说,此时 x.__proto__ 和 X.prototype 会指向同一个对象。

引言:对象创建问题 3. 为 Base 创建 call 属性,该属性是一个 function。因此我们可以这样调用:Base.Call()

引言:对象创建问题 4. 为 Base 创建 Construct 属性,该属性也是一个 function。在执行 var base = new Base() 时,就会调用这个 Construct 属性。 5. 为 Base 创建 Scope、Length 等属性,此处略过。 6. 为 Base 创建 prototype 属性:先用 new Object() 创建一个对象,为该对象创建一个属性叫 constructor,该属性值设置为 Base。再把 Base 的 prototype 设置为这个新创建的对象。伪代码如下:

var x = new Object();
x.constructor = Base;
Base.prototype = x;

 

现在我们将关注点集中在第 2 步和第 6 步。

 

__proto__ 与 prototype 的关系

__proto__ 和 prototype

从第 2 步可以看出,任意一个用构造器构造出来的 object(包括 Objects 和 Functions),都会有 __proto__ 属性,指向该构造器的 prototype 属性。需要注意的是,__proto__ 是一个私有属性,在 IE 浏览器上不可见,但在 Chrome 中可以查看到。

从第 6 步可以看出,任意一个用 new Function() 构造出来的 functions,都会有 prototype 属性。该属性是用 new Object() 构建出来的,其初始公开属性只有一个 constructor。

    引言:对象创建问题

 

原型链的形成与属性查找

原型链

接下来分析第 6 步的伪代码,即为 function 创建 prototype 的步骤:

var x = new Object(); // 参见2中的规律,会有x.__proto__= Object.prototype。
x.constructor = Base;
Base.prototype = x;

此时我们用 Base() 构造一个对象出来:

var base= new Base(); // 参见2中的规律,会有base.__proto__ = Base.prototype,也就是 = x。
  // 因此有base.__proto__.__proto__ = x.__proto__
// 而x.__proto__ = Object.prototype(见上一个代码片段)  
// 所以,base.__proto__.__proto__ = Object.prototype.

__proto__.__proto__,这就是传说中 JavaScript 对象的原型链!由于用 Function() 创建构造器时的关键第 6 步,保证了所有 object 的原型链的顶端,最终都指向了 Object.prototype。

    引言:对象创建问题

 

Property Lookup(属性查找)

如果我们尝试读取某个 object 的某个属性,JavaScript 会如何处理呢?

假设有一个 object 叫 xxx,我们执行 alert(xxx.a),即读取 xxx 的 a 属性。JavaScript 首先会在 xxx 本身寻找 a 属性,如果没找到,则到 xxx.__proto__ 里寻找 a 属性,并沿着原型链向上查找,找到即返回(如果没找到,则返回 undefined)。可以来看个例子:

    引言:对象创建问题

从上图得知:base 本身是没有 constructor 属性的,但是 base.constructor 确实能返回 Base 这个函数。原因在于 base.__proto__ 有这个属性。(base.__proto__ 是什么?就是 Base.prototype,即在上面构建 Function 的第 6 步伪代码中,为 Base.prototype.constructor 赋值为 Base 本身。)

 

Object 基类与原型继承

Object 作为“基类”

另外,由于任意 object 的原型链的顶端都是 Object.prototype。因此,Object.prototype 里定义的属性,就会通过原型链被所有 object 继承下来。这样,预定义好的 Object 就成了所有对象的“基类”。这就是原型链的继承机制。

    引言:对象创建问题

看上图,Object.prototype 已经预定义好了一些属性。如果我们再追加一条属性叫 propertyA,那么这个属性和预定义属性一样,都可以从 base 上读到。

 

原型继承

我们已经得知:

对于 var xxx = new Object(); 有 xxx.__proto__ = Object.prototype;

对于 var xxx = new Base(); 有 xxx.__proto__.__proto__ = Object.prototype;

这看起来像什么呢?从 C# 的角度看,很像 Base 是 Object 的子类。也就是说,由 Base() 构造出来的 object,比由 Object() 构造出来的 object,在原型链上低一个层级。这是通过把 Base.prototype 指向由 Object() 创建的对象来做到的。那么自然而然,如果我想定义一个继承自 Base 的构造器,只需将该构造器的 prototype 指向一个 Base() 构造出来的对象。

function Derived(){}
var base = new Base();
Derived.prototype = base;
var d = newDerived(); //很容易推算出:d.__proto__.__proto__.__proto__ = Object.prototype.

推算过程:d.__proto__ 指向 Derived.prototype,也就是 base;则 __proto__.__proto__ 指向 base.__proto__,也就是 Base.prototype,也就是某个 new object() 创建出来的对象,假设是 o;则 __proto__.__proto__.__proto__ 指向 o.__proto__,也就是 Object.prototype。

 

结论与延伸问题

回答开头的问题,以及几个新的问题

那两行代码至少创建了三个对象:Base、base、Base.prototype。顺便说明,base 是没有 prototype 属性的,只有 function 类型的 object,在被构建时才会被创建 prototype 属性。

    引言:对象创建问题

 

d.constructor 会返回什么呢?

构造器 Base() 和 Derived() 里都是空的,如果有代码,将会怎么被执行呢?

……

待续。见下篇

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章