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

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

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.mrgr.cn/news/38475.html

相关文章:

  • 繁体字能申请注册商标吗?
  • 【AIGC】ChatGPT提示词助力自媒体内容创作升级
  • 支付宝远程收款api之小荷包跳转码
  • 黑神话悟空小西天
  • Scrapy入门
  • 网络编程(Java)
  • 用Uvicorn 构建和部署高性能的异步 Web服务器@python
  • ViTamin——视觉-语言时代的可扩展视觉模型设计
  • SPI驱动学习七(SPI_Slave_Mode驱动程序框架)
  • 大语言模型知识点分享
  • 【C++】C++的Vector使用和实现
  • 【ESP32】Arduino开发 | I2C控制器+I2C主从收发例程
  • 解决在vue项目中index.html中直接引入Cesium.js时候报错:Cesium is not defined
  • Elasticsearch使用Easy-Es + RestHighLevelClient实现深度分页跳页
  • 鸿蒙开发(NEXT/API 12)【硬件(接入报点预测)】手写功能开发
  • 【系统规划与管理师】【案例分析】【考点】【问题篇】第10章 团队建设与管理
  • 如何使用ssm实现航空信息管理系统+vue
  • 浅谈java异常[Exception]
  • 记录Mac编译Android源码踩过的坑
  • C#基础:掌握控制流语句,构建灵活的程序逻辑