面试题(头条)
利用原型和原型链相关知识,画出 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函数本身。所以我们能得出下图结论:
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
- 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
二. 函数对象
从函数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
三. 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
总结
按照上面的三个路径,很容易记忆理解原型链,上面内容也基本上涵盖了原型链的大部分场景,应付面试题绰绰有余。记忆理解后,再也不用每次面试都要重新复习这个难点了。
推荐阅读
- 2019年前端大事件回顾:流年笑掷,未来可期
- Single-Spa + Vue Cli 微前端落地指南
- BAT开源项目汇总
- 【第22题】理解 JS 模块化
- 【深入vue】为什么Vue3.0不再使用defineProperty实现数据监听?
- 抛弃jenkins,如何用node从零搭建自动化部署管理平台
- 前端部署演化史
- 深入理解 ES6 Iterator
- 解读HTTP/2与HTTP/3 的新特性(推荐)
关注我
扫一扫 关注我的公众号【前端名狮】,更多精彩内容陪伴你!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。