一个好前端,必须要搞懂JS原型和原型链!

简介: 前言原型和原型链基本上是基础前端面试必问的问题之一,虽然这是一个非常基础的知识点,但是往往工作了好几年的前端程序员都理不清楚。或许你大概知道在Javascript中有原型的概念,也知道有原型链的存在,但是如果面试官让你说出来,你可能会把自己都说蒙圈!总体原因:对原型和原型链理解得不够透彻!

1.为什么要学会它们?


如果你学过后端语言比如Java等等,那么你应该知道它们都是面向对象的开发方式。面向对象有许多特点,其中继承就是其中一个。在Java中通常通过类class的方式来实现继承。


而我们的JavaScript语言是一门基于对象的语言,它不是一门真正的面向对象编程的语言。虽然ES6提出了class编程的方式,但它终究只是一个语法糖,class编译之后其实就是一个函数。


那么在JavaScript中如何实现继承呢?这个时候就用到了原型和原型链,它们非常巧妙地解决了JavaScript中实现继承的问题!


2.原型和原型对象


JS中,我们所说的原型通常是针对于函数而言的,当然构造函数也是一个函数。


我们都知道函数也是一个对象,是对象那么它就有属性,在JS中,我们所创建的每一个函数自带一个属性prototype,我们就把prototype称为原型,有些小伙伴也把它称之为“显示原型”,反正就一个意思。


这个prototype它指向了一个对象,你可以把prototype想象成一个指针,或者更简单的理解为prototype的属性值(键值对)。prototype指向的这个对象我们就称之为原型对象,通常大家就直接将prototype理解为原型对象。


为了让大家更好理解,我们可以在浏览器控制台简单查看一下函数的prototype,如下图:11.png

上图中我们声明了一个Person函数,既然函数是对象,那么我们就可以使用“.”来查看它的属性,可以看到有一个prototype属性,这个是每一个函数都有的。


我们在代码里面打印出来看看,示例代码如下:

<script>
  function Person() { }
  console.log(Person.prototype)
</script>


输出结果:12.png

我们可以看到原型对象prototype里面有一个constructor属性,它指向了Person构造函数,我们可以画一张图来理解:13.png


总结

其实原型或者原型对象没有那么复杂,总结下来就下面几点:

  • 每个函数都有prototype属性,被称作原型。
  • prototype原型指向一个对象,故也称作原型对象。


3.prototype__ptoto__


很多小伙伴把prototype__proto__混为一潭,其实这是两个维度的东西。prototype的维度是函数,而__proto__的维度是对象。__proto__是每个对象都有的属性,我们通常把它称为"隐式原型",把prototype称为"显式原型"。


有些小伙伴可能有疑惑,函数也是一个对象,那它是不是也有__proto__属性呢?答案是肯定的,我们可以通过浏览器控制台验证一下。


Function:



15.png

15.png

对象:14.png

我们可以看到函数有prototype__proto__两个属性,而对象只有__proto__属性。接下来我们再来看看__proto__属性有什么呢?

在浏览器控制台进行测试:16.png


上图中我们发现了一个新的属性:[[Prototype]]。很多小伙伴会误认为这个就是我们所说的显式原型prototype,其实不是的,官方对于这个属性其实有解释,我们这里通俗的给大家解释一下:

[[prototype]]其实就是隐式原型__proto__,因为各大浏览器厂家不同,所以取了别名罢了,大家只需记住这个和__proto__一样即可。


上段代码中我们定义了一个空对象,它有一个隐式原型[[prototype]],隐式原型的constructor指向了构造函数Object


总结

__proto__prototype不太一样,一个是对象拥有的隐式原型,一个是函数拥有的显式原型,这里我们简单总结一下__proto__

  • 通常被称作隐式原型,每个对象都拥有该属性。
  • [[prototype]]其实就是__proto__


4.原型链


前面两节我们主要介绍了prototype__proto__,那么它们之间有什么关系呢?

为了理清楚之间它们的关系,我们拿出一段示例代码:

<script>
  function Person(name) {
  }
  // 在函数的原型上添加变量和方法
  Person.prototype.name = "小猪课堂";
  Person.prototype.say = function () {
    console.log("你好小猪课堂");
  }
  let obj = new Person();
  console.log(obj.name); // 小猪课堂
  obj.say(); // 你好小猪课堂
</script>


上段代码大家应该都很熟悉,我们声明了一个构造函数Person,其实就是一个函数。我们知道函数的prototype是一个对象,我们就可以往这个对象上添加东西,所以我们就直接往函数的原型上添加了变量和方法。


接着我们使用new关键词创建一个Person构造函数的实例对象,分别打印name和调用say方法,大家会发现输出结果其实都是Person原型上的东西。


这是为什么呢?这其实就和我们的原型链有关了,我们把obj打印出来看看。


consol.log(obj):


17.png


我们会发现obj对象上面其实并没有name属性和say方法,但是在它的隐式原型[[prototype]]上有namesay,而且我们会发现obj[[prototype]]中的constructor指向的式它的构造函数Person


