2016年9月,我在理解了JavaScript原型和原型链之后,用 Adobe Illustrator 创作了下面这副《图解JavaScript原型链》矢量图作品,并围绕这张图写了一篇题为《一张图搞懂 Javascript 中的原型链、prototype、__proto__的关系》的文章。
掘金社区文章链接:https://juejin.cn/post/6844903446294839309
这张图还保留着曾用域名的水印。
下面用文字和代码解释上图:
1、水蓝色圆圈表示 typeof 检测为 "function"
typeof Function // "function"
typeof Function.prototype // "function"
typeof Date // "function"
typeof Array // "function"
typeof Number // "function"
typeof Object // "function"
typeof Boolean // "function"
typeof String // "function"
typeof Event // "function"
typeof Error // "function"
typeof RegExp // "function"
2、桔黄色圆圈表示 typeof 检测为 "object"
typeof Math // "object"
typeof JSON // "object"
typeof Date.prototype // "object"
typeof Array.prototype // "object"
typeof Number.prototype // "Object"
typeof Object.prototype // "Object"
typeof Boolean.prototype // "Object"
typeof String.prototype // "Object"
typeof Event.prototype // "object"
typeof Error.prototype // "object"
typeof RegExp.prototype // "object"
2、null 是特殊的对象
typeof null // "object"
3、黑色三角形箭头以及黑色的曲线代表 __protp__ 属性引用了谁
Function.__proto__ === Function.prototype // true
Date.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype
Number.__proto__ === Function.prototype
Object.__proto__ === Function.prototype
Boolean.__proto__ === Function.prototype
String.__proto__ === Function.prototype
Event.__proto__ === Function.prototype
Error.__proto__ === Function.prototype
RegExp.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype // true
Date.prototype.__proto__ === Object.prototype
Array.prototype.__proto__ === Object.prototype
Number.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null // true
Boolean.prototype.__proto__ === Object.prototype
String.prototype.__proto__ === Object.prototype
Event.prototype.__proto__ === Object.prototype
Error.prototype.__proto__ === Object.prototype
RegExp.prototype.__proto__ === Object.prototype
Math.__proto__ === Object.prototype // true
JSON.__proto__ === Object.prototype // true
2016年底,HTML5 新特性 Canvas 画布引起了我的注意,一是因为 Canvas 对图形图像的操作能直接控制到像素级,而且我本身对图像处理和动效可视化感兴趣;二是因为 Canvas 很纯粹,我喜欢纯粹——仅需一个元素—— <canvas>,剩下的绘制全都交给 JS 控制。所以我在16年底开始了对 Canvas 的研究,当时在北京中科院某研究所供职,所以我有很多时间研究和尝试新的技术和效果,我给所里一套旧的网络安全竞赛系统设计了3套新“皮肤”,分别讲述了3个“故事情境”,我在实现这些界面的时候,大量使用了 Canvas 和 SVG 技术。这些经历让我发现了自己兴趣——可视化;动效设计;很享受作品从一个想法到设计阶段,再到实现阶段都由自己完成的创造的乐趣。
<canvas id="myCanvas"></canvas>
<script>
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d);
// 用 ctx 这根“画笔”在canvas画布上挥洒创意吧。
</script>
2017年年初,偶然间我在 Behance 和 Dribbble 设计社区接触到 FUI/VFX ,迅速对这种在科幻电影中才会出现的极具未来感和科技感的人机界面设计风格着迷。我开始在设计社区收集此类设计作品,并关注持续输出此类风格作品的艺术家。期待未来我有精力和能力将这些设计作品的精髓吸收借鉴到我用代码创作的作品中去。我希望自己的 FUI 作品兼具观赏性和实用性。
我在 Behance 设计社区收藏的 FUI/VFX 风格作品链接
有了对 FUI 风格的热爱和积累的经验,再加上这几年对 Canvas 绘图技术的学习小有收获。我在2016年10月画的那张帮助自己理解JS原型链的可视化图,终于在3年后的2019年9月,我用 Canvas + FUI 让原型链流动了起来。节点 + 流光效果非常适合用来可视化JavaScript原型链的“链”式引用关系。
下面就是我在2016年制作的那张简易理解JavaScript原型链的2019年 Canvas 版本。塞博朋克的配色,电线般错综缠绕的流光,节点和I/O的思想,集中体现在这一欣赏价值与实用价值并存的作品上。
除了能如上图总览JavaScript全部标准内置对象原型继承关系,观者亦可切换单个内置对象的完整原型链。
以 Function 为例。
null === Object.prototype.__proto__ // true
Object.prototype === Function.prototype.__proto__ // true
Function.prototype === Function.__proto__ // true
JS 中所有函数都能调用 apply() 、bind() 和 call() 方法,包括我们开发者创建的函数。如果我们没有给自定义的函数设置这三个方法,当调用这些方法时,原型链就起了作用,因为所有函数的原型——Function.prototype——拥有这些方法,所以函数都能调用这三个方法。
比如,console.log 函数本身没有 call 方法,但我们依然能这样做:
console.log.call(null, 'Hello AirGlass');
// 因为 console.log 函数的原型链上有 call 方法
console.log.call === Function.prototype.call // true
同样,console.log 函数本身没有定义 toString 方法,但我们依然能这样做:
console.log.toString() // "function log() { [native code] }"
但是,上面 console.log 调用的 toString 方法到底是来自 Object.prototype 还是来自 Function.prototype 呢?答案是,来自 Function.prototype。留意上图的 __proto__ 属性,原型链查找是有先后顺序的,先从近处找,再往远处找,找到为止,找不到就是 undefined。
console.log.toString === Function.prototype.toString // true
console.log.toString === Object.prototype.toString // false
再放一张JSON的,JSON不是构造函数,它的原型是 Object.prototype。
再放一张 Number 的。