为什么要进行深度剖析
proto 和 prototype 是一个老生常谈的话题,也是作为一个前端开发人员必须搞懂的问题,且不应该有任何的折扣,这是通往高级前端开发的必经之路和必备技能。虽然实际开发中用的并不多,但是当你学习一些新的js框架乃至自己封装一些高性能的组件时是及奇有用的。
网上的相关资料很多,讲解也是多种多样,但是至今都没有看到一篇自己满意的文章,于是在翻阅众多资料以及相关书籍之后,作者我一鼓作气,撸起袖子——就是干。
在正式进行讲解时,各位看官务必弄清楚以下两个问题。
什么是 proto(高层三中被称之为 [[Prototype]]) ? 什么是prototype
- 初学javascript我们就知道,万物皆对象,proto__符号是用于指向someObject的原型,所有的对象都具有属性__proto
- prototype 是 function 特有的属性,用来指向 function 的 原型
请允许我先上一张从百度搜索出来的图:
看到这里我知道你开始吐槽了,和网上看到的大部分一样,然后表示一脸懵逼
别急,接下来我会为你一一剖析每一个过程
为了配合讲解,先上一段代码,边思考边往下看(程序猿的世界里最讲道理的就是这东西了):
Function.prototype.b = 666; // Funciton 中添加属性b
Object.prototype.a = 8888; // Object 中添加属性a
function foo() {
this.d = 999;
}
// 构造函数创建的实例
var f1 = new foo(); // 实例 object
console.log(f1.a); // 8888
console.log(f1.b); // undefined
console.log(foo.a); // 8888
console.log(foo.b); // 666
var o1 = new Object();
console.log(o1.a); // 8888
console.log(o1.b); // undefined
console.log(Object.a) // 8888
console.log(Object.b) // 666
- 首先,我们分别在 Function 和 Object 的原型中分别添加了属性 b 和 a
- 然后创建了一个构造函数 foo
- 通过 new 构建了一个新的对象
构造函数继承
接下来我们结合代码来看如下图中的关系:
var f1 = new foo(); // 实例 object
console.log(f1.a); // 8888
console.log(f1.b); // undefined
通过 new 构造出来的实例 f1 的 proto 指向了构造函数【foo】的原型, foo 的 proto 直接指向了 Object.prototype ,我们刚才手动在 Object 的 prototype 上添加了属性a。Object.prototype.a = 8888; 所以有了如下结果:
console.log(f1.a); // 8888
有因为 new 出来的 f1 仅仅是 Object 的实例,最终只能继承 Object原型上的属性,所以对于没有的属性 b 自然就是 undefined
console.log(f1.b); // undefined
函数继承
到此我们了解了构造函数实例、构造函数、以及构造函数 prototype 之间的关系,接下来,我们在身下的部分,如图所示:
console.log(foo.a); // 8888
console.log(foo.b); // 666
有图中标号为1的线,我们知道 foo 的 proto 指向 Function.prototype 所以就有了
Function.prototype.b = 666; // Funciton 中添加属性b
console.log(foo.b); // 666
根据图中的4号线的指向,Function.prototype 的 proto 指向 Object.prototype 所以就有了
Function.prototype.b = 666; // Funciton 中添加属性b
console.log(foo.a); // 8888
也就是说 foo 同时继承了 Function.prototype 和 Object.prototype 的属性
对象的继承
var o1 = new Object();
console.log(o1.a); // 8888
console.log(o1.b); // undefined
o1 为 Object 的实例, Object 的原型指向 Object.prototype,所以o1 自然而然的继承了 Object.prototype 的属性
整个图中最难以理解的部分就是 Object 和 Function.prototype 的关系了,为啥 对象又函数扯上关系了,也不知道当初祖师爷是怎么想的,既然如此,路一行代码看看就知道了。
console.log(Object.a) // 8888
console.log(Object.b) // 666
果然不出所料,Object 同时继承了 Object.prototype 和 Function.prototype 的属性
一开始,我们的代码就用代码展示了 这个经典的图中的 proto 和 prototype 之间的关系,而后有简单的结合代码做了展示,相信各位也已经看懂了,也了解 函数的实例、构造函数的实例、构造函数、函数以及对象之间的关系。在这里作者我需要向各位强调以下必须注意的知识点
- 所有的 Object 都有 proto 属性
- prototype 是 function 所特有的
- 通过 new 构造出来的object,无论是 new foo() 还是 new Object() 所构造出来的 object,最终都会继承 Object.prototype
- new foo() 的实例既可以继承 foo.prototype 也可以继承 Object.prototype
- new Object() 只能继承 Object.prototype
- 只有 Object 才可以继承 Function.prototype
记住以下几点,我想足够纵横 proto 和 prototype 的关系,彻彻底底的弄明白原型链之间的工种关系。测地弄明白这张图和这几行代码个人认为足够了,如果各位看官还是觉得有点吃力,好好看看 《高级程序设计第三版》PDF请自行异步这里下载 的第六章关于原型链的讲解,看完之后在回过头来看看这图,相信一定会荒原大悟。
小结
其它前端性能优化:
- 图片优化——质量与性能的博弈
- 浏览器缓存机制介绍与缓存策略剖析
- webpack 性能调优与 Gzip 原理
- 本地存储——从 Cookie 到 Web Storage、IndexDB
- CDN 的缓存与回源机制解析
- 服务端渲染的探索与实践
- 解锁浏览器背后的运行机制
- DOM 优化原理与基本实践
- Event Loop 与异步更新策略
- 回流(Reflow)与重绘(Repaint)
- Lazy-Load
- 事件的节流(throttle)与防抖(debounce
- 前端学习资料下载
- 技术体系分类
前端技术架构体系(没有链接的后续跟进):
- 调用堆栈
- 作用域闭包
- this全面解析
- 深浅拷贝的原理
- 原型prototype
- 事件机制、
- Event Loop
- Promise机制、
- async / await原理、
- 防抖/节流原理
- 模块化详解、
- es6重难点、
- 浏览器熏染原理、
- webpack配置(原理)
- 前端监控、
- 跨域和安全、
- 性能优化(参见上面性能优化相关)
- VirtualDom原理、
- Diff算法、
- 数据的双向绑定
- [TCP协议(三次握手、四次挥手)](https://blog.csdn.net/woleigequshawanyier/article/details/85223642
- DNS域名解析
其它相关
欢迎各位看官的批评和指正,共同学习和成长
希望该文章对您有帮助,你的 支持和鼓励会是我持续的动力