百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

图解:什么是 JS 原型和原型链? 图解:什么是 js 原型和原型链接的区别

xsobi 2024-12-16 17:05 1 浏览

本文转载自微信公众号「小鹿动画学编程」,作者不甘平凡的码农。转载本文请联系小鹿动画学编程公众号。

写在前边

在我初学 JS 语言的继承机制原型和原型链的时候,我一直理解不了这种设计机制,再加上之前原有对 Java继承的理解,在学习 JS 继承机制的设计上踩了一个大坑,很多知识点前期都是死记硬背,无法真正的理解它的设计思想。

JS 中的继承机制思想可以说是学习 JS 的一个核心思想,更可以说是 JS 中的一个命脉,往往这些复杂、抽象的继承关系,以及专业术语、代名词却成为了困扰初学者的绊脚石。当我真正理解它的设计思想时,其实并没有那么复杂,而且觉得非常简单。

在写这篇 JS 的原型和原型链的文章之前,我在谷歌搜索检索了大量的高赞有关 JS 原型和原型链的文章,大部分都是围绕着“是什么”来讲的,导致部分初学者缺少对 JS 继承的设计与实现的前后关联性,还是很难准确的去理解。

我们先要明白,学习这块内容知识要知道设计者“ 为什么这样做 ” 远比 “怎么做的” 重要的多这才是掌握这部分内容的关键。

今天小鹿对 JS 的继承机制要做一个系统的总结,从设计者的角度出发,将复杂的设计思想用动画呈现,将零碎的知识点体系化,争取让你一文搞懂 JS 的继承机制思想(原型和原型链)。

思维导图

1.JS 的发展史

要想贯彻 JS 的核心设计思想,我们要从 JS 的诞生说起。

1.1 为什么会诞生 JavaScript ?

相对比较成熟的浏览器是由网景公司发布的,早些年间,浏览器只能浏览网页内容,而不能进行用户交互。比如我们登录输入用户名和密码,在浏览器是不能进行判断用户是否真正输入了,而是通过服务器来判断,如果没有输入,返回错误提示用户,这种设计非常的浪费时间和服务器资源。

为了解决这个问题,网景公司需要开发一种运行在浏览器中的脚本语言,用来简单的做用户输入校验等操作。

当时最流行的语言是面向对象的Java编程语言 ,网景公司为了能够借助 Java将浏览器脚本语言流传开,所以起名 JavaScript。其实两者没有任何的关系。

1.2 存在的问题

JS 中的数据类型设计受当时 Java流行的影响,都是对象类型,这时候就遇到问题了,有对象必然涉及到继承机制,那么 JS 的继承机制要设计成 Java一样呢?还是另有设计思想?

2.JS 继承的设计思想

JS 的开发者想如果设计成像 Java一样有“类”的概念岂不是和 Java一样成为了一种完全面向对象的编程语言了?最后决定自己设计一种继承机制,但是它的设计思想还是采用了 Java的一些特性。

2.1 生成对象

通常 Java 生成对象是通过 new 的方式,通过类生成一个实例对象的过程。但是 JS 中并没有类,那 JS 的设计者要怎么做?

他找到了 Java 和 JS 的共同点就是两者都有构造函数, Java的 new 的过程内部其实调用了构造函数。但是 JS 是没有“类”的概念的,于是 JS 就把new 一个“类”设计成了 new 一个构造函数,于是构造函数成为了一个实例对象的原型对象。

3.为什么要设计原型对象?

上述这样的原型设计有一个致命的缺点就是无法共享公共属性。

因为我们知道,每 new 一个对象,生成的实例是两个不同的对象。所以共有的属性也不是共享的。

所以要设计一个对象专门用来存储对象共享的属性,那么我们叫它「原型对象」。

4.什么是原型对象?

要想让构造函数生成的所有实例对象都能够共享属性,那么我们就给构造函数加一个属性叫做prototype,用来指向原型对象,我们把所有实例对象共享的属性和方法都放在这个构造函数的prototype属性指向的原型对象中,不需要共享的属性和方法放在构造函数中。

这里有一点疑惑就是,我们知道对象可以设置属性,函数也可以设置属性吗?对于初学者来说是比较懵逼的,那我们可以稍微的简单说一下:

