__proto__ 和 prototype 深度剖析

为什么要进行深度剖析

proto 和 prototype 是一个老生常谈的话题,也是作为一个前端开发人员必须搞懂的问题,且不应该有任何的折扣,这是通往高级前端开发的必经之路和必备技能。虽然实际开发中用的并不多,但是当你学习一些新的js框架乃至自己封装一些高性能的组件时是及奇有用的。
网上的相关资料很多,讲解也是多种多样,但是至今都没有看到一篇自己满意的文章,于是在翻阅众多资料以及相关书籍之后,作者我一鼓作气,撸起袖子——就是干。

在正式进行讲解时,各位看官务必弄清楚以下两个问题。

什么是 proto(高层三中被称之为 [[Prototype]]) ? 什么是prototype

  • 初学javascript我们就知道,万物皆对象,proto__符号是用于指向someObject的原型,所有的对象都具有属性__proto
  • prototype 是 function 特有的属性,用来指向 function 的 原型

请允许我先上一张从百度搜索出来的图:
__proto__ 和 prototype 深度剖析_第1张图片

看到这里我知道你开始吐槽了,和网上看到的大部分一样,然后表示一脸懵逼
别急,接下来我会为你一一剖析每一个过程

为了配合讲解,先上一段代码,边思考边往下看(程序猿的世界里最讲道理的就是这东西了):

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 构建了一个新的对象

构造函数继承


接下来我们结合代码来看如下图中的关系:
__proto__ 和 prototype 深度剖析_第2张图片

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 之间的关系,接下来,我们在身下的部分,如图所示:

__proto__ 和 prototype 深度剖析_第3张图片

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域名解析

其它相关

  • 前端学习资料下载
  • 技术体系分类
  • react-native 实战项目学习
  • react-naitve 采坑笔记

欢迎各位看官的批评和指正,共同学习和成长
希望该文章对您有帮助,你的 支持和鼓励会是我持续的动力

你可能感兴趣的:(前端技术架构)