news 2026/4/16 12:44:03

js怎样控制浏览器前进、后退、页面跳转?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
js怎样控制浏览器前进、后退、页面跳转?

JavaScript 原型与继承终极指南:从原理到实战(2025 版)

原型与继承是 JavaScript 的核心灵魂,也是前端面试的 “高频重灾区”。很多开发者深陷 “原型链迷宫”,仅停留在 “__proto__指向原型对象” 的表层认知,却不懂其底层设计逻辑与实战应用。本文从 “内存模型→核心概念→继承实现→框架应用→避坑指南” 五层逻辑,结合 V8 引擎执行机制和 React/Vue 实战案例,彻底拆解原型与继承的本质,帮你真正掌握 JavaScript 的面向对象编程思想。

一、底层原理:为什么 JavaScript 没有 “类” 却能实现继承?

JavaScript 是一门 “基于原型的语言”,而非传统面向对象的 “基于类的语言”。这一设计源于 Brendan Eich(JS 创始人)的初衷 —— 在极短时间内设计一门兼具函数式和面向对象特性的语言,原型机制正是折中后的最优解。

1. 核心设计思想:原型链继承

  • 本质:通过 “原型对象”(prototype)实现属性和方法的共享,通过 “原型链”(__proto__串联)实现属性的查找与继承。
  • 核心逻辑:每个对象都有一个 “原型对象”,当访问对象的属性 / 方法时,若对象本身没有,会通过__proto__向上查找原型对象,直到找到或抵达原型链顶端(Object.prototype.__proto__ = null)。

2. V8 引擎视角的原型内存模型

V8 引擎中,原型相关的三个核心对象构成了继承的基础,其内存关系如下(Mermaid 流程图直观展示):

proto

prototype

constructor

proto

proto

属性name

方法sayHello

实例对象obj

原型对象Foo.prototype

构造函数Foo

Object.prototype

null

张三

console.log(`Hello, ${this.name}`)

proto

prototype

constructor

proto

proto

属性name

方法sayHello

实例对象obj

原型对象Foo.prototype

构造函数Foo

Object.prototype

null

张三

console.log(`Hello, ${this.name}`)

豆包

你的 AI 助手,助力每日工作学习

  • 关键关联
    1. 实例对象的__proto__指向其构造函数的prototype(原型对象);
    2. 原型对象的constructor指向其对应的构造函数;
    3. 所有原型对象最终继承自Object.prototype,形成完整的原型链。

3. 原型与构造函数的 “三角关系”(必懂)

这是理解原型机制的核心,三者相互关联、不可分割:

对象 / 函数核心属性 / 方法作用
构造函数(如 Foo)prototype属性指向原型对象,供实例继承属性 / 方法
实例对象(如 obj)__proto__属性(隐式原型)指向构造函数的prototype,开启原型链查找
原型对象(Foo.prototype)constructor属性指向构造函数,标识原型所属的构造函数

验证代码(浏览器控制台可直接执行):

javascript

运行

// 1. 定义构造函数 function Foo(name) { this.name = name; // 实例属性(每个实例独立) } // 2. 原型对象上定义共享方法 Foo.prototype.sayHello = function() { console.log(`Hello, ${this.name}`); }; // 3. 创建实例 const obj = new Foo("张三"); // 验证三角关系 console.log(obj.__proto__ === Foo.prototype); // true(实例→原型对象) console.log(Foo.prototype.constructor === Foo); // true(原型对象→构造函数) console.log(obj.constructor === Foo); // true(实例通过原型链继承constructor)

二、核心概念:彻底分清 prototype、__proto__与 constructor

原型机制的混淆,本质是对这三个核心概念的理解模糊。以下从 “定义、作用、使用场景” 三个维度彻底拆解:

1. prototype(原型属性)

  • 定义:仅函数(构造函数)拥有的属性,指向一个 “原型对象”。
  • 作用:存储共享的属性和方法,供其创建的所有实例继承(避免每个实例重复创建相同方法,节省内存)。
  • 注意点
    • 普通对象没有prototype属性(如const obj = {}; obj.prototype → undefined);
    • 箭头函数没有prototype属性(无法作为构造函数使用)。

