当前位置: 首页 > news >正文

JS 原型与原型链详解

JavaScript 原型与原型链详解

文章目录

  • JavaScript 原型与原型链详解
    • 一、基础概念类
      • 1.1 什么是原型?JavaScript 中如何访问一个对象的原型?
      • 1.2 构造函数、实例对象和原型对象之间的关系是什么?
      • 1.3 prototype 和 **proto** 的区别是什么?
    • 二、原型链机制类
      • 2.1 什么是原型链?描述原型链的查找机制
      • 2.2 代码示例分析
    • 三、构造函数与实例类
      • 3.1 new 操作符执行时发生了什么?
      • 3.2 手动实现 new 操作符
        • 3.2.1apply逻辑
        • 3.2.2`result instanceof Object ? result : obj;`解决的问题
    • 四、继承与原型链类
      • 4.1 JS如何实现继承?
    • 原型链继承
    • 五、高级应用类
      • 5.1 如何修改内置对象(如 Array)的原型?这样做有什么风险?
        • 5.1.1修改内置原型
        • 5.1.2创建子类继承内置对象(ES6 Class方式)
        • 5.1.3使用Object.create创建原型链继承
      • 5.2 如何判断一个属性是对象自身的还是继承自原型链的?
    • 六、总结

一、基础概念类

1.1 什么是原型?JavaScript 中如何访问一个对象的原型?

**原型(Prototype)**是 JavaScript 实现继承的基础机制。每个 JavaScript 对象都有一个内部属性 [[Prototype]](可通过 __proto__Object.getPrototypeOf() 访问),它指向该对象的原型对象。原型对象本身也是一个普通对象,同样拥有自己的原型,这样就形成了原型链。

访问对象原型的方式

  • 对于构造函数:通过 Constructor.prototype 访问
  • 对于实例对象:
    • obj.__proto__(非标准但广泛支持)
    • Object.getPrototypeOf(obj)(标准方法)
function Person() {}
const p = new Person();// 访问构造函数的原型
console.log(Person.prototype); // 访问实例的原型
console.log(p.__proto__); 
console.log(Object.getPrototypeOf(p)); 

1.2 构造函数、实例对象和原型对象之间的关系是什么?

三者关系可概括为:

  1. 构造函数:用于创建对象的函数,拥有 prototype 属性
  2. 原型对象:通过 Constructor.prototype 访问,包含共享属性和方法
  3. 实例对象:通过 new Constructor() 创建,其 __proto__ 指向构造函数的原型

关系图示:

构造函数 (Person)├── prototype (原型对象)│     ├── constructor (指回构造函数)│     └── 共享属性和方法└── 实例化└── 实例对象 (p)└── __proto__ (指向原型对象)

关键点:

  • 构造函数的 prototype 属性指向原型对象
  • 原型对象的 constructor 属性指回构造函数
  • 实例对象的 __proto__ 指向构造函数的原型对象

1.3 prototype 和 proto 的区别是什么?

特性prototype__proto__
所属对象函数对象(包括函数)
作用为构造函数定义共享属性和方法指向对象的原型,形成原型链
是否标准否(非标准但广泛支持)
访问方式Constructor.prototypeobj.__proto__Object.getPrototypeOf(obj)
用途场景实现继承和共享方法属性查找机制

关键区别

  • prototype 是函数特有的属性,用于实现基于原型的继承
  • __proto__ 是对象实例的属性,指向其构造函数的原型对象

二、原型链机制类

2.1 什么是原型链?描述原型链的查找机制

原型链是由对象的 __proto__ 链接形成的链条结构,它允许对象访问其原型上的属性和方法,直到 Object.prototype.__proto__(值为 null)为止。

