JavaScript原型、原型链与继承机制深度解析(含高频面试题)

JavaScript 是一门基于原型(Prototype-based)的面向对象编程语言,不同于 Java、C++ 等基于类的面向对象语言,JS 没有真正的类与实例的静态编译关系,其对象复用、属性查找、代码复用、层级复用完全依托原型与原型链机制实现。

原型、prototype__proto__、原型链、构造函数、继承体系、Function 与 Object 底层关系,是 JS 面向对象体系的底层核心,也是前端进阶、源码阅读、框架原理、面试必考的核心知识体系。本文将从底层原理出发,系统性拆解原型机制、六种经典继承方案、顶层构造关系,并配套高频面试真题,实现理论与实战一体化掌握。

一、JavaScript原型核心基础体系

1.1 原型的核心定义

在 JavaScript 中,每个对象都拥有一个内置原型对象。对象可以自动访问原型上的属性与方法,这种机制称为原型委托。原型的核心设计目的是:实现属性与方法的共享复用,减少内存冗余,实现对象的层级继承

JS 中所有引用类型(对象、数组、函数、正则、日期)均基于原型体系构建,所有内置方法(toStringhasOwnPropertypush 等)全部挂载在原型对象上,而非实例自身。

1.2 prototype 与 __proto__ 核心区分

原型体系存在两个极易混淆的核心属性,二者是原型链构建的核心,职责完全不同。

1.2.1 prototype(显式原型)

prototype构造函数专属属性,仅函数拥有。

1、作用:存储实例共享的属性与方法,供所有实例对象委托访问;

2、属性归属:只有函数具备 prototype,普通对象没有;

3、核心价值:实现实例之间的方法共享,节省内存。

1.2.2 __proto__(隐式原型)

__proto__所有对象专属属性(包含函数对象、数组对象、普通对象)。

1、作用:指向创建当前对象的构造函数的 prototype,用于构建原型链查找关系

2、属性归属:所有引用类型对象都拥有;

3、规范说明:__proto__ 是非标准废弃访问器属性,现代开发推荐使用 Object.getPrototypeOf() / Object.setPrototypeOf() 替代。

1.3 核心对应关系公式(底层核心)

// 核心恒等式
实例对象.__proto__ === 构造函数.prototype

// 示例
function Person(){}
const p = new Person();
console.log(p.__proto__ === Person.prototype); // true

1.4 constructor 构造器属性

每个构造函数的 prototype 上默认自带 constructor 属性,指向当前构造函数本身,用于标记实例的构造来源。

console.log(Person.prototype.constructor === Person); // true
console.log(p.constructor === Person); // true

在继承过程中,constructor 属性极易丢失,是继承方案优化的关键细节。

二、原型链运行机制

2.1 原型链定义

当对象的 __proto__ 逐级向上指向原型对象,形成的层级查找链条,即为原型链。原型链是 JS 实现属性查找、继承复用的唯一底层机制。

2.2 原型链查找规则

1、访问对象属性/方法时,优先查找对象自身

2、自身不存在则通过 __proto__ 向上查找原型;

3、逐级向上查找,直至 Object.prototype

4、Object.prototype.__proto__ === null,为原型链的顶层终点;

5、查找终点仍未找到属性,返回 undefined

2.3 原型链顶层结构

所有普通对象最终都继承自 Object.prototype,所有函数最终继承自 Function.prototype,构成 JS 完整的原型顶层闭环。

三、Function 与 Object 终极底层关系(重难点)

Function 和 Object 的互成闭环关系,是 JS 原型体系的终极考点,也是绝大多数开发者的知识盲区。

3.1 基础关系梳理

1、Object 是所有对象的祖宗:所有引用类型最终都继承自 Object.prototype;

2、Function 是所有函数的祖宗:所有函数(包含内置函数、自定义函数)都继承自 Function.prototype;

3、函数本身也是对象,因此函数既属于 Function 实例,也属于 Object 体系。

3.2 核心闭环公式

// 1. 所有函数 __proto__ 指向 Function.prototype
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true

// 2. 所有原型对象都是普通对象,继承自 Object.prototype
console.log(Function.prototype.__proto__ === Object.prototype); // true

// 3. 顶层终点
console.log(Object.prototype.__proto__ === null); // true

3.3 关系总结

1、Function 构造了所有函数,包括 Object、Function 自身;

2、Object 构造了所有对象,包括所有原型对象;

3、Function 源于自身,Function.prototype 源于 Object,形成完美闭环。

四、JavaScript 六种经典继承方式(完整演进)

JS 没有原生类继承,所有继承均基于原型链模拟实现。从 ES5 到 ES6 共演进出六种标准继承方案,各有优劣、适配不同场景,是面试与工程化高频考点。

4.1 原型链继承(最基础)

原理

将子类原型直接指向父类实例,实现原型链挂靠。

function Parent(name) {
    this.name = name;
    this.arr = [1,2,3];
}
Parent.prototype.say = function(){ console.log(this.name); }

function Child(){}
// 核心继承代码
Child.prototype = new Parent();

const c1 = new Child();
console.log(c1.name);

缺陷

1、父类引用类型属性被所有实例共享,一个实例修改数组,所有实例同步变更;

2、子类无法向父类传参;

3、丢失 constructor 指向。

4.2 构造函数继承(借用父构造)

原理

在子类构造函数中通过 call/apply 借用父类构造函数,复制父类实例属性。

