前言
这是一道十分经典的面试题,涵盖了从函数的基本概念、运算符优先级,到作用域链、原型链、this关键字、new关键字等根底知识点考查,能够说能残缺答对 JS 根底才算过了关,本文就带大家回顾并分析这道面试题,应该是全网最具体的文章了,这次彻底搞懂它。
// afunction Foo () { getName = function () { console.log(1); } return this;}// bFoo.getName = function () { console.log(2);}// cFoo.prototype.getName = function () { console.log(3);}// dvar getName = function () { console.log(4);}// efunction getName () { console.log(5);}
按程序执行后别离输入什么?
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
先本人尝试写出后果再看答案,前面是具体解析。
答案
Foo.getName(); // 2getName(); // 4Foo().getName(); // 1getName(); // 1 new Foo.getName(); // 2new Foo().getName(); // 3new new Foo().getName(); // 3
解析
1. Foo.getName()
这一问首先考查的是函数的基本概念:在 JS 中函数是第一类对象,也被称作"一等公民",这是因为函数领有对象所领有的全副性能。所以这里的 Foo.getName()
能够看作是调用了 Foo
对象上的属性,在题目中的 b 处有其定义,故后果输入 2 。
2. getName()
这里调用的 getName
在上下文中被定义了两次,一次是通过变量申明,一次是函数申明,故这一问考查的是变量申明晋升与函数申明晋升,申明提前会让申明晋升到代码的最上层,而函数再一次施展了它"一等公民"的特权:函数申明晋升比变量申明晋升更高,所以这一问理论执行代码可看作:
function getName() { console.log(5);};var getName;getName = function () { console.log(4);};
两者申明独特晋升之后,变量的赋值操作最初执行,所以调用 getName()
输入的后果是 4 。
3. Foo().getName()
和第一问相比看似只多了个括号,理论考查的内容齐全不一样。
咱们先温习一下 JS 中的运算符优先级,这是下来全副解题的根底。
-> MDN - 运算符优先级汇总表。
首先成员拜访运算从左到右执行,所以咱们要先看 Foo()
函数做了什么,依据题目 a 处的定义:
function Foo () { getName = function () { console.log(1); } return this;}
执行 Foo()
之后为 getName
赋值一个函数(留神这里的 getName
并没有 var
关键字,所以还考查了作用域链的知识点),JS 在遇到未声明的变量时会向上一级一层层查找,后面咱们晓得了变量申明会晋升,在全局作用域下 getName
是曾经被申明的了,所以执行 Foo()
的作用其实就是把全局的 getName
又赋值了新函数。
而 Foo()
自身返回了 this
,所以这一问又变成了「this.getName()
输入什么?」。这里当然也就考查了 this 关键字 的知识点,这里在全局上下文执行,this
指代window
,也就是调用了 getName()
,执行后果是后面 Foo()
赋予的新函数,所以输入了 1 。
4. getName()
因为题目条件是程序执行,所以这里通过了第三问之后全局 getName
曾经被批改过了,在上一问曾经解析完,这里毫无疑问执行输入是 1 。
5. new Foo.getName()
乍一看认为是要考查 new 关键字 了,其实并没有,它还是考查了下面提到的运算符优先级,依据优先级咱们能够得出,Foo.getName()
是会先执行的,执行完只是输入了第一问的后果,再对其执行 new
没有意义,最初输入的还是 2 。
6. new Foo().getName()
这里开始考查 new 关键字 的概念,但咱们还是要先说说这一问波及的运算符优先级问题,可能你看过其它文章解析这一问的时候会说等价于 (new Foo()).getName()
,可你晓得为什么会是这样吗?为什么第 5 问是先执行 Foo.getName()
而这一问却是先执行 new Foo()
呢?
这是因为 new
运算在优先级上有两种模式:
- 带参数列表:
new … ( … )
优先级 18 - 无参数列表:
new …
优先级 17
如果优先级不同那么按优先级最高的运算符先执行,不必思考联合性。
比方1 + 1 * 2
执行起来就是1 + (1 * 2)
如果优先级雷同则按联合性执行
比方赋值运算联合性是"从右到左",所以a = b = 1
理论为a = (b = 1)
所以这就解释了为什么这一问会是 new Foo()
先执行,咱们画个图看下:
在上一问里成员拜访优先级是18,new
(无参列表)优先级是17,优先级不同,则高优先级先执行,所以上一问先执行 Foo.getName()
;
而这一问里 new
(带参列表)优先级与成员拜访同属18,优先级雷同,并行下看联合性,new
带参时联合性不相干,所以间接执行,成员拜访联合性从左到右,所以先拿出 Foo()
执行,于是得出了下面等价于 (new Foo()).getName()
的论断。
接下来就是考查 new 相干概念了,new Foo()
以 Foo
为原型创立了一个新对象,这个实例自身并没有 geiName
这个办法,然而题目 c 处在 Foo
函数的原型上挂载过一个 getName
办法,最终实例会通过原型链拜访到 Foo.prototype.getName()
这个办法,后果输入 3 。
温习
new
关键字做了什么:
- 创立新对象并将
.__proto__
指向构造函数的.prototype
- 将
this
指向新创建的对象- 返回新对象
温习 原型链 知识点:
每个函数实例对象都有一个
__proto__
属性,__proto__
指向了prototype
,当拜访实例对象的属性或办法,会先从本身构造函数中查找,如果找不到就通过__proto__
去原型中查找。
7. new new Foo().getName()
考查的上一问其实曾经讲完了,还是一样画张图:
所以得出理论执行的是:new(new Foo().getName())
new Foo().getName()
在上一问可知实例最终拜访 Foo
原型链上的办法,最终为创立 new(Foo.prototype.getName())
的实例返回,后果输入 3 。
以上就是文章的全部内容,心愿对你有所帮忙!如果感觉文章写得不错,能够点赞珍藏,也欢送关注,我会继续更新更多前端有用的常识与实用技巧,我是茶无味de一天,心愿与你独特成长~