「JavaScript深入」彻底搞懂JS原型与原型链

news2024/11/17 9:53:46

JavaScript深入 — 原型与原型链

    • 一、原因
    • 二、使用class实现继承
      • 普通的类
      • 实现继承
    • 三、原型
    • 四、原型链
    • 小结
      • 原型
      • 原型链
      • prototype和proto
    • 引申


在这里插入图片描述

在这里插入图片描述


一、原因

JavaScript中除了基础类型外的数据类型,都是对象(引用类型)。但是由于其没有类(class)的概念,如何将所有对象联系起来就成了一个问题,于是就有了原型和原型链的概念。

原型链是JavaScript中实现对象属性继承的一种机制。

原型:构造函数会有一个原型对象 prototype,保存了该构造函数,以及定义的方法;而由构造函数生成的实例,则有一个私有属性 [[prototype]],也同样指向该构造函数的 prototype(在很多环境下都可以使用 __proto__ 访问,但现在已经提供了接口,我们可以通过 Object.getPrototypeOf(instance) 访问。
原型链:前面我们提到的构造函数的 prototype,也有私有属性[[prototype]] 指向另一个 prototype,最终指向 Object.prototype,该原型的__proto__ 指向 null

class Teacher {
	constructor(name){
		this.name = name;
	}
	sayHi(){
		console.log(this.name + ": hi")
	}
}

const teacherA = new Teacher("A");

👆 如上代码片段解析
在这里插入图片描述


二、使用class实现继承

因为JS中没有类(class)这个概念,所以JS的设计者使用了构造函数来实现继承机制

ES6中的 class 可以看作只是一个语法糖,它的绝大部分的功能,ES5都可以做到,新的 class 写法只是让原型的写法更加的清晰、更像面向对象编程的语法而已。下文也会进一步的说明。 —— 摘自阮一峰的ES6入门

普通的类

class Student {
  constructor(name, score) {
    this.name = name;
    this.score = score;
  }
  introduce() {
    console.log(`I am ${this.name}, I got ${this.score} in the exam.`);
  }
}
const student = new Student("小明", 59);
console.log("student", student);
student.introduce();

实现继承

class Person {
  constructor(name) {
    this.name = name;
  }
}

class Student extends Person {
  constructor(name, score) {
    super(name);
    this.score = score;
  }
}

三、原型

🌰 依然是上面的例子,当我们在控制台打印 student 时,我们发现其中并没有 introduce 这个方法

在这里插入图片描述

展开 [[Prototype]](或__proto__)后,我们可以看到 introduce 方法,而这个[[Prototype]] 就是 student 这个对象的隐式原型

在这里插入图片描述
Student 类上会有一个属性 prototype,通过验证可知其与student.__proto__ 指向同一地址空间,而 Student.prototype 被称为显式原型

在这里插入图片描述

当我们在一个对象上尝试查找一个属性或方法时,如果找不到对应的属性或方法,就会往它的隐式原型上去找
在这里插入图片描述


四、原型链

🌰 依然是上面的例子

class Person {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    console.log(`Hi, I am ${this.name}`);
  }
}

class Student extends Person{
  constructor(name, score) {
    super(name);
    this.score = score;
  }
  introduce() {
    console.log(`I am ${this.name}, I got ${this.score} in the exam.`);
  }
}

👇 打印 student 有如下结果

在这里插入图片描述

👇 完整原型链如下

在这里插入图片描述


小结

原型

在JavaScript中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个 prototype 属性,这个属性指向函数的原型对象,使用原型对象的好处是所有对象实例共享它所包含的属性和方法

原型链

每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节

prototype和proto

  • prototype 是构造函数的属性
  • __proto__ 是每个实例都有的属性,可以访问 [[prototype]] 属性,当然 [[prototype]] 也有 __proto__ 属性
  • 实例的 __proto__ 与其构造函数的 prototype 指向的是同一个对象

在这里插入图片描述

__proto__ 并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但依旧不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型,然后再来为原型添加方法/属性。
——摘自阮一峰的ES6入门


引申

为什么JS不像C++/Java那样用类、指针这种,而是用原型和原型链?

🌰 看代码说输出:

function Person(name) {
  this.name = name;
}

const person = new Person("Tom");

