JS 原型的原理

接下来,我们要说一个很重要的东西,就是原型,也就是 prototype。

原型这个东西在 js 里面是非常有用的一个东西,也非常的重要。

对于前端来说,原型肯定不会陌生。

但是有几个问题:这玩意到底是干啥的?它的原理是什么?都用在哪里?

首先,这里用一句话特别明确的告诉大家:

原型这个东西,它可以给类添加或修改东西,这样的话,这个类它所有的实例就都会有这个东西,这是它的根本作用。

那么我们就直接来一个例子看看:

可以看到,当我们在数组的原型上面加了一个 a = '123' 之后,它就有了。

最厉害的是,以后我再蹦出来一些别的数组实例,只要它还是个数组,那它上面就也会有这个 a:

这个就是原型的作用,它可以给类添加东西,添加完之后,这个类所 new 出来的,所有实例上面都有这个东西。

那么现在我们就有一个问题,怎么做到的,它的原理是什么?

它的原理其实非常的简单,就是在你这个实例需要某个东西的时候(这个东西可以是个属性,也可以是个方法),它是这样的一个顺序:
首先,从自己身上找, 如果自己身上有,那就直接拿来用。如果没有,它会继续去我的这个类身上找。如果类身上有,那自然就好。如果还没有,它还会再去父类的原型上面试着找一下。如果还没有,在往上找,一直找到最后,能找到 Object.prototype 上,这是它最终的一个归宿(因为 Object 其实是所有人间接的祖宗,它是最上面的)。一直找到 Object 身上,如果还没有,不好意思,undefined,没找到就是 undefined。

说白了,先在自己身上找,没有就找它爹要,找他爹其实就是在找它爹的原型,还没有,就找它爷爷,一直找到 Object 身上,还没有,就是 undefined。

既然了解了原型,那我什么时候会用到它?可以用它来干啥?

以前的时候可能还会用的多一点,现在有专门的class了,方法直接写里面,那还要原型干啥?

当然有用,用途也非常的简单,就是给类去添加方法。

一般情况下,分成 2 种用途:
1,添加一些公共的方法。

假设我现在有一个方法,它的作用是统计一下我这个字符串里面有几个 a,那么注意,我有两种方法来做这个事:

首先第一个方法,我可以把它封装成一个函数:

这种方法是可以的,但是有 2 个问题:

1,太啰嗦了。人家每次用的时候都是括号罗括号,如果层级深一点,变量名长一点,看起来就一大堆,很麻烦。

2,你直接把 function countA(){} 这个函数给露在全局外面,这个时候其实很容易重名的。

所以第二种方法可以这么来写,我们可以直接把它扔到原型上面去:

可以看到,一样的可以去解决问题。

并且,在用法上 str.countA() 比 countA(str) 用起来比较方便和自然。

而且最重要的是,不容易重名。因为如果是一个全局的变量,那就很容易重名。

比如现在这个是统计字符串的 a,如果我还有一个 countA 是用来统计数组有几个 a 的呢?

这样就不会重名。

所以,原型的第一种用途就是,可以去给一些类,不管是系统类,还是你自己的类,去添加一些公共的方法。

然后第二种用途就是,可以去修补系统的函数

什么意思呢?

这种用法其实用的还蛮多的,有人给它起了个专门的名词叫 polyfill,补充系统函数的功能

相信大家经常看到 polyfill 这个词,其对应的中文也有很多,比如适配器,垫片等等。刚开始的时候百思不得其解,啥玩意啊?
其实说白了,polyfill 就是兼容。

比如 map 函数在高级浏览器中是没问题的:

可以看到,这个结果在谷歌浏览器上面是 OK 的。

但是,低级浏览器是不认识 map 的,它是 ES6 才出现的函数,比如 IE:

那么如果我们用了这种方法之后,是不是在低级浏览器上也能使用 map 了:

就是这么简单。

当然这时候还有个问题,就是我们自己实现的方法,虽然说功能是一致的,但是它的性能远不如原生提供的方法来的高。

所以,我们可以这么写:

说的直白点,如果本身你就有 map,那就用系统那套,因为系统那套性能高。

如果没有,Array.prototype.map 是 undefined,那我就用后面自己实现的这个。

这样的话,既照顾了低级浏览器,高级浏览器下面也还是用的系统那套,性能不会受到影响。

这个就是原型的一个用法。

到现在,我们基本上来说,就算是理解了原型各种各样的一些东西,那么我们来总结一遍:

原型在以前的版本当中,其实它更主要的是帮我们来实现这个类本身。

而现在,我们有了 class 之后,理论上,在我写这个类的时候,其实是用不着原型了,但是原型它自己也有很大的作用。

原型的作用其实非常的简单,就是给这个类去添加方法。

它不是去给某一个实例加,它是给类加东西,加完之后,所有的实例它都会有。

当然这里面我们不光要了解它的一个作用,我们也得知道它为什么能起这个作用,这个也是一件很重要的事。

这个过程其实特别的简单,当然这个过程不用我们来完成,都是由 js,由语言自身来支撑这些功能的,不用我们来做。

就是说它其实会往上找,就这三个字,就能体现它这功能的原理。

说白了就是说,它会先去找实例本身,如果有,那自然最好。如果没有,没关系,我可以找实例所对应的那个类,它的原型,然后找它的原型上有没有。如果还没有的话,它还会继续往上再来找,这样的一个过程。

所以说原型这个东西,其实也并不复杂。

然后原型的功能,或者说我们什么时候去用它,它一般来说有 2 个大用途:

  • 第一个,我可以去给这个类去添加一些公共的方法
  • 第二个,给系统类去打补丁。你有就用你的,因为系统自带的,肯定性能最高,没有就再用我这个。
相关推荐
AI原吾2 小时前
掌握Python-uinput:打造你的输入设备控制大师
开发语言·python·apython-uinput
机器视觉知识推荐、就业指导2 小时前
Qt/C++事件过滤器与控件响应重写的使用、场景的不同
开发语言·数据库·c++·qt
毕设木哥2 小时前
25届计算机专业毕设选题推荐-基于python的二手电子设备交易平台【源码+文档+讲解】
开发语言·python·计算机·django·毕业设计·课程设计·毕设
珞瑜·2 小时前
Matlab R2024B软件安装教程
开发语言·matlab
weixin_455446172 小时前
Python学习的主要知识框架
开发语言·python·学习
孤寂大仙v2 小时前
【C++】STL----list常见用法
开发语言·c++·list
她似晚风般温柔7893 小时前
Uniapp + Vue3 + Vite +Uview + Pinia 分商家实现购物车功能(最新附源码保姆级)
开发语言·javascript·uni-app
咩咩大主教3 小时前
C++基于select和epoll的TCP服务器
linux·服务器·c语言·开发语言·c++·tcp/ip·io多路复用
FuLLovers3 小时前
2024-09-13 冯诺依曼体系结构 OS管理 进程
linux·开发语言
Jiaberrr4 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app