所以我们大胆的做一个猜想:obj的隐式原型__proto__是否和构造函数Person的显式原型prototype相等呢?我们用代码证实一下:

console.log(obj.__proto__ === Person.prototype) // true


我们发现它们两个果然相等!

接下来我们修改一下我们的代码,我们在obj对象上添加一个name属性,看看会输出什么。


代码如下:

<script>
  function Person(name) {
    this.name = name;
  }
  // 在函数的原型上添加变量和方法
  Person.prototype.name = "小猪课堂";
  Person.prototype.say = function () {
    console.log("你好小猪课堂");
  }
  let obj = new Person("张三");
  console.log(obj.name); // 张三
  obj.say(); // 你好小猪课堂
  console.log(obj)
</script>


输出结果:

18.png


上段代码中我们obj上面有自己的name属性了,这个时候输出的就是obj自带的name属性。到这里我们又可以做一个大胆的猜想:obj对象想要获取name或者say,首先判断自己的属性当中有没有,如果没有找到,那么就在__proto__属性中去找,而这个时候__proto__Personprototype是相等的,也就是__proto__指向Person,那么便可以找到namesay


上面的查找过程是不是很像链式查找啊!而这就是我们所说的原型链,而且我们发现查找的过程主要是通过__proto__原型来进行的,所以__proto__就是我们原型链中的连接点。


总结:


上面的查找的过程形成的一条线索就叫做原型链,大家可以把原型链拆开来理解:原型和链。

  • 原型就是我们的prototype
  • 链就是__proto__,它让整个链路连接起来。


想要理解原型链,我们还得理解__proto__指向哪儿,也就是说它指向那个构造函数,比如上面的obj对象的__proto__指向的就是Person构造函数,所以我们继续往Person上查找。


最后我们上一张经典的图,相信大家能看懂了:

19.png



上面这张图看似很复杂,但是我们理解了prototype__proto__之后很简单,大家按照下面的思路去看上面这张图会很简单的:

  1. 上面很多虚线,我们发现虚线上都有__proto__属性,所以可以看出来__proto__就是一个连接的作用。
  2. 上图中无非有三个构造函数:FooObjectFunction,我们都知道每个函数都有一个prototype显示原型,而且这个显示原型指向了自身这个构造函数。
  3. 接着我们在看图中的new关键字,我们知道new创建的对象都有一个__proto__隐式原型,而且这个隐式原型执行了它的构造函数,也就是__proto__ === prototype
  4. Foo的隐式原型__proto__指向的是Functionprototype,因为函数是属于Function这个构造函数的。所以上图中的FooObject__proto__都指向了Functionprototype



总结


利用原型链这种链式查找的方式,我们就巧妙地实现了继承!要理解原型和原型链其实不难,主要是大家还是要有面向对象的思想,比如通过new关键词创建实例,构造函数是什么?


如果觉得文章太繁琐或者没看懂,可以观看视频: 小猪课堂

相关文章
|
3月前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
6天前
|
前端开发
【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
25 1
【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
|
3月前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
3月前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
87 5
|
3月前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
124 1
|
9月前
|
前端开发 JavaScript
JavaScript中的原型和原型链
JavaScript中的原型和原型链
115 0
|
9月前
|
JavaScript 前端开发
【面试题】最详尽的 JS 原型与原型链终极详解(一)
【面试题】最详尽的 JS 原型与原型链终极详解(一)
171 0
|
9月前
|
JavaScript 前端开发
手把手教你学会js的原型与原型链,猴子都能看懂的教程
手把手教你学会js的原型与原型链,猴子都能看懂的教程
|
9月前
|
JavaScript 前端开发
JavaScript原型,原型链
JavaScript原型,原型链
|
8月前
|
设计模式 JavaScript 前端开发
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
JavaScript的继承机制基于原型链,它定义了对象属性和方法的查找规则。每个对象都有一个原型,通过原型链,对象能访问到构造函数原型上的方法。例如`Animal.prototype`上的`speak`方法可被`Animal`实例访问。原型链的尽头是`Object.prototype`,其`[[Prototype]]`为`null`。继承方式包括原型链继承(通过`Object.create`)、构造函数继承(使用`call`或`apply`)和组合继承(结合两者)。ES6的`class`语法是语法糖,但底层仍基于原型。继承选择应根据需求,理解原型链原理对JavaScript面向对象编程至关重要
171 7
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略

热门文章

最新文章

  • 1
    【Java若依框架】RuoYi-Vue的前端和后端配置步骤和启动步骤
  • 2
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 4
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 5
    详解智能编码在前端研发的创新应用
  • 6
    巧用通义灵码,提升前端研发效率
  • 7
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 8
    智能编码在前端研发的创新应用
  • 9
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 10
    抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目
  • 1
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    23
  • 2
    Node.js 中实现多任务下载的并发控制策略
    32
  • 3
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    25
  • 4
    【JavaScript】深入理解 let、var 和 const
    48
  • 5
    【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
    44
  • 6
    【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
    52
  • 7
    【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
    55
  • 8
    如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
    71
  • 9
    【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
    55
  • 10
    JavaWeb JavaScript ③ JS的流程控制和函数
    62