原型链查找机制

  1. 访问对象属性时,首先在对象自身查找
  2. 如果自身不存在该属性,则通过 __proto__ 查找其原型对象
  3. 继续沿原型链向上查找,直到找到属性或到达原型链顶端(null
  4. 如果最终未找到,则返回 undefined

这种机制实现了 JavaScript 的继承和属性共享。

2.2 代码示例分析

function Person() {}
Person.prototype.name = "Alice";let p = new Person();
console.log(p.name); // "Alice"
console.log(p.hasOwnProperty("name")); // false

执行过程解析

  1. p.name 访问时:
    • 首先在 p 对象自身查找 name 属性 → 未找到
    • 通过 p.__proto__ 查找 Person.prototype → 找到 name: "Alice"
    • 返回 "Alice"
  2. p.hasOwnProperty("name")
    • hasOwnProperty 是检查属性是否为对象自身的属性
    • name 实际存在于 Person.prototype 上,而非 p 对象自身
    • 返回 false

三、构造函数与实例类

3.1 new 操作符执行时发生了什么?

new 操作符创建实例时,背后执行了以下步骤:

  1. 创建一个新的空对象 {}
  2. 将新对象的 __proto__ 指向构造函数的 prototype 属性
  3. 将构造函数的 this 绑定到新对象,并执行构造函数
  4. 如果构造函数返回一个对象,则返回该对象;否则返回新创建的对象

完整过程示例

function Person(name) {this.name = name;
}const p = new Person("Alice");

等价于:

function myNew(Constructor, ...args) {// 1. 创建空对象let obj = {};// 2. 设置原型链obj.__proto__ = Constructor.prototype;// 3. 绑定this并执行构造函数let result = Constructor.apply(obj, args);// 4. 返回结果(优先返回对象,否则返回新对象)return result instanceof Object ? result : obj;
}const p = myNew(Person, "Alice");

3.2 手动实现 new 操作符

function myNew(Constructor, ...args) {// 1. 创建一个新对象,并将其原型指向构造函数的prototypelet obj = Object.create(Constructor.prototype);// 2. 调用构造函数,将this绑定到新对象,args是一个数组let result = Constructor.apply(obj, args);// 3. 如果构造函数返回了一个对象,则返回该对象;否则返回新对象return result instanceof Object ? result : obj;
}
3.2.1apply逻辑

apply(thisArg,argsArray) 接收 两个参数

  1. thisArg(必需):函数运行时绑定的 this 值。
  2. argsArray(可选):一个数组或类数组对象,包含传递给函数的参数列表。如果省略或为 null/undefined,则相当于传递空数组。

argsArray 的核心作用是解耦 myNew 和构造函数 Constructor 之间的参数关系

  1. myNew 不需要关心具体有多少参数,只需将所有额外参数打包到 args 数组中。
  2. Constructor 可以自由定义自己需要的参数,通过 this 接收并处理。
3.2.2result instanceof Object ? result : obj;解决的问题

作用:处理构造函数 Constructor 的返回值,确保最终返回的对象符合预期

在 JavaScript 中,构造函数(通过 new 调用的函数)可以显式返回一个值。这个返回值可以是:

  1. 一个对象(包括数组、函数、普通对象等)。
  2. 一个原始值(如 numberstringbooleannullundefined)。

如果构造函数返回一个对象,new 操作符会忽略默认创建的实例对象,直接返回这个指定的对象;如果返回原始值,则忽略返回值,仍然返回默认创建的实例对象。

示例:构造函数返回对象 vs 原始值
// 情况1:构造函数返回对象
function Person1() {return { name: "Bob" }; // 返回一个新对象
}
const p1 = new Person1();
console.log(p1); // { name: "Bob" }(不是 Person1 的实例)// 情况2:构造函数返回原始值
function Person2() {return 123; // 返回原始值
}
const p2 = new Person2();
console.log(p2); // Person2 的实例(不是 123)

四、继承与原型链类

4.1 JS如何实现继承?

原型链继承

核心:将子类的原型指向父类的实例

JavaScript 中通过原型链继承实现继承的基本模式:

function Parent() {this.parentProperty = "Parent Value";
}Parent.prototype.parentMethod = function() {console.log("Parent Method");
};function Child() {this.childProperty = "Child Value";
}// 关键步骤:将Child的原型指向Parent的实例
Child.prototype = new Parent();const c = new Child();
console.log(c.parentProperty); // "Parent Value"
c.parentMethod(); // "Parent Method"

继承关系图示

Child实例 (c)├── __proto__ → Child.prototype (Parent的实例)├── __proto__ → Parent.prototype├── constructor → Parent└── parentMethod└── childProperty (来自Child构造函数)

注意:这种继承方式存在一些问题(如引用类型共享、无法向父类构造函数传参等),现代开发更推荐使用 ES6 的 classextends 语法。

五、高级应用类

5.1 如何修改内置对象(如 Array)的原型?这样做有什么风险?

5.1.1修改内置原型

修改方式示例

// 添加自定义方法到Array原型
Array.prototype.customMethod = function() {console.log("This is a custom method");
};const arr = [1, 2, 3];
arr.customMethod(); // "This is a custom method"

潜在风险

  1. 全局污染:修改内置原型会影响所有使用该内置对象的代码
  2. 命名冲突:可能与未来 JavaScript 版本新增的方法名冲突
  3. 兼容性问题:可能导致与其他库或框架的不可预期交互
  4. 维护困难:使代码行为变得不可预测,增加调试难度

最佳实践

  • 避免直接修改内置原型
  • 如需扩展功能,考虑使用工具函数或创建子类
  • 如果必须修改,添加前缀以减少冲突风险(如 myCustomMethod
5.1.2创建子类继承内置对象(ES6 Class方式)

这是最推荐的方式,既扩展了功能,又不会影响原生对象。

1. 基础继承示例

// 创建继承自Array的自定义类
class CustomArray extends Array {// 添加自定义方法customSum() {return this.reduce((acc, val) => acc + val, 0);}// 可以添加更多自定义方法customMax() {return Math.max(...this);}
}// 使用示例
const arr = new CustomArray(1, 2, 3, 4);
console.log(arr.customSum()); // 输出: 10
console.log(arr.customMax()); // 输出: 4
console.log(arr instanceof Array); // true (仍然属于Array类型)
5.1.3使用Object.create创建原型链继承

更底层的实现方式,不使用ES6 class语法。

// 创建基于Array的新对象
const EnhancedArray = function(...items) {const arr = [...items];return Object.setPrototypeOf(arr, EnhancedArray.prototype);
};// 设置原型链
EnhancedArray.prototype = Object.create(Array.prototype);
EnhancedArray.prototype.constructor = EnhancedArray;// 添加自定义方法
EnhancedArray.prototype.customSum = function() {return this.reduce((acc, val) => acc + val, 0);
};// 使用示例
const arr = EnhancedArray(1, 2, 3);
console.log(arr.customSum()); // 输出: 6
console.log(arr instanceof Array); // true

5.2 如何判断一个属性是对象自身的还是继承自原型链的?

判断方法

  1. hasOwnProperty() 方法:

    • 只检查对象自身的属性,不检查原型链
    const obj = { a: 1 };
    console.log(obj.hasOwnProperty('a')); // true
    console.log(obj.hasOwnProperty('toString')); // false
    
  2. Object.getOwnPropertyNames()

    • 返回对象自身的所有属性名(不包括原型链)
    const obj = { a: 1, b: 2 };
    console.log(Object.getOwnPropertyNames(obj)); // ["a", "b"]
    
  3. in 操作符:

    • 检查属性是否存在于对象或其原型链中
    const obj = { a: 1 };
    console.log('a' in obj); // true
    console.log('toString' in obj); // true
    

综合示例

function checkProperty(obj, prop) {if (obj.hasOwnProperty(prop)) {console.log(`${prop} 是对象自身的属性`);} else if (prop in obj) {console.log(`${prop} 是继承自原型链的属性`);} else {console.log(`${prop} 不是对象的属性`);}
}const obj = { a: 1 };
checkProperty(obj, 'a'); // "a 是对象自身的属性"
checkProperty(obj, 'toString'); // "toString 是继承自原型链的属性"
checkProperty(obj, 'b'); // "b 不是对象的属性"

六、总结

JavaScript 的原型和原型链机制是其面向对象编程的核心,理解这些概念对于掌握 JavaScript 至关重要:

  1. 原型是对象间共享属性和方法的机制
  2. 原型链实现了属性的查找和继承
  3. 构造函数、实例和原型三者构成了 JavaScript 对象系统的基础
  4. new 操作符通过特定步骤创建实例并建立原型链
  5. 原型链继承是 JavaScript 实现继承的主要方式
  6. 谨慎操作内置原型,避免潜在风险
  7. 准确判断属性来源对调试和维护非常重要

相关文章:

  • CMO增加人工智能投资:数据表明了什么
  • 广东食品安全管理员证有哪些,怎么考的呢
  • const auto 和 auto
  • 第二节:Vben Admin v5 (vben5) Python-Flask 后端开发详解(附源码)
  • MySQL基础知识(DDL、DML)
  • Docker:修改已有容器的端口映射
  • 树莓派超全系列教程文档--(63)rpicam-apps可用选项介绍之常用选项
  • 芯伯乐XBLW GT712选型及应用设计指南
  • Redis:极速缓存与数据结构存储揭秘
  • MySQL:Prepared Statement 预处理语句
  • 软考 系统架构设计师系列知识点之杂项集萃(87)
  • Qt Connections详解:信号与槽的核心机制
  • 【华为Pura80系列】鸿蒙生态再升级:Pura 80 系列影像突破,WATCH 5 开启智能手表新纪元
  • LeetCode - 1047. 删除字符串中的所有相邻重复项
  • Spring中@Value注解:原理、加载顺序与实战指南
  • 使用ExecutorService进行异步任务调度
  • 初学者运行Pycharm程序可能会出现的问题,及解决办法
  • Dagster软件定义资产(SDA)完全指南:从概念到落地实践
  • 前端开发中vue的脚手架你知道是什么意思吗?
  • Java中的集合框架:List、Set、Map的使用与性能对比
  • 一个可以看qq空间的网站/百度助手app下载
  • 网站建设一年多少恰/免费的网站平台
  • 网站模糊背景/新闻发布稿
  • UE4做购物网站/企业软文怎么写
  • 昆明网站建设外包/免费企业网站建设流程
  • 长沙公司网站设计/关键词整站优化