宝哥软件园

最新的Javascript程序员面试问题及解题方法

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

现在很多JS程序员直接在电脑上面试,解决公司提前准备好的Javascript问题,或者干脆直接写在纸上,体现程序员的想法等。边肖为大家整理了最新的JS面试问题、解答和思路。让我们来看看。

结束时间:

fun fun(n,o){ console . log(o)return { fun : fun(m){ return fun(m,n);} };} var a=fun(0);a .乐趣(1);a .乐趣(2);a .乐趣(3);//未定义,var b=fun(0)。乐趣(1)。乐趣(2)。乐趣(3);//未定义,var c=fun(0)。乐趣(1);c . fun(2);c . fun(3);//未定义,//:中的A、B、C三行输出是什么?//回答:

//a:未定义,0,0//b:未定义,0,1,2//c:未定义,0,1,1

他们没事吧?如果你得到所有正确的答案,恭喜你。js闭包问题几乎没有什么可以麻烦你的。如果回答不正确,继续分析。

JS中有几个函数

首先在此之前,要了解JS中有两种类型的函数,命名函数和匿名函数。

区分这两个函数的方法很简单,可以通过输出fn.name来判断,有名字的是命名函数,没有名字的是匿名函数

注意:在较低版本IE上无法获取命名函数的名称,会返回undefined。建议在火狐或者谷歌浏览器上测试

或者使用IE兼容的方法获取函数名:

/* * *获取指定函数的函数名(为了与IE兼容)* @ param { fun } fun任意函数*/fun获取函数名(fun) {if (fun.name!==undefined)返回fun.namevar ret=fun . ToString();ret=ret.substr('function ')。长度);ret=ret.substr(0,ret . indexof('(')));返回ret}使用上述函数测试它是否是匿名函数:

可以知道,变量fn1是命名函数,fn2是匿名函数

创建函数的几种方法

在谈完函数的类型之后,我们还需要知道在JS中创建函数有几种方法。

1.声明一个函数

声明函数最常见和标准的方法包括函数名和函数体。

函数fn1(){}

2.创建匿名函数表达式

创建一个内容为函数的变量

Var fn1=function (){}注意这个方法创建的函数是匿名的,也就是没有函数名

var fn1=function(){ };getFunctionName(fn1)。长度;//03.创建命名函数表达式

创建一个变量,其内容是一个带有名称的函数

var fn1=函数xxchanghai(){ };注意:命名函数表达式的函数名只能在创建函数中使用

也就是说,这个方法创建的函数只能在函数的外层使用fn1,而不能使用xxcanghai的函数名。xxcanghai的命名只能在创建的函数中使用

测试:

var fn1=function xxchanghai(){ console . log(' in : fn1 ',typeof fn1,' xxcanghai: ',type of xxchanghai ' ');};console.log('out:fn1 ',类型为fn1,' xxcanghai: ',类型为xxchanghai ' ');fn1();//out:fn1函数xx仓海:undefined//in:fn1函数xx仓海:function可以看到xx仓海的函数名不能在函数外使用,这是未定义的。

注意:在对象中定义var o={ fn : function (){…}}等函数也是函数表达式

4.函数构造器

您可以将函数字符串传递给函数构造函数,并返回包含该字符串命令的函数。此方法创建匿名函数。

5.自动执行功能

(function(){ alert(1);})();(函数fn1(){ alert(1);})();自执行函数属于上述“函数表达式”,具有相同的规则

6.创建函数的其他方法

当然,还有其他方法来创建或执行函数。这里就不多说了。比如一些不常见的方法,比如eval、setTimeout、setInterval等。这里不介绍,它们是非标准方法。我不想在这里过多展开。

三个好玩的功能有什么关系?

在谈完函数的类型和创建函数的方法后,可以回到主题,阅读这个面试问题。

这段代码中有三个fun函数,那么第一步就是找出这三个fun函数之间的关系,哪个函数和哪个函数是一样的。