function Parent(name) {
    this.name = name;
    this.arr = [1,2,3];
}
function Child(name) {
    Parent.call(this, name); // 核心
}

const c1 = new Child("张三");
const c2 = new Child("李四");

优点

解决引用类型共享问题、支持子类传参。

缺陷

只能继承实例属性,无法继承原型方法,造成方法无法复用。

4.3 组合继承(原型链+构造函数,ES5经典)

原理

结合前两种方式:构造函数继承实例属性,原型链继承原型方法。

function Parent(name) {
    this.name = name;
    this.arr = [1,2,3];
}
Parent.prototype.say = function(){console.log(this.name)}

function Child(name) {
    Parent.call(this, name); // 继承属性
}
Child.prototype = new Parent(); // 继承方法
Child.prototype.constructor = Child; // 修复构造器指向

缺陷

父类构造函数执行两次,造成原型上存在一份冗余属性。

4.4 原型式继承(Object.create)

原理

依托 Object.create() 创建一个空对象,挂靠指定原型。适用于单纯对象继承。

const parent = {name:"父类"};
const child = Object.create(parent);

缺陷

依然存在引用属性共享问题,无法实现严格的类继承。

4.5 寄生式继承

原理

基于原型式继承,封装一层增强函数,为新对象扩展方法。

缺陷:无法复用函数,每次继承都会创建新方法,内存冗余。

4.6 寄生组合继承(ES5 最优继承)

原理

通过 Object.create 直接继承父类原型,不执行父类构造函数,彻底解决两次执行问题,是ES5 最完美继承方案

function Parent(name){
    this.name = name;
}
Parent.prototype.say = function(){}

function Child(name){
    Parent.call(this, name);
}
// 核心最优继承
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

4.7 ES6 Class extends 继承(语法糖)

ES6 class 继承是寄生组合继承的语法糖,底层完全基于原型机制实现,通过 super 实现父构造调用,是现代开发标准继承方案。

class Parent {
    constructor(name){
        this.name = name;
    }
    say(){}
}
class Child extends Parent {
    constructor(name){
        super(name);
    }
}

五、原型体系扩展特性与易错点

5.1 原型属性遮蔽

当实例自身属性与原型属性重名时,自身属性优先遮蔽原型属性,原型属性不会被修改。

5.2 hasOwnProperty 属性判断

hasOwnProperty() 仅判断自身属性,不遍历原型链,可用于区分自身属性与继承属性。

5.3 原型动态性

原型对象的修改会实时同步给所有实例,原型链查找是动态实时的。

六、高频面试真题解析(原型与继承专属)

真题一:prototype 与 __proto__ 区别(口述必考)

标准答案

1、prototype构造函数独有的显式原型,用于存放实例共享方法;

2、__proto__所有对象独有的隐式原型,用于指向构造函数的 prototype,构建原型链;

3、核心等式:实例.__proto__ === 构造函数.prototype

4、__proto__ 非标准,推荐使用 Object.getPrototypeOf

真题二:原型链顶层关系判断题

题目:判断以下代码输出

console.log(Function.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__);

答案:true、true、null

解析:Function 自构造,所有函数原型继承 Object,Object 原型为顶层终点。

真题三:原型链继承引用类型共享坑

题目:写出代码输出

function Parent(){
    this.arr = [1,2,3];
}
Parent.prototype.name = "父类";
function Child(){}
Child.prototype = new Parent();

const c1 = new Child();
const c2 = new Child();
c1.arr.push(4);
console.log(c2.arr);

答案[1,2,3,4]

解析:原型链继承中,所有实例共享父类引用属性,一个修改全部生效,是原型链继承的致命缺陷。

真题四:六种继承方式优缺点总结(背诵题)

标准面试总结

1、原型链继承:共享引用属性、无法传参;

2、构造函数继承:可传参、隔离引用,无法继承原型方法;

3、组合继承:功能齐全,父类执行两次存在冗余;

4、原型式/寄生式继承:适合对象继承,不适合类继承;

5、寄生组合继承:ES5 最优,无冗余、无共享缺陷;

6、ES6 extends:语法简洁、底层寄生组合、工程化首选。

真题五:属性遮蔽与原型查找

题目:分析输出结果

function Person(){}
Person.prototype.a = 10;
const p = new Person();
p.a = 20;
console.log(p.a);

答案:20

解析:自身属性优先级高于原型属性,发生属性遮蔽,不会修改原型值。

真题六:instanceof 底层原理

面试题:简述 instanceof 原理

答案:遍历实例的原型链,判断构造函数的 prototype 是否出现在实例的原型链上,存在返回 true,否则 false。

七、全文核心终极总结(可直接背诵)

1、原型核心:prototype 供函数存共享方法,__proto__ 构建原型链;

2、原型链机制:由内向外逐级查找,终点为 Object.prototype.__proto__ = null;

3、Function与Object关系:Function 构造所有函数,Object 构造所有对象,形成顶层闭环;

4、继承演进核心:从共享缺陷 → 无法继承方法 → 冗余执行 → 寄生组合最优;

5、现代规范:ES6 extends 是寄生组合继承语法糖,为工程化首选;

6、高频坑点:原型引用属性共享、constructor 丢失、原型链动态查找、属性遮蔽。

后浪云移动端信息流广告 后浪云主机服务 适合长期部署、独立站和海外机房需求。