【第24题】三条路径理清原型链(定格记忆篇)

面试题(头条) 利用原型和原型链相关知识,画出 Object、 Function、Object.prototype、Function.prototype 四个对象间的关联关系 答案解析: 原型和原型链,是javascript这门语言中的重要概念,同时也是面试过程中的必考知识点。小编对它也是日常迷惑,每次都需要查资料、重新理逻辑,近日重新换了个角度思考,对原型和原型链有了更加深刻的认识和理解。 被忽视的点 有一些比较重要的知识点,往往容易被忽略,清醒的认识下面的几个点,对于后面理解原型和原型链有很大帮助。 1. prototype是函数对象的独有属性,同时prototype对象是一个普通对象object(Function.prototype除外,它是一个function) 在JS的世界中,对象被分为两种:普通对象(object)、函数对象(function)。 只有函数对象才有prototype属性,普通对象是没有prototype属性的。 可以通过下面代码进行验证: let obj = { name: '前端名狮' }; function foo () { console.log('my name is 前端名狮'); } console.log(obj.prototype); // undefined console.log(foo.prototype); // {constructor: foo} console.log(typeof(foo.prototype)) // object console.log(foo.prototype.hasOwnProperty('constructor')) // true 由上我们能看出: obj.prototype 输出undefined,表明普通对象没有prototype属性。 foo.prototype的输出结果是一个普通的object,同时里面包含了constructor属性,而constructor指向了foo函数本身。所以我们能得出下图结论: image 2. Object,Function 都是函数 由于原型和原型链之间的关系复杂,梳理过程中,很容易忽视Object、Function是函数,可以通过下面代码加深理解 console.log(typeof(Object)); //打印出 Function let obj = new Object(); // 可以使用new创建,表明是一个函数 console.log(typeof(Function)); let fn = new Function('console.log(88)'); // 通过new的方式创建函数,表明Function本身是一个函数 三条路径覆盖原型链 所谓的原型链,就是查找一个对象的属性时,先查找它本身是否存在该属性,如果本身不存在的话,会通过隐式原型属性__proto__ 找到它的上一级对象,,然后再通过上一级对象的__proto__查找上一级的上一级,一直到最顶层对象null,这样的一种查找途径,通过__proto__形成了一条链路。 上面提到,prototype是函数对象的特有属性,而__proto__属性任何对象有。所以原型链实际是靠__proto__属性实现的。 我们一定要牢记下面这句话,我们下面所有的内容都是基于这句话展开的,没有为什么,JS这门语言实现时就是这么指定的,记住就好了: 对象的__proto__指向的是创建它的函数的prototype __proto__相当于C++中的指针,prototype相当于指针指向的对象,该prototye对象中包含了__proto__指针,指向上一级对象。 下面我们来分析一下原型链存在的三条路径: 一. 普通对象 常见普通对象的生成其实有两种形式,如下代码: // 第一种 let obj = { name: '前端名狮' } // 第二种,通过函数对象新建 function Foo () { console.log('my name is 前端名狮'); } let foo = new Foo(); 正常情况下,两种情况实际属于同一种情况,为什么呢? new 本身经历了一个复杂的过程,正常情况返回的实际是内部创建的一个object对象,具体可以阅读【第15题】- new 操作符内部实现原理 下面我们分析一下obj,foo的原型链路径 obj 本身是一个普通对象,它的构造函数实际是Object,所以我们找到了创建obj的函数Object。具体推导流程如下面代码所示,一直到顶层null obj.constructor === Object obj.__proto__ === Object.prototype Object.prototype.__proto__ === null image foo的构造函数是Foo,所以foo.__proto__ === Foo.prototype。 由上方的分析可知,prototype是一个普通对象,所以Foo.prototype是一个普通对象object,再往上查找就回到了第一种情况Foo.prototype.__proto__ === Object.prototype,再往上就到达原型链的顶层了。 foo.constructor === Foo foo.__proto__ === Foo.prototype Foo.prototype.__proto__ === Object.prototype Object.prototype.__proto__ === null image 二. 函数对象 从函数Foo开始,分析下原型链的路径 function Foo () { console.log('my name is 前端名狮'); } Foo 是一个函数,所以它的构造函数是Function,即Foo.constructor === Function,可以得知Foo.__proto__ === Function.prototype。再往上就是Function.prototype.__proto__ === Object.prototype,剩下的就跟上面的情况一样了。 Foo.constructor === Function Foo.__proto__ === Function.prototype Function.prototype.__proto__ === Object.prototype Object.prototype.__proto__ === null image 三. Object和Function 正如前面分析的那样,Object、Function都是函数对象,这两个对象是JS对象的源头,所以单独拎出来分析。 Object本身是一个函数对象,它的构造函数是Function,即 Object.constructor === Function,所以Object.__proto__ === Function.prototype,再往上查找就是Function.prototype.__proto__ === Object.prototype,再往上就是null。 Object.constructor === Function Object.__proto__ === Function.prototype Function.prototype.__proto__ === Object.prototype Object.prototype.__proto__ === null Function本身是一个函数,它的构造函数还是一个函数,所以Function.constructor === Function,所以Function.__proto__ === Function.prototype,后面的就和上面一样了。 Function.constructor === Function Function.__proto__ === Function.prototype Function.prototype.__proto__ === Object.prototype Object.prototype.__proto__ === null image 总结 按照上面的三个路径,很容易记忆理解原型链,上面内容也基本上涵盖了原型链的大部分场景,应付面试题绰绰有余。记忆理解后,再也不用每次面试都要重新复习这个难点了。 推荐阅读 2019年前端大事件回顾:流年笑掷,未来可期 Single-Spa + Vue Cli 微前端落地指南 BAT开源项目汇总 【第22题】理解 JS 模块化 【深入vue】为什么Vue3.0不再使用defineProperty实现数据监听? 抛弃jenkins,如何用node从零搭建自动化部署管理平台 前端部署演化史 深入理解 ES6 Iterator 解读HTTP/2与HTTP/3 的新特性(推荐) 关注我 扫一扫 关注我的公众号【前端名狮】,更多精彩内容陪伴你! 【前端名狮】

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):