函数fun(n,o) { console.log(o)返回{ fun : fun(m){//.} };}

先看第一个fun函数,它属于标准的命名函数声明,是一个新创建的函数。它的返回值是一个对象文字表达式,属于一个新对象。

这个新对象包含一个名为fun的属性。从上面的介绍来看,它是一个匿名函数表达式,也就是说,一个新创建的匿名函数表达式存储在属性fun中。

注意:所有声明的匿名函数都是新函数。

因此,第一个有趣的功能不同于第二个有趣的功能,是一个新创建的功能。

函数范围链问题

在我们讨论第三个有趣的函数之前,我们需要讨论一下我们是否可以访问函数表达式中存储当前函数的变量。

测试1,对象内部的函数表达式:

var o={ fn : function(){ console . log(fn);}};o . fn();//ERROR报告错误

测试2,非对象内部的函数表达式:

var fn=function(){ console . log(fn);};fn();//function(){ console . log(fn);};正确

结论是:使用var或非对象内部函数表达式,可以访问存储当前函数的变量;无法访问对象内部。

原因也很简单,因为函数作用域链的问题,使用var在外部创建一个fn变量,当然如果在内部找不到fn,可以在第一卷的作用域中找到,但是在内部创建对象时,因为fn不是在函数的作用域内创建的,所以无法访问。

所以综上所述,我们可以知道从最内层返回的fun函数不是第二层fun函数,而是最外层的fun函数。

因此,这三种娱乐功能之间的关系得到了澄清。第一个等于第三个,没有一个等于第二个。

正在调用哪个函数?

再看原问题,我们现在知道程序中有两个fun函数(第一个和第三个一样),那么下一个问题就是找出他在运行时执行的是哪个fun函数。

fun fun(n,o){ console . log(o)return { fun : fun(m){ return fun(m,n);} };} var a=fun(0);a .乐趣(1);a .乐趣(2);a .乐趣(3);//未定义,var b=fun(0)。乐趣(1)。乐趣(2)。乐趣(3);//未定义,var c=fun(0)。乐趣(1);c . fun(2);c . fun(3);//未定义,//:中的A、B、C三行输出是什么?

1.第一行a

var a=fun(0);a .乐趣(1);a .乐趣(2);a .乐趣(3);可以知道第一个fun(0)是调用一级fun函数。第二个乐趣(1)是调用前一个乐趣的返回值的乐趣函数,所以:

最后几个fun(1)、fun(2)、fun(3)都是调用二级fun函数。

因此:

o是未定义的;第一次调用fun(0)时;

当第二次调用fun(1)时,m是1。此时fun关闭外层函数的n,即第一次调用n=0,即m=1,n=0,内部调用第一层fun函数fun(1,0)。所以o是0;

第三次调用fun(2)时m为2,但仍调用a.fun,所以第一次调用时仍关闭n,所以内部调用第一层fun(2,0);所以o是0

第四次也是一样;

也就是说,最终的答案是未定义的,0,0,0

2.第二行b

var b=fun(0)。乐趣(1)。乐趣(2)。乐趣(3);//未定义,从fun(0)开始,一定是第一层fun函数调用;而他的返回值是一个对象,所以第二个fun(1)调用二级fun函数,后面的也调用二级fun函数。

因此:

o是未定义的;第一次调用第一层fun(0)时;

打电话的时候。乐趣第二次,m是1。此时fun关闭外层函数的n,即第一次调用n=0,即m=1,n=0,内部调用第一层fun函数fun(1,0);所以o是0;

什么时候?第三次调用fun(2),m为2,当前fun函数不是第一次执行的返回对象,而是第二次执行的返回对象。而第一层fun函数第二次执行时n=1,o=0,第二次n返回时关闭,所以第三次调用第三层fun函数时m=2,n=1,即调用第一层fun函数fun(2,1),所以o为1;

打电话的时候。fun(3)第四次,m是3,这关闭了第三次调用的n。同样,第一级fun函数最后叫做fun (3,2);所以o是2;

这就是最终答案:未定义,0,1,2

3.第三行c

var c=fun(0)。乐趣(1);c . fun(2);c . fun(3);//未定义,根据前面两个例子,我们可以知道:

Fun(0)执行一级Fun函数,而。fun(1)执行fun(0)返回的二级fun函数。语句结束后,fun(1)的返回值存储在C中,而不是fun(0)的返回值,所以C中的闭包也是fun(1)第二次执行的N的值。C.fun(2)执行fun(1)返回的二级fun函数,c.fun(3)也执行fun(1)返回的二级fun函数。

因此:

o是未定义的;第一次调用第一层fun(0)时;

打电话的时候。乐趣第二次,m是1。此时fun关闭外层函数的n,即第一次调用n=0,即m=1,n=0,内部调用第一层fun函数fun(1,0);所以o是0;

什么时候?fun(2)是第三次调用,m是2。此时fun的闭包为第二次调用n=1,即m=2,n=1,内部调用一级fun函数fun(2,1);所以o是1;

第四次。fun(3)是一样的,但它仍然是第二次调用的返回值,所以第一级fun函数fun(3,1)最终被调用,所以o仍然是1

这就是最终答案:未定义,0,1,1

标题:

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

第一个问题

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

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

第二个问题

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

可变申报促销

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

例如较低的代码:

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

var x;console.log('x '在窗口中);//truex=0;

函数表达式

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);}//提升函数声明,覆盖声明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的运算符优先级问题。

Js操作员优先级:

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

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

问题6

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

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

构造函数的返回值

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

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

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

2.如果有返回值,检查返回值是否为引用类型。如果是非引用类型,比如基本类型(String、Number、Boolean、NULL、Undefined),则等于没有返回值,实际返回其实例化对象。

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

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

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

最后输出3。

第七个问题

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

最后的实际实现是:

new ((new Foo())。getName());

更多资讯
游戏推荐
更多+