示例:共享方法的实现(核心用途)

javascript

运行

// 错误做法:每个实例都创建独立方法(浪费内存) function Bar(name) { this.name = name; this.sayHi = function() { // 实例方法,每个实例一份 console.log(`Hi, ${this.name}`); }; } // 正确做法:原型上定义共享方法(所有实例共用一份) function Bar(name) { this.name = name; } Bar.prototype.sayHi = function() { // 原型方法,所有实例共享 console.log(`Hi, ${this.name}`); }; const bar1 = new Bar("李四"); const bar2 = new Bar("王五"); console.log(bar1.sayHi === bar2.sayHi); // true(方法共享,内存优化)

2.proto(隐式原型)

  • 定义:所有对象(包括函数)都拥有的隐式属性(ES6 标准化后可通过Object.getPrototypeOf()访问),指向其 “原型对象”。
  • 作用:构建原型链,实现属性 / 方法的查找与继承。
  • 注意点
    • 不建议直接修改__proto__(性能差,易导致原型链混乱),优先使用Object.create()指定原型;
    • __proto__是实例对象与原型对象的 “桥梁”,而非构造函数的属性。

示例:原型链查找机制

javascript

运行

// 原型链:obj → Foo.prototype → Object.prototype → null const obj = new Foo("张三"); console.log(obj.name); // "张三"(实例本身拥有) console.log(obj.sayHello()); // "Hello, 张三"(Foo.prototype拥有) console.log(obj.toString()); // "[object Object]"(Object.prototype拥有) console.log(obj.nonExistent); // undefined(原型链顶端仍未找到)

3. constructor(构造函数指针)

  • 定义:原型对象上的属性,指向其对应的构造函数。
  • 作用:标识对象的 “创建者”,可通过实例对象的constructor获取其构造函数。
  • 注意点
    • 若重写原型对象,会覆盖constructor属性,需手动修复(否则指向错误);
    • 实例对象的constructor是通过原型链继承自原型对象的。

示例:constructor 的修复场景

javascript

运行

function Person(age) { this.age = age; } // 重写原型对象(覆盖默认constructor) Person.prototype = { run: function() { console.log(`跑步,年龄${this.age}`); } }; const p = new Person(25); console.log(p.constructor); // Object(错误,应为Person) // 手动修复constructor Person.prototype.constructor = Person; console.log(p.constructor); // Person(正确)

三、JavaScript 的 6 种继承方式:从基础到进阶

JavaScript 没有原生的 “类继承” 语法(ES6 的class本质是原型继承的语法糖),但通过原型机制可实现多种继承方式,不同方式各有优劣,需根据场景选型。

1. 原型链继承(基础方式)

  • 实现原理:让子类的原型对象指向父类的实例,通过原型链继承父类的属性和方法。
  • 实战代码

javascript

运行

// 父类:Animal function Animal(type) { this.type = type; // 实例属性 this.eat = function() { // 实例方法(会被所有子类实例共享吗?不!) console.log(`${this.type}在吃东西`); }; } // 父类原型方法(共享) Animal.prototype.sleep = function() { console.log(`${this.type}在睡觉`); }; // 子类:Dog(继承Animal) function Dog(name) { this.name = name; } // 核心:子类原型指向父类实例,构建原型链 Dog.prototype = new Animal("狗"); // 修复constructor Dog.prototype.constructor = Dog; // 子类添加自有方法 Dog.prototype.bark = function() { console.log(`${this.name}在叫`); }; // 测试 const dog = new Dog("旺财"); dog.eat(); // "狗在吃东西"(继承自父类实例) dog.sleep(); // "狗在睡觉"(继承自父类原型) dog.bark(); // "旺财在叫"(子类自有方法) console.log(dog.__proto__ === Dog.prototype); // true console.log(dog.__proto__.__proto__ === Animal.prototype); // true(原型链层级)
  • 优点:实现简单,直接通过原型链继承父类所有属性和方法。
  • 缺点
    1. 父类的实例属性会被所有子类实例共享(如type属性,修改一个实例会影响其他);
    2. 子类实例创建时无法向父类构造函数传递参数;
    3. 无法实现多继承。