JavaScript 中的函数拥有对象的所有能力,也因此可被称作为任意其他类型对象来对待。当我们说函数是第一类对象的时候,就是说函数也能够对象的一些功能,比如添加属性,函数当做参数传递等。

所以说,实例对象一旦通过构造函数创建,就会自动给实例对象赋值上原型对象上共享的属性或方法。说清楚一点就是该对象属性都指向了原型对象的属性值。

5.对象和函数在原型链关系?

上述的图反映了对象以及函数在原型链中的关系,如果你觉得上边的这张图看懵逼了,没关系,我刚开始学习原型链的时候,根本不知道上边这是什么“清明上河图”,小鹿下面通过一步步的拆分讲解,看这张图就非常简单,没错,非常简单。

我们文章的开头也说了什么是原型对象,说白了就是构造函数的一个 prototype属性,这个属性就指向原型对象。

其实我们其中一些连接属性没有讲到,只讲到了prototype属性,下面一张图来将剩下的属性补充完整,我们只要把这张图印到大脑中就可以了。

我们来分析一下上图,首先我们先要声明一个狗的构造函数,定义其名字和体重属性(私有属性),同时每个构造函数我们上边讲到了,都会有一个prototype属性。

这个prototype指向的就是原型对象,原型对象放的就是对象共享的属性。但是注意,原型对象里有一个constructor属性,这个属性又指回了构造函数。

我们通过 new 构造函数生成两个狗的对象实例,一个叫豆豆,一个叫贝贝,这两个是两个不同的对象,名字体重都不相同,但是他们会共享原型对象上的属性 type,它们共有的属性都是犬类。

在 JS 所有对象中,只要是对象,都会有一个内置属性叫做_proto_,而且这个属性是系统自动生成的,只要你创建一个对象,这个对象就有这个属性。这个_proto_属性指向的是原型对象。

通过上边的分布讲解,我们明白了构造函数与对象实例以及原型对象的关系。

总结为一句话为:

构造函数的 prototype 指向原型对象,原型对象有一个 constructor 属性指回构造函数,每个构造函数生成的实例对象都有一个 _proto_ 属性,这个属性指向原型对象。

没错,原型就是这么简单。但是你会发现,原型也是对象呀,你说只要是对象都会有一个_proto_属性指向自身构造函数的原型对象。

没错,要想知道原型对象的_proto_属性指向谁,就要知道是哪个构造函数创建了原型对象?

我们知道,所有的 JS 对象的都是继承了一个叫做 Object 的对象。可以理解为Object 构造函数创造了这个万物,他们的关系如下,和上边是同样的道理,上边总结的那句话好好理解一下。

但是上图中会有一个疑问,Object 构造函数原型对象的也是对象,它肯定也有一个_proto_属性,为什么会指向 null 呢?

我们在拿上述总结的那句话,_proto_属性指向的是自身构造函数的原型对象,自身的构造函数是谁?是 Object 构造函数,那 Object构造函数的原型是谁?当然是本身(如图),所以把_proto_指向了null。

上边的关系如果不仔细整理的话确实很乱,尤其是对于初学者,但是如果像小鹿这样已整理,再乱的关系把它安排的井井有条,没有理解,就多看几篇文章。

6.原型链

我们还有一个问题没有解决就是原型链?既然我么你知道什么是原型了,原型链是什么?顾名思义,肯定是一条链,既然每个对象都有一个_proto_属性指向原型对象,那么原型对象也有_proto_指向原型对象的原型对象,直到指向上图中的null,这才到达原型链的顶端。

不要忘了,上边那种图我们还没有把它理解,我们把图自上而下理解。

第一张图分解,上边小鹿画的图的关系和这个一样的,仔细对比一下,很简单,第一张图就这么解决了。

我们继续向下分割,看第二张图。

第二张图怎么还是那么眼熟呢,这不是小鹿上边分析的 Object 的关系图吗?对的,没错。

第三张图,稍微绕个弯子,但是换汤不换药呀,听小鹿分析来。

看着还是眼熟,只不过把function换成了Function,f 变成了大写的 F,这里涉及到一个知识点就是,在 JS 中,所有的 function函数都是由Function继承来的,可以说是Function是所有 function的祖宗。

那Function是由谁生产来的?我们看到图中的Function函数有_proto_属性了,而且属性指向自己的原型对象,那不就是自己繁衍自己吗?可以这么理解。

小结

这里我们在纵观全图,总结几条定义你比对着图去找。