console.log(Person.prototype); // {}
console.log(Person.__proto__); // ƒ () { [native code] }
console.log(person.prototype); // undefined
console.log(person.__proto__); // {}

ObjectObject.prototype

复习时突然有点混淆上述两者了,以为 Object 是原型链终点,其实是 Object 的原型,即 Object.prototype

Object 本身是一个构造函数,所以Object.__proto__ 其实指向的是Function.prototype,而Function.prototype.__proto__ 指向了 Object.prototype

在这里插入图片描述

  • 构造函数既有 prototype,指向自己的原型;又有__proto__,指向其继承的构造函数。
  • 构造函数继承原型链的顶端Function,其__proto___指向自己的prototype,如上图,使得 ObjectFunction 在原型链顶端形成了一个类似“环形”的链,第一次遇到时可能难以想明白。

这也解释了我的疑惑,为什么判断类型的方法不能用 Object.toString,而是Object.prototype.toString,二者同名但不是一个方法。

前者沿原型链找到 Function.prototype,调用其 toString 方法,而后者则是Object 原型上定义的方法。

console.log(Object.toString === Object.prototype.toString); // false
console.log(Object.toString === Function.prototype.toString); // true

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2174550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

央行放大招!潘行长发话了,现在能“贷款炒股“,这是真的吗?

9月,央行掌门人潘行长最新放话了,二套房贷款利率也得下调。这降准的利好一出,股市小涨了一波后又回到了原位,给全球金融市场带来了一股不同寻常的暖流。而非常有意思的是,在这次众多的金融政策大礼包当中,有…

CentOS8.5.2111(3)实验之DHCP服务器架设

一、实验目标 1.掌握DHCP服务器的主配置文件各项申明参数及操作及其含义 2. 具备DHCP 服务器、中继服务器的配置能力 3. 具备测试客户端正常获取服务器分配地址的能力 4. 具备DHCP服务器故障排除能力 二、实训原理/流程 (一)项目背景 …

媒界:助力民生保障 长城“消防炮”即将批量交付硬核守护万家灯火

一大波“消防炮”即将列装! 日前,长城炮官方账号发布一条“‘消防炮’来了”的视频,引发广泛关注。视频显示一批长城炮车型已完成消防装备加装,华丽变身“消防炮”,整装待发,即将交付全国消防,…

创建型模式-简单工厂-工厂方法-抽象工厂

简单工厂模式 例题 uml 代码 package simpleFactory; import java.lang.management.OperatingSystemMXBean; import java.util.Scanner; //定义Person class Person{ public void say(){} } class Man extends Person{ public void say() { System.out.…

Transformer是不是BERT、GPT的妈?看完就知道了

Transformer变异衍生出来了两个超强悍的预训练模型 一、Transformer模型 Transformer是近年来深度学习领域中备受瞩目的模型之一,其核心思想是通过自注意力机制和位置编码来捕捉输入序列中的长距离依赖关系。 自注意力机制让模型在处理每个输入元素时能够关注到所有…

Mac电脑上最简单安装Python的方式

背景 最近换了一台新的 MacBook Air 电脑,所有的开发软件都没有了,需要重新配环境,而我现在最常用的开发程序就是Python。这篇文章记录一下我新Mac电脑安装Python的全过程,也给大家一些思路上的提醒。 以下是我新电脑的配置&…

Java入门2——基本数据类型详解

今天我们系统学习一下Java的八种基本数据类型,和C语言有些还是不太一样的,还是要打起精神,好好学习~ 一、Java的数据类型 首先我们画个图,了解一下Java的数据类型 以上就涵盖了Java的数据类型,那么下面,我…

C语言自定义类型:联合体

目录 前言一、联合体1.1 联合体类型的声明1.2 联合体的特点1.3 相同成员的结构体和联合体对比1.4 联合体大小的计算1.5 联合体的⼀个练习 总结 前言 前面我讲到C语言中的自定义结构——结构体,其实C语言中的自定义结构不只有结构体,还有枚举和联合体&am…

交通 | 上门配送or自提点配送?最后一公里配送中的需求引导问题

编者按: 为提高最后一公里配送的效率,本文将客户激励与不确定的路线决策相结合,建立了一个两阶段随即规划问题,并开发了一种精确式的分支定界算法进行求解。 摘要: 为了提高最后一公里配送的效率,零售商…