2. 构造函数继承(解决实例属性共享问题)

  • 实现原理:在子类构造函数中通过call()/apply()调用父类构造函数,将父类的实例属性绑定到子类实例上。
  • 实战代码

javascript

运行

// 父类:Animal function Animal(type) { this.type = type; this.skills = ["跑", "跳"]; // 引用类型实例属性 } // 子类:Dog(构造函数继承) function Dog(name, type) { // 核心:调用父类构造函数,绑定this为子类实例 Animal.call(this, type); this.name = name; // 子类自有属性 } // 测试 const dog1 = new Dog("旺财", "狗"); const dog2 = new Dog("来福", "狗"); dog1.skills.push("叫"); console.log(dog1.skills); // ["跑", "跳", "叫"] console.log(dog2.skills); // ["跑", "跳"](实例属性独立,无共享问题) console.log(dog1.type); // "狗"(继承自父类) // 缺点:无法继承父类原型上的方法 dog1.sleep(); // 报错:dog1.sleep is not a function
  • 优点
    1. 父类实例属性独立(无共享问题);
    2. 子类实例创建时可向父类传递参数。
  • 缺点
    1. 无法继承父类原型上的方法(只能继承实例属性和方法);
    2. 父类的实例方法会被每个子类实例重复创建(浪费内存)。

3. 组合继承(原型链 + 构造函数,最常用基础方式)

  • 实现原理:结合 “原型链继承” 和 “构造函数继承” 的优点 —— 原型链继承父类原型方法(共享),构造函数继承父类实例属性(独立)。
  • 实战代码

javascript

运行

// 父类:Animal function Animal(type) { this.type = type; this.skills = ["跑", "跳"]; } // 父类原型方法(共享) Animal.prototype.sleep = function() { console.log(`${this.type}在睡觉`); }; // 子类:Dog(组合继承) function Dog(name, type) { // 1. 构造函数继承:继承实例属性(独立) Animal.call(this, type); this.name = name; } // 2. 原型链继承:继承原型方法(共享) Dog.prototype = new Animal(); // 修复constructor Dog.prototype.constructor = Dog; // 子类原型方法 Dog.prototype.bark = function() { console.log(`${this.name}在叫`); }; // 测试 const dog1 = new Dog("旺财", "狗"); const dog2 = new Dog("来福", "狗"); // 实例属性独立 dog1.skills.push("叫"); console.log(dog1.skills); // ["跑", "跳", "叫"] console.log(dog2.skills); // ["跑", "跳"] // 原型方法共享 console.log(dog1.sleep === dog2.sleep); // true dog1.sleep(); // "狗在睡觉" // 子类方法正常 dog1.bark(); // "旺财在叫"
  • 优点
    1. 实例属性独立(无共享问题);
    2. 原型方法共享(节省内存);
    3. 支持向父类构造函数传递参数。
  • 缺点
    1. 父类构造函数会被调用两次(一次是new Animal(),一次是Animal.call());
    2. 子类原型上会存在父类的实例属性(冗余,如typeskills),但会被子类实例属性覆盖。

4. 寄生组合继承(最优基础继承方式)

  • 实现原理:优化组合继承的缺点,通过 “寄生构造函数” 创建父类原型的副本,避免父类构造函数被调用两次。
  • 实战代码

javascript

运行

// 父类:Animal function Animal(type) { this.type = type; this.skills = ["跑", "跳"]; } Animal.prototype.sleep = function() { console.log(`${this.type}在睡觉`); }; // 核心:创建父类原型的副本(不调用父类构造函数) function createProto(Parent) { function F() {} // 空构造函数(寄生) F.prototype = Parent.prototype; // 继承父类原型 return new F(); // 返回原型副本实例 } // 子类:Dog(寄生组合继承) function Dog(name, type) { Animal.call(this, type); // 构造函数继承(仅调用一次父类构造) this.name = name; } // 原型链继承:子类原型指向父类原型副本 Dog.prototype = createProto(Animal); // 修复constructor Dog.prototype.constructor = Dog; // 子类方法 Dog.prototype.bark = function() { console.log(`${this.name}在叫`); }; // 测试 const dog = new Dog("旺财", "狗"); console.log(dog.__proto__.__proto__ === Animal.prototype); // true(原型链正常) console.log(dog.type); // "狗"(实例属性正常) dog.sleep(); // "狗在睡觉"(原型方法正常)
  • 优点
    1. 组合继承的所有优点(实例独立、原型共享、支持传参);
    2. 父类构造函数仅调用一次(无冗余属性);
    3. 原型链清晰,无多余层级。
  • 缺点:实现稍复杂(可封装为工具函数)。