1、所有的实例的_proto_都指向该构造函数的原型对象(prototype)。

2、所有的函数(包括构造函数)是Function的实例,所以所有函数的 _proto_的都指向Function的原型对象。

3、所有的原型对象(包括 Function的原型对象)都是Object的实例,所以_proto_都指向 Object(构造函数)的原型对象。而 Object构造函数的 _proto_指向 null。

4、Function构造函数本身就是 Function 的实例,所以_proto_指向Function的原型对象。

全篇文章的精华都在最后的总结部分,前边的所有分解讲解是为了让你理解这些函数对象以及原型对象之间的关系,这关系都是固定的,谁指向谁,都是写死额,只要你记住了他们的关系,这张图就理解的差不多了,能够理解完这张图,你的原型和原型链已经了解的很扎实了,但是还需要做一些面试题巩固一下。

相关推荐

好用的云函数!后端低代码接口开发,零基础编写API接口

前言在开发项目过程中,经常需要用到API接口,实现对数据库的CURD等操作。不管你是专业的PHP开发工程师,还是客户端开发工程师,或者是不懂编程但懂得数据库SQL查询,又或者是完全不太懂技术的人,通过...

快速上手:Windows 平台上 cURL 命令的使用方法

在工作流程中,为了快速验证API接口有效性,团队成员经常转向直接执行cURL命令的方法。这种做法不仅节省时间,而且促进了团队效率的提升。对于使用Windows系统的用户来说,这里有一套详细...

使用 Golang net/http 包:基础入门与实战

简介Go的net/http包是构建HTTP服务的核心库,功能强大且易于使用。它提供了基本的HTTP客户端和服务端支持,可以快速构建RESTAPI、Web应用等服务。本文将介绍ne...

#小白接口# 使用云函数,人人都能编写和发布自己的API接口

你只需编写简单的云函数,就可以实现自己的业务逻辑,发布后就可以生成自己的接口给客户端调用。果创云支持对云函数进行在线接口编程,进入开放平台我的接口-在线接口编程,设计一个新接口,设计和配置好接口参...

极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:iN在之前和大家说过,在iN的家里是没有墙面开关的。...

window使用curl命令的注意事项 curl命令用法

cmd-使用curl命令的注意点前言最近在cmd中使用curl命令来测试restapi,发现有不少问题,这里记录一下。在cmd中使用curl命令的注意事项json不能由单引号包括起来json...

Linux 系统curl命令使用详解 linuxctrl

curl是一个强大的命令行工具,用于在Linux系统中进行数据传输。它支持多种协议,包括HTTP、HTTPS、FTP等,用于下载或上传数据,执行Web请求等。curl命令的常见用法和解...

Tornado 入门:初学者指南 tornados

Tornado是一个功能强大的PythonWeb框架和异步网络库。它最初是为了处理实时Web服务中的数千个同时连接而开发的。它独特的Web服务器和框架功能组合使其成为开发高性能Web...

PHP Curl的简单使用 php curl formdata

本文写给刚入PHP坑不久的新手们,作为工具文档,方便用时查阅。CURL是一个非常强大的开源库,它支持很多种协议,例如,HTTP、HTTPS、FTP、TELENT等。日常开发中,我们经常会需要用到cur...

Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介

本章涵盖使用Actix提供静态网页...

我给 Apache 顶级项目提了个 Bug apache顶级项目有哪些

这篇文章记录了给Apache顶级项目-分库分表中间件ShardingSphere提交Bug的历程。说实话,这是一次比较曲折的Bug跟踪之旅。10月28日,我们在GitHub上提...

linux文件下载、服务器交互(curl)

基础环境curl命令描述...

curl简单使用 curl sh

1.curl--help#查看关键字2.curl-A“(添加user-agent<name>SendUser-Agent<name>toserver)”...

常用linux命令:curl 常用linux命令大全

//获取网页内容//不加任何选项使用curl时,默认会发送GET请求来获取内容到标准输出$curlhttp://www.baidu.com//输出<!DOCTYPEh...

三十七,Web渗透提高班之hack the box在线靶场注册及入门知识

一.注册hacktheboxHackTheBox是一个在线平台,允许测试您的渗透技能和代码,并与其他类似兴趣的成员交流想法和方法。它包含一些不断更新的挑战,并且模拟真实场景,其风格更倾向于CT...