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

JavaScript-原型、原型链详解

一、构造函数

在 JavaScript 中,构造函数是一种特殊的函数,用于创建和初始化对象,它就像一个 “对象模板”。通过 new 关键字调用构造函数时,会创建一个新对象,并将构造函数中的属性和方法 “绑定” 到这个新对象上。

1.构造函数基础示例

// 定义一个构造函数 Star
function Star(uname, age) {// 通过 this 为新创建的对象添加属性this.uname = uname; this.age = age; 
}// 使用 new 关键字创建对象实例
const ldh = new Star('刘德华', 18); 
const zxy = new Star('张学友', 19); console.log(ldh.uname); // 输出: 刘德华
console.log(zxy.age); // 输出: 19

在这个例子中,Star 是构造函数,每次通过 new Star(...) 创建实例(ldhzxy)时,都会为实例添加 uname 和 age 属性。

2.构造函数的 “方法重复” 问题

如果尝试为构造函数添加方法,会出现内存浪费的问题:

function Star(uname, age) {this.uname = uname;this.age = age;this.sing = function() { // 每个实例都会新建一个 sing 函数console.log('我会唱歌');};
}const ldh = new Star('刘德华', 18);
const zxy = new Star('张学友', 19);console.log(ldh.sing === zxy.sing); // 输出: false(两个不同的函数)

每个实例(ldhzxy)都有独立的 sing 函数,在内存中重复创建相同逻辑的函数,造成浪费。此时,** 原型(prototype)** 机制就可以解决这个问题 —— 将方法定义在构造函数的原型上,让所有实例共享,避免重复创建。

二、原型(原型对象)的引入

JavaScript 规定,每个构造函数都有一个 prototype(原型)属性,指向另一个对象(即原型对象)。我们可以把不变的方法挂载到这个原型对象上,让所有实例共享这些方法,避免重复创建。

修改代码如下:

function Star(uname, age) {this.uname = uname;this.age = age;
}// 将 sing 方法定义在 Star 的原型对象上
Star.prototype.sing = function() {console.log('我会唱歌');
};const ldh = new Star('刘德华', 18);
const zxy = new Star('张学友', 19);console.log(ldh.sing === zxy.sing); // true,现在共享同一个函数

此时,所有通过 Star 构造函数创建的实例(ldh、zxy)都会通过原型链共享 Star.prototype.sing 方法,不再重复创建,节约了内存。

1.构造函数this的指向

结论:构造函数里面的this就是实例对象

function Star(uname) {console.log(this)this.uname = uname
}
const ldh = new Star('刘德华')

验证:

2.构造函数里的原型对象的this指向

结论:原型对象里面的函数this指向还是实例对象

    <script>let thatfunction Star(uname) {this.uname = uname}Star.prototype.sing = function () {that = thisconsole.log('唱歌')}const ldh = new Star('刘德华')ldh.sing()console.log(that === ldh)</script>

注意:

<script>let that; // 全局变量,初始值为 undefinedfunction Star(uname) {this.uname = uname; // 构造函数中的 this 指向实例,但未赋值给 that}Star.prototype.sing = function () {that = this; // 只有调用 sing 方法时,this 才会指向调用它的实例console.log('唱歌');};const ldh = new Star('刘德华');// ldh.sing(); // 注释掉这行时,sing 方法未被调用,that 保持初始值 undefinedconsole.log(that === ldh); // 输出 false 的原因:
</script>

当 ldh.sing() 被注释时(未调用):

  • sing 方法从未执行,其中的 that = this 代码块没有机会运行
  • 全局变量 that 保持初始值 undefined(声明时未赋值)
  • 最终比较 undefined === ldh 为 false(类型和值都不相等)

3.原型对象的constructor 属性

注意:每个原型对象里面都有个construtor属性,该属性指向该原型对象的构造函数

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>function Star() {}const ldh = new Star();console.log(Star.prototype)console.log(Star.prototype.constructor === Star);</script>
</body></html>

⑴.constructor使用场景

如果有多个对象的方法,我们可以给原型对象采取对象形式赋值。但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。

原型对象的定义方式

第一个示例(逐个添加原型方法)

第一个看成方法

此方法不会丢失 constructor 指向

<script>function Star() { }Star.prototype.sing = function () {console.log('唱歌');};Star.prototype.dance = function () {console.log('跳舞');};console.log(Star.prototype);
</script>

  • 原型对象引用不变:通过 prototype 属性逐个添加方法时,始终操作的是构造函数默认的原型对象(初始包含 constructor 属性)。
  • 保留 constructor 指向:原型对象的 constructor 仍然指向 Star 构造函数,因为未覆盖整个原型对象。
  • 打印结果:Star.prototype 会包含 constructor、sing、dance 三个属性。
第二个示例(整体替换原型对象)

第二个看成对象

<script>console.log(Star.prototype);  // ①function Star() { }Star.prototype = {sing: function () {console.log('唱歌');},dance: function () {console.log('跳舞');}};console.log(Star.prototype);  // ②
</script>

  • 原型对象引用改变:直接将 prototype 赋值为一个全新的对象,覆盖了默认的原型对象。
  • 丢失 constructor 指向:新对象没有 constructor 属性,此时 Star.prototype.constructor 会指向 Object(默认构造函数),除非手动添加 constructor: Star。

三、对象原型

答:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>function Star() {}const ldh = new Star()// 对象原型__proto__ 指向 改构造函数的原型对象console.log(ldh.__proto__)console.log(ldh.__proto__ === Star.prototype)// 对象原型里面也有constructor 指向 构造函数 Starconsole.log(ldh.__proto__.constructor === Star)</script>
</body></html>

注意区分原型对象和对象原型

四、原型继承

1.引入

看以下代码

   function Woman() {this.eyes = 2this.head = 1}const red = new Woman()console.log(red)function Man() {this.eyes = 2this.head = 1}const pink = new Man()console.log(pink)

它们除了名字不同,里面都有相同的属性,可以抽取出来

2.封装-抽取公共部分

把男人和女人公共的部分抽取出来放到Person里面,然后公共的部分放原型对象上

        const Person = {eyes: 2,head: 1}function Woman() {}Woman.prototype = Personconst red = new Woman()console.log(red)console.log(Woman.prototype)function Man() {}Man.prototype = Personconst pink = new Man()console.log(pink)



存在一个问题:

解决:

3.Woman.prototype和Man.prototype都指向堆中同一块地址

在 JavaScript 中,对象是按引用传递的。当将同一个对象 Person 直接赋值给 Woman.prototype 和 Man.prototype 时,两者会共享同一个对象的引用,因此它们的原型确实指向堆内存中的同一块地址。以下是对代码的详细分析:

⑴. 原型赋值的本质是引用传递

const Person = { eyes: 2, head: 1 }; // 定义一个对象(堆内存中创建)// 原型赋值(传递引用而非复制对象)
Woman.prototype = Person; // Woman 的原型指向 Person 对象
Man.prototype = Person;   // Man 的原型也指向同一个 Person 对象
  • Person 是一个普通对象,存储在堆内存中。
  • Woman.prototype 和 Man.prototype 本质上是两个指针,它们被赋值为 Person 对象的内存地址。
  • 结论:两者的原型指向同一块堆内存地址,可通过 === 验证:
console.log(Woman.prototype === Man.prototype); // 输出 true

⑵.实例与原型的关系

const red = new Woman(); // red 的 __proto__ 指向 Woman.prototype(即 Person)
const pink = new Man();  // pink 的 __proto__ 指向 Man.prototype(即 Person)console.log(red.__proto__ === pink.__proto__); // 输出 true(均指向 Person)
  • 两个实例的原型链最终都会追溯到同一个 Person 对象,因此它们会共享 Person 中定义的所有属性(eyeshead)。

⑶.潜在问题:共享原型对象的副作用

由于 Woman.prototype 和 Man.prototype 指向同一个对象,对其中一个原型的修改会影响另一个:

// 为 Woman 的原型添加新方法(实际修改的是 Person 对象)
Woman.prototype.speak = function() { return "Hello"; };// Man 的原型也会自动拥有该方法
console.log(pink.speak()); // 输出 "Hello"(意外共享)

这种行为可能导致预期外的继承关系污染,尤其是在需要独立原型的场景下(如 Woman 和 Man 应属于不同类别)。

⑷.解决方法

把上面的Person写成构造函数,然后类似Java多态的写法;Woman.prototype = new Person()

子类的原型=new 父类

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>function Person() {this.eyes = 2this.head = 1}function Woman() {}Woman.prototype = new Person()Woman.prototype.constructor = Womanconst red = new Woman()console.log(red)console.log(Woman.prototype)// 为 Woman 的原型添加新方法(实际修改的是 Person 对象)Woman.prototype.speak = function () { return "Hello"; };// Man 的原型也会自动拥有该方法function Man() {}Man.prototype = new Person()const pink = new Man()console.log(pink)console.log(pink.speak()); // 输出 "Hello"(意外共享)</script>
</body></html>

此时,再用Man调用speak方法就报错了

五、原型链

相关文章:

  • Office文件内容提取 | 获取Word文件内容 |Javascript提取PDF文字内容 |PPT文档文字内容提取
  • 聊透多线程编程-线程互斥与同步-13. C# Mutex类实现线程互斥
  • 图片压缩工具,多种压缩方案可选
  • requestAnimationFrame是什么?【前端】
  • 基于瑞芯微RK3576国产ARM八核2.2GHz A72 工业评估板——ROS2系统使用说明
  • MH2103系列coremark1.0跑分数据和优化,及基于arm2d的优化应用
  • 鸿蒙NEXT开发LRUCache缓存工具类(单例模式)(ArkTs)
  • Gmssl实战
  • OpenSSL1.1.1d windows安装包资源使用
  • 【C++编程入门】:从零开始掌握基础语法
  • Python常用的第三方模块【openpyxl库】读写Excel文件
  • Vue路由传参的几种方式-案例
  • 系统分析师知识点:访问控制模型OBAC、RBAC、TBAC与ABAC的对比与应用
  • ONLYOFFICE协作空间3.1发布:虚拟数据房间中基于角色的表单填写、房间模板、改进访客管理等
  • 利用WSL2的镜像功能访问Windows下的所有网卡
  • 日志文件太大,如何分卷压缩便于传输
  • 第 2 篇:初探时间序列 - 可视化与基本概念
  • 【网络编程】从零开始彻底了解网络编程(三)
  • IQ信号和实信号的关系与转换的matlab实现
  • 软件工程师中级考试-上午知识点总结(上)
  • 澎湃思想周报|哈佛与特朗普政府之争;学习适应“混乱世”
  • 全球前瞻|中国印尼举行首次“2+2”部长级会议,美乌将签署矿产协议
  • 人民日报:对科研不端行为加大惩处力度,让造假成本远高于收益
  • 寻找“香奈儿”代工厂
  • 建投读书会·东西汇流|西风东渐中的上海营造
  • 摩根大通首席执行官:贸易战损害美国信誉