5. ES6 class 继承(语法糖,推荐现代开发)

  • 实现原理:ES6 引入的class语法,本质是原型继承的 “语法糖”,底层逻辑与寄生组合继承一致,但写法更简洁、直观。
  • 实战代码

javascript

运行

// 父类:Animal(class定义) class Animal { // 构造函数(对应ES5的构造函数) constructor(type) { this.type = type; this.skills = ["跑", "跳"]; } // 原型方法(对应ES5的Animal.prototype.method) sleep() { console.log(`${this.type}在睡觉`); } // 静态方法(不会被实例继承,仅属于类本身) static eat() { console.log("动物都需要吃东西"); } } // 子类:Dog(extends继承) class Dog extends Animal { constructor(name, type) { // 核心:调用父类构造函数(必须在this前调用) super(type); this.name = name; // 子类自有属性 } // 子类原型方法 bark() { console.log(`${this.name}在叫`); } // 重写父类方法(多态) sleep() { console.log(`${this.name}(${this.type})在打盹`); } } // 测试 const dog = new Dog("旺财", "狗"); dog.bark(); // "旺财在叫"(子类方法) dog.sleep(); // "旺财(狗)在打盹"(重写父类方法) Animal.eat(); // "动物都需要吃东西"(静态方法,类调用) console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true(继承关系成立)
  • 优点
    1. 语法简洁直观,符合传统面向对象编程习惯;
    2. 原生支持继承、静态方法、方法重写(多态);
    3. 底层逻辑优化,无组合继承的缺点。
  • 缺点:ES6 语法,需兼容旧浏览器(可通过 Babel 转译)。

6. 寄生继承(补充方式)

  • 实现原理:基于现有对象创建新对象,增强其属性和方法,本质是 “原型继承 + 对象增强”。
  • 实战代码

javascript

运行

// 父类:Animal function Animal(type) { this.type = type; } Animal.prototype.sleep = function() { console.log(`${this.type}在睡觉`); }; // 寄生继承函数 function createDog(original, name) { // 创建父类实例的副本(原型继承) const clone = Object.create(original.prototype); // 增强对象(添加自有属性和方法) clone.name = name; clone.bark = function() { console.log(`${this.name}在叫`); }; return clone; } // 测试 const dog = createDog(Animal, "旺财"); dog.type = "狗"; dog.sleep(); // "狗在睡觉"(继承父类原型方法) dog.bark(); // "旺财在叫"(增强方法)
  • 优点:实现简单,可快速增强现有对象。
  • 缺点
    1. 无法实现多继承;
    2. 增强的方法无法共享(每个实例一份,浪费内存)。

四、6 种继承方式对比选型表(一目了然)

继承方式优点缺点适用场景
原型链继承实现简单,继承完整实例属性共享、无法传参、无多继承简单场景,无需独立实例属性
构造函数继承实例属性独立、支持传参无法继承原型方法、方法重复创建仅需继承父类实例属性的场景
组合继承实例独立、原型共享、支持传参父类构造调用两次、原型有冗余属性ES5 环境的主流场景
寄生组合继承实例独立、原型共享、支持传参、无冗余实现稍复杂ES5 环境的最优选择
ES6 class 继承语法简洁、原生支持多态、无底层缺陷需 ES6 + 环境(可转译)现代开发(React/Vue/Node.js)
寄生继承实现简单、可增强对象方法无法共享、无多继承快速增强现有对象的临时场景

五、实战场景:原型与继承的核心应用