Vue 3 文件编译流程详解与 Babel 的使用

文章目录 一、背景二、结论三、vitejs/plugin-vue 插件调试前物料准备vuePlugin 入口buildStart 方法transform 方法 四、vue/compiler-sfc 核心包parse 方法compileScript、rewriteDefault 方法compileTemplate 方法 五、整体架构六、总结参考资料 一、背景 最近正在研究 rea…

企业防泄密妙招有哪些?请记住这8招!超实用,学起来!

在古代,有云:“密者,德之高也;事以密成,语以泄败。” 这些谚语不仅是对忠诚守密的高度赞扬,更是对保密工作重要性的深刻阐述。 在现代企业中,数据泄露已成为不容忽视的严峻挑战。 如何有效防止…

MFC单按钮启停实例

单击按钮启动、停止交替切欣换 1、在1Dlg.h文件中添加代码 public:CMy1Dlg(CWnd* pParent NULL); // standard constructorBOOL m_b;2、在1Dlg.cpp文件中添加代码 CMy1Dlg::CMy1Dlg(CWnd* pParent /*NULL*/): CDialog(CMy1Dlg::IDD, pParent) { m_hIcon AfxGetApp()->Lo…

高中还来得及选择信息学奥赛赛道吗?

随着信息学奥赛(NOI)在升学中的重要性日益凸显,越来越多的学生和家长将其视为进入顶尖高校的一个重要途径。然而,很多学生可能直到高中阶段才意识到信息学奥赛的重要性,或者才开始对编程产生兴趣。于是问题出现了&…

DarkLabel2.4版本导入MOT17数据集

目录 背景导入效果MOT17数据集说明DarkLabel导入视频导入gt文件 背景 做目标追踪,目前找了一圈开源工具,发现DarkLabel还是很好用的,提供自动目标跟踪,标注很方便。 由于目标追踪我用的是bytetrack,官网是用mot17数据…

学校周赛(2)

A.Minimize! 题目 思路 本题只需要遍历c的取值&#xff0c;实时更新答案即可 代码 #include<iostream> #include<algorithm> using namespace std;void todo(){int a,b;cin>>a>>b;int ansINT_MAX;for(int ca;c<b;c){ansmin(ans,(c-a)(b-c));}co…

二叉树的前序遍历,中序遍历,后序遍历以及层次遍历(递归方式+C语言代码)

#include<stdlib.h> #include<stdio.h> #include<assert.h> //定义一个二叉树结点结构体 typedef int ElemTpye; typedef struct TreeNode {ElemTpye data;struct TreeNode* left;struct TreeNode* right; }TreeNode; //创建结点 TreeNode* createTreenode(E…

【qt】QQ仿真项目1

一览全局: QQ仿真项目 一.创建项目添加资源文件二.创建数据库三.自定义标题栏Qt类四.加载样式表标题栏按钮的搭配五.标题栏实现移动窗体六.标题栏按钮连接信号槽七.标题栏双击最大化和还原八.基类窗口实现标题栏按钮信号九.重写基类窗口绘图事件确保设置样式表生效十.用户登录界…

Spring Gateway学习

系列文章目录 JavaSE基础知识、数据类型学习万年历项目代码逻辑训练习题代码逻辑训练习题方法、数组学习图书管理系统项目面向对象编程&#xff1a;封装、继承、多态学习封装继承多态习题常用类、包装类、异常处理机制学习集合学习IO流、多线程学习仓库管理系统JavaSE项目员工…

C++20中头文件concepts的使用

<concepts>是C20中新增加的头文件&#xff0c;此头文件是concepts库的一部分&#xff0c;主要用于模板编程、泛型编程。包括 1.core language concepts&#xff1a; std::same_as&#xff1a;指定一种类型(type)与另一种类型是否相同。 std::derived_from&#xff1a;指定…

学习之什么是装饰器

什么是装饰器 本质&#xff1a;就是一个函数&#xff0c;能为其它的函数增加额外功能 # 目标函数 def power_num(n):"""计算并返回1-n之间每个数的平方和:param n::return:"""total 0for i in range(1, n1):total i ** 2"""…