宝哥软件园

容易被忽略的javascript面试问题七问七答

编辑:宝哥软件园 来源:互联网 时间:2021-09-17

这个问题是我提出的一套前端面试问题中的最后一个,用来评估面试官的JavaScript综合能力。可惜这两年几乎没人能答对,不是因为太难,而是因为大部分面试官太看不起他了。

主题如下:

function Foo(){ getName=function(){ alert(1);};归还这个;} foo . getname=function(){ alert(2);};foo . prototype . getname=function(){ alert(3);};var getName=function(){ alert(4);};函数getName(){ alert(5);}//请写出以下输出:foo . getname();getName();Foo()。getName();getName();new Foo . GetName();新Foo()。getName();新的新Foo()。getName();答案是:

function Foo(){ getName=function(){ alert(1);};归还这个;} foo . getname=function(){ alert(2);};foo . prototype . getname=function(){ alert(3);};var getName=function(){ alert(4);};函数getName(){ alert(5);}//答案:foo . getname();//2GetName();//4Foo()。getName();//1 getname();//1 new Foo . GetName();//2new Foo()。getName();//3新的新Foo()。getName();//3这个话题是我之前的开发经验和各种JS坑的结合。这个题目涉及到很多知识点,包括变量定义提升、这个指针指向、运算符优先级、原型、继承、全局变量污染、对象属性和原型属性优先级等等。

这个问题包含7个小问题,可以分开说。

第一个问题

看看这个问题前半部分做了什么。首先定义一个名为Foo的函数,然后为Foo创建一个名为getName的静态属性来存储一个匿名函数,然后为Foo原型对象新创建一个名为getName的匿名函数。之后,通过函数变量表达式创建了一个名为getName的函数,最后声明了一个名为getName的函数。

第一个问题,Foo.getName,自然是访问Foo函数上存储的静态属性,自然是2,所以没什么好说的。

第二个问题

第二,直接调用getName函数。由于是直接调用的,是访问上面作用域中调用getName的函数,所以和1、2、3无关。无数面试官对这个问题回答了5。这里有两个坑,一个是变量声明的提升,一个是函数表达式。

1.变量声明提升。

也就是说,所有声明的变量或声明的函数将被提升到当前函数的顶部。

例如较低的代码:

console.log('x '在窗口中);//true var x;x=0;当执行代码时,js引擎将声明语句提升到代码的顶部,变成:

var x;console.log('x '在窗口中);//truex=0;2.函数表达式。

var getName和function getName都是声明语句,不同的是var getName是函数表达式,而function getName是函数声明。关于JS中的各种函数创建方法,我们可以看到大多数人都会犯的经典JS闭包面试问题。

函数表达式最大的问题是js会将这段代码分成两行,分别执行。

例如,以下代码:

console . log(x);//输出:函数x(){ } var x=1;函数x(){}实际执行的代码是:首先将var x=1拆分为var x;且x=1;两行,然后var x;和函数x(){}被提升到顶部,成为:

var x;function x(){ } console . log(x);x=1;因此,final函数声明的x覆盖了变量声明的x,日志输出就是x函数。

同样,原始标题中代码的最终执行是:

function Foo(){ getName=function(){ alert(1);};归还这个;} var getName//只提升变量声明函数getName(){ alert(5);}//提升函数声明,涵盖var foo . getname=function(){ alert(2)的声明;};foo . prototype . getname=function(){ alert(3);};getName=function(){ alert(4);};//最终赋值再次覆盖函数getName声明getName();//最终输出4第三个问题。

Foo()。第三个问题的getName();先执行Foo函数,然后调用Foo函数返回值对象的getName属性函数。

Foo函数getName=function () {alert (1)的第一句话;};是一个函数赋值语句,注意它没有var声明,所以先在当前Foo函数作用域中查找getName变量,没有,在当前函数作用域的上层,也就是外部作用域中查找getName变量,在第二个问题中找到alert(4)函数,将这个变量的值赋给function(){alert(1)}。

事实上,外部作用域中的getName函数在这里进行了修改。

注意:如果窗口对象仍然没有在这里找到,它将总是被找到。如果窗口对象中没有getName属性,将在窗口对象中创建一个getName变量。

之后Foo函数的返回值就是这个,博客公园里有很多关于JS这个问题的文章,这里就不多说了。

简单来说,这个的方向是由其函数的调用方法决定的。在这里的直接调用模式中,这指向窗口对象。

Foo函数返回一个window对象,相当于执行window.getName(),window中的getName已经修改为alert(1),所以最终会输出1。

这里考察两个知识点,一个是可变范围的问题,一个是这个指向的问题。

第四个问题

直接调用getName函数相当于window.getName(),因为这个变量已经被Foo函数修改过了,结果和第三个问题一样,就是1。

第五个问题

第五个问题是new foo . getname();这里是js的运算符优先级问题。

通过查阅上表,我们可以知道点(。)高于新操作,所以相当于:

new(Foo . GetName)();因此,getName函数实际上是作为构造函数执行的,会弹出2。

问题六

第六个问题是new Foo()。getName()。首先,看到操作员优先级括号高于新的,实际执行是。

(new Foo())。然后getName()先执行Foo函数,但是此时Foo有一个返回值作为构造函数,所以这里我们需要解释js中构造函数的返回值。

构造函数的返回值。

在传统语言中,构造函数不应该有返回值,实际执行的返回值是构造函数的实例化对象。

在js中,构造函数可能有返回值,也可能没有。

1.如果没有返回值,实例化的对象将像其他语言一样返回。

2.如果有返回值,检查返回值是否为引用类型。如果是非引用类型,如基本类型(字符串、数字、布尔值、空值、未定义),则等于无返回值,实际返回其实例化对象。

3.如果返回值是引用类型,则实际返回值是该引用类型。

在最初的问题中,这是返回的,这最初表示构造函数中的当前实例化对象,因此Foo函数最终返回实例化对象。

然后调用实例化对象的getName函数,因为在Foo构造函数中没有给实例化对象添加任何属性,然后在当前对象的原型中查找getName,并找到它。

最终输出为3。

第七个问题

第七个问题,新的新的Foo()。getName();这也是运营商优先考虑的问题。

实际实现是:

new ((new Foo())。getName());首先初始化Foo的实例化对象,然后在其原型上使用getName函数作为构造函数再次创建新的。

最终结果是3。

最后

就回答情况而言,第一个问题能100%正确回答,第二个问题只有50%左右正确,第三个问题不能正确回答,第四个问题非常非常少。其实这个题目没有太多刁钻的用法,只是可能遇到的一些场景,大部分有1到2年工作经验的人应该是完全正确的。

只能说有些人太急躁,太轻蔑。希望大家能通过这篇文章了解js的一些特性。

更多资讯
游戏推荐
更多+