1. 框架中的原型应用(React/Vue)

(1)Vue 组件的原型链

Vue 组件实例的原型链为:VueComponent实例 → VueComponent.prototype → Vue.prototype,因此可通过Vue.prototype挂载全局方法 / 属性:

javascript

运行

// 全局挂载工具方法(通过原型链共享) Vue.prototype.$formatDate = function(date) { return date.toLocaleDateString("zh-CN"); }; // 所有组件实例均可访问 new Vue({ mounted() { console.log(this.$formatDate(new Date())); // 所有组件共享该方法 } });
(2)React 类组件的继承

React 类组件基于 ES6class继承,Component类的原型方法(如setStatecomponentDidMount)被所有子类组件继承:

javascript

运行

// 子类组件继承React.Component class App extends React.Component { constructor(props) { super(props); // 调用父类构造函数 this.state = { count: 0 }; } render() { return ( <div> <p>计数:{this.state.count}</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> 加1 </button> </div> ); } }

2. 原型链排错实战

问题:实例属性被意外覆盖

javascript

运行

// 父类 function Person() { this.hobbies = ["读书"]; } // 子类 function Student() {} Student.prototype = new Person(); const s1 = new Student(); const s2 = new Student(); s1.hobbies.push("运动"); console.log(s2.hobbies); // ["读书", "运动"](意外共享)
原因:父类实例属性被子类实例共享(原型链继承的缺陷)
解决方案:改用组合继承或 ES6 class 继承,通过call()绑定实例属性

3. 手动实现new关键字(原型核心面试题)

new关键字的本质是创建实例对象并关联原型链,手动实现可深度理解原型机制:

javascript

运行

function myNew(constructor, ...args) { // 1. 创建空对象 const obj = {}; // 2. 关联原型链(obj.__proto__ = constructor.prototype) Object.setPrototypeOf(obj, constructor.prototype); // 3. 调用构造函数,绑定this为obj const result = constructor.apply(obj, args); // 4. 若构造函数返回对象,返回该对象;否则返回obj return typeof result === "object" && result !== null ? result : obj; } // 测试 function Foo(name) { this.name = name; } Foo.prototype.sayHello = function() { console.log(`Hello, ${this.name}`); }; const obj = myNew(Foo, "张三"); obj.sayHello(); // "Hello, 张三"(原型链关联成功)

六、避坑指南:90% 开发者踩过的 5 个坑

1. 坑点 1:重写原型后未修复constructor

  • 问题代码

javascript

运行

function Person() {} Person.prototype = { run: function() {} }; const p = new Person(); console.log(p.constructor === Person); // false(指向Object)
  • 原因:重写原型对象时,覆盖了默认的constructor属性。
  • 解决方案:手动修复constructor指向:

javascript

运行

Person.prototype.constructor = Person;

2. 坑点 2:混淆 “原型链查找优先级”

  • 问题代码

javascript

运行

function Foo() { this.name = "实例属性"; } Foo.prototype.name = "原型属性"; const obj = new Foo(); console.log(obj.name); // "实例属性"(预期原型属性)
  • 原因:实例属性优先级高于原型属性,查找时先找实例,再找原型。
  • 解决方案:若需访问原型属性,需通过Object.getPrototypeOf(obj).name

3. 坑点 3:直接修改__proto__导致性能问题

  • 问题__proto__是访问器属性,直接修改会触发原型链重构,性能极差。
  • 解决方案:创建对象时通过Object.create()指定原型:

javascript

运行

const proto = { name: "张三" }; const obj = Object.create(proto); // 替代 obj.__proto__ = proto

4. 坑点 4:认为 “ES6 class 是真正的类继承”

  • 问题:误以为class是 JavaScript 新增的 “类继承” 机制,忽略其原型本质。
  • 真相class是原型继承的语法糖,底层仍依赖prototype__proto__
  • 验证

javascript

运行

class Foo {} console.log(Foo.prototype); // 存在原型对象,证明其原型本质

5. 坑点 5:原型上定义引用类型属性

  • 问题代码

javascript

运行

function Foo() {} Foo.prototype.list = []; const obj1 = new Foo(); const obj2 = new Foo(); obj1.list.push(1); console.log(obj2.list); // [1](意外共享)
  • 原因:原型上的引用类型属性会被所有实例共享。
  • 解决方案:将引用类型属性定义在构造函数中(实例属性):

javascript

运行

function Foo() { this.list = []; // 每个实例独立拥有 }

七、总结:核心知识点速查表与原则

1. 核心知识点速查表

核心概念关键结论
原型链实例→构造函数.prototype→父类.prototype→Object.prototype→null
继承方式选型现代开发优先 ES6 class,ES5 环境用寄生组合继承
查找优先级实例属性 > 原型属性 > 父类原型属性
核心属性关系实例.proto=== 构造函数.prototype
静态方法属于类本身,不被实例继承(ES6 class 用 static 关键字)

2. 核心原则(记住 3 句话)

  1. 原型用于共享:方法和不变属性定义在原型上,节省内存;
  2. 实例用于独立:引用类型属性和可变属性定义在实例上,避免共享;
  3. 现代优先 ES6 class:语法简洁、无底层缺陷,是当前开发的最优选择。

八、拓展学习资源

  1. MDN 官方文档:原型链与继承
  2. V8 引擎官方博客:《JavaScript 原型机制解析》
  3. 面试高频题:《手动实现 ES6 class 继承》《原型链查找的实现原理》

原型与继承的核心是理解 “共享与独立” 的设计思想 —— 原型负责共享方法,实例负责存储独立属性。掌握这一思想,再结合实战场景反复练习,就能彻底走出 “原型链迷宫”,写出更高效、易维护的 JavaScript 代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 10:56:55

Wan2.2-T2V-A14B在博物馆文物活化展示中的沉浸式应用

Wan2.2-T2V-A14B在博物馆文物活化展示中的沉浸式应用 在一座安静的展厅里&#xff0c;一尊西汉青铜酒樽静静陈列于玻璃柜中。灯光下&#xff0c;它泛着幽微的铜绿光泽&#xff0c;铭文斑驳&#xff0c;却难以诉说千年前宴饮之盛。观众驻足片刻&#xff0c;转身离开——这或许是…

作者头像 李华
网站建设 2026/4/16 12:31:39

如何规划半年高效转型网络安全?给零基础者的阶段目标与学习路线图

网络安全技术被广泛应用于各个领域&#xff0c;各大企业都在争抢网络安全人才&#xff0c;这使得网络安全人才的薪资一涨再涨&#xff0c;想转行网络安全开发的人也越来越多。而想要顺利转行网络安全开发&#xff0c;首先要学习网络安全技术&#xff0c;那么转行网络安全从何学…

作者头像 李华
网站建设 2026/4/10 15:13:47

负载均衡-HAProxy 全解析

HAProxy 负载均衡全解析 从基础部署、负载策略到会话保持及性能优化指南 官网 https://www.haproxy.org/ 文档 https://www.haproxy.org/#docs 下载 https://www.haproxy.org/#down HAProxy 介绍 HAProxy 是一款提供高可用性、负载均衡以及基于TCP&#xff08;第四层&#xff…

作者头像 李华
网站建设 2026/4/16 10:57:22

GetBox PyMOL插件:分子对接框自动生成的完整解决方案

GetBox PyMOL插件&#xff1a;分子对接框自动生成的完整解决方案 【免费下载链接】GetBox-PyMOL-Plugin A PyMOL Plugin for calculating docking box for LeDock, AutoDock and AutoDock Vina. 项目地址: https://gitcode.com/gh_mirrors/ge/GetBox-PyMOL-Plugin 还在为…

作者头像 李华
网站建设 2026/4/13 11:16:48

Godot游戏资源解包全攻略:快速提取内部素材的完整方案

Godot游戏资源解包全攻略&#xff1a;快速提取内部素材的完整方案 【免费下载链接】godot-unpacker godot .pck unpacker 项目地址: https://gitcode.com/gh_mirrors/go/godot-unpacker 在游戏开发与资源分析领域&#xff0c;Godot引擎的.pck文件格式常常成为访问游戏内…

作者头像 李华