作者 | Jeskson

来源 | 达达前端小酒馆

1

究竟是怎么样的一道面试题,能让我拿出来说说呢?下面请看代码:

  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(c) {
  5. return fun(c,a);
  6. }
  7. };
  8. }
  9. var d = fun(0); d.fun(1); d.fun(2);
  10. d.fun(3);
  11. var d1 = fun(0).fun(1).fun(2).fun(3);
  12. var d2 = fun(0).fun(1);
  13. d2.fun(2);
  14. d2.fun(3);













  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(c) {
  5. return fun(c,a);
  6. }
  7. };
  8. }
  9. var d = fun(0); d.fun(1); d.fun(2);
  10. d.fun(3);
  11. var d1 = fun(0).fun(1).fun(2).fun(3);
  12. var d2 = fun(0).fun(1); d2.fun(2);
  13. d2.fun(3);
  1. undefined
  2. VM1036:2 0
  3. VM1036:2 0
  4. VM1036:2 0
  5. VM1036:2 undefined
  6. VM1036:2 0
  7. VM1036:2 1
  8. VM1036:2 2
  9. VM1036:2 undefined
  10. VM1036:2 0
  11. VM1036:2 1
  12. VM1036:2 1
  13. {fun: ƒ}
  1. //答案:
  2. //undefined,0,0,0
  3. //undefined,0,1,2
  4. //undefined,0,1,1

JS函数分两种:具名函数(命名函数)和匿名函数。

如何判断两种函数的方法呢?

可以用fn.name来判断,如果有name就是具名函数,如果没有name就是匿名函数。

需要注意的是在IE浏览器上无法获取具名函数的name,会返回undefined的结果,而在谷歌浏览器上就可以获取。

  1. // 获取名称
  2. function getFunctionName(fun){
  3. if(fun.name !== undefined)
  4. return fun.name;
  5. var funName = fun.toString();
  6. funName = funName.substr('function '.length);
  7. funName = funName.substr(0, funName.indexOf('('));
  8. return funName;
  9. }

2

函数创建的方法有哪些?

第一种是:声明函数,声明函数方法,包括函数名和函数体。

  1. function funDa() {}

第二种是:创建匿名函数的表达式。

创建一个变量,这个变量的内容是一个函数,为匿名函数

  1. var funDa = function() {}

这样这个函数就没有了name

  1. var funDa = function(){}
  2. getFunctionName(funDa).length;
  3. // 0

第三种是:创建具名函数表达式。

  1. var funDa = function dada(){};

创建一个变量,变量赋值的内容为一个带有名称的函数。具名函数表达式的函数名只能在创建函数内部使用,函数的外层只能使用funData,dada函数名只能在创建函数内部使用。



在对象内定义函数,也是属于函数表达式。

第四种是:Function构造函数

  1. Function("alert(1)");
  2. ƒ anonymous() {
  3. alert(1)
  4. }
  5. Function("dada");
  6. ƒ anonymous() {
  7. dada
  8. }
  9. new Function("alert(1)");
  10. ƒ anonymous() {
  11. alert(1)
  12. }
  13. new Function("dada");
  14. ƒ anonymous() {
  15. dada
  16. }

Function构造函数传一个函数字符串,返回包含这个字符串命令的函数。

第五种是:自执行函数





  1. ( function(){
  2. alert(1);
  3. })();
  4. undefined
  5. ( function(){
  6. alert(1);
  7. })
  8. ƒ (){
  9. alert(1);
  10. }

  1. (function da1(){
  2. alert(1);
  3. })();

自执行函数也是“函数表达式”。

第六种是:其他

运用eval,setTimeout,setInterval等方法。

3

第一个fun函数是属于标准的具名函数声明,是新创建的函数,返回的是一个对象字面量表达式,属性一个新的Object。

这返回,对象内部包含一个fun的属性,属于匿名函数表达式,这个fun属性存放的是一个新创建匿名函数表达式,所有声明的匿名函数都是一个新函数。则第一个fun函数和第二个fun函数不同,都是新创建的函数。

4

函数作用域链的问题



对象内部的函数表达式:

  1. var d = {
  2. fn: function(){
  3. console.log(fn);
  4. }
  5. };
  6. d.fn();
  7. VM1879:3 Uncaught ReferenceError: fn is not defined
  8. at Object.fn (<anonymous>:3:21)
  9. at <anonymous>:6:3
  10. fn @ VM1879:3
  11. (anonymous) @ VM1879:6
  12. var d1 = {
  13. fn: function(){
  14. console.log("dada");
  15. }
  16. };
  17. d1.fn();
  18. VM1973:3 dada
  19. undefined

非对象内部的函数表达式:

  1. var da = function () {
  2. console.log(da);
  3. };
  4. da();
  5. VM2270:2 ƒ () {
  6. console.log(da);
  7. }
  8. undefined

使用var可以访问到存放当前函数的变量,var da,da()访问函数的变量,在对象内部不能访问到。

5

函数作用域链:

  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(c) {
  5. return fun(c,a);
  6. }
  7. };
  8. }
  9. var d = fun(0); d.fun(1); d.fun(2);
  10. d.fun(3);
  11. var d1 = fun(0).fun(1).fun(2).fun(3);
  12. var d2 = fun(0).fun(1); d2.fun(2);
  13. d2.fun(3);
  1. var d = fun(0);
  2. d.fun(1);
  3. d.fun(2);
  4. d.fun(3);
  1. undefined
  2. VM2273:2 0
  3. VM2273:2 0
  4. VM2273:2 0

第一个fun(0)在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数。即就是fun(1),fun(2),fun(3)函数独使在调用第二层fun函数,第一次调用fun(0)时,b为undefined,第二次调用fun(1),c为1,a为0。

var d = fun(0);调用的是第一层

而d.fun->fun(0).fun调用第二层

fun:function(1),return fun(1,a),fun(1,0),此时fun闭包了外层函数的a,也就是第一次调用的a=0。这样子第一层fun函数为fun(1,0),所以为0。

第一次:

  1. function fun(0,undefined) {
  2. console.log(undefined)
  3. return {
  4. fun: function(c) {
  5. return fun(c,0);
  6. }
  7. };
  8. }

fun(0),b为undefined,fun(0).fun(1),c=1,此时fun闭包外层函数的a,也就是第一次调用的a=0,即c=1,a=0,并在内部调用第一层fun函数fun(1,0),所以b=0。

  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(1) {
  5. return fun(1,0);
  6. }
  7. };
  8. }

第三次调用fun(2)时,c为2,还是调用d.fun,还是闭包了第一次调用时的a,fun(2,0)所以输出b为0。

  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(c) {
  5. return fun(c,a);
  6. }
  7. };
  8. }
  9. var d = fun(0); d.fun(1); d.fun(2); d.fun(3);

6

  1. var d1 = fun(0).fun(1).fun(2).fun(3);

从fun(0)调用第一层fun函数,返回值为一个对象,第二个fun(1)调用的是第二层fun函数,后面的也是第二层fun函数。

第一层fun(0),b为undefined,第二层.fun(1)时c为1,c=1,a=0,内部调用第一层fun函数fun(1,0),所以b为0。

调用你.fun(2)时,c为2,此时当前的fun函数不是第一次执行的返回对象,而是第二次执行的返回对象,第二次执行第一层fun函数是:

fun(1,0),a=1,b=0。第三次执行fun函数,c=2,a=1

  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(1) {
  5. return fun(1,0);
  6. }
  7. };
  8. }
  9. fun(1,0),a=1,b=0。第三次执行fun函数,c=2,a=1
  10. function fun(a,b) {
  11. console.log(b)
  12. return {
  13. fun: function(2) {
  14. return fun(2,1);
  15. }
  16. };
  17. }
  18. // 1
  19. function fun(2,1) a=2, b=1

第四次调用.fun(3)为c为3

  1. // a=2
  2. function fun(a,b) {
  3. console.log(b)
  4. return {
  5. fun: function(3) {
  6. return fun(3,2);
  7. }
  8. };
  9. }

7

  1. var d2 = fun(0).fun(1);
  2. d2.fun(2);
  3. d2.fun(3);



var d2=fun(0).fun(1);

// undefined, 0

此时的return(c=1,a=0),return fun(1,0),所以b为0

d2.fun(2);

第三次调用.fun(2),c为2


  1. // c为2,a=1,b=0
  2. function fun(a,b) {
  3. console.log(b)
  4. return {
  5. fun: function(2) {
  6. return fun(2,a);
  7. }
  8. };
  9. }
  10. 所以return fun(2,1)
  11. function fun(a=2,b=1),所以为 1

d2.fun(3),c为3,还是调用的第二次的返回值,最终调用第一层的

fun(a,b)

  1. // c为3,a=1,b=0
  2. function fun(a,b) {
  3. console.log(b)
  4. return {
  5. fun: function(3) {
  6. return fun(3,a);
  7. }
  8. };
  9. }
  10. 所以return fun(3,1)
  11. function fun(a=3,b=1),所以为 1

注意这里的:

  1. // c为3,a=1,b=0
  2. 这是调用这个代码的结果
  3. a=1,b=0
  1. var d2 = fun(0).fun(1);

好了,这样就知道大概的答案和讲解了:

8

总的来说,你明白了!讲的好辛苦,给个赞哦!求奖励,我来了


  1. function fun(a,b) {
  2. console.log(b)
  3. return {
  4. fun: function(c) {
  5. return fun(c,a);
  6. }
  7. };
  8. }
  9. var d = fun(0); d.fun(1); d.fun(2);
  10. d.fun(3);
  11. var d1 = fun(0).fun(1).fun(2).fun(3);
  12. var d2 = fun(0).fun(1); d2.fun(2);
  13. d2.fun(3);
  14. undefined
  15. VM1036:2 0
  16. VM1036:2 0
  17. VM1036:2 0
  18. VM1036:2 undefined
  19. VM1036:2 0
  20. VM1036:2 1
  21. VM1036:2 2
  22. VM1036:2 undefined
  23. VM1036:2 0
  24. VM1036:2 1
  25. VM1036:2 1
  26. {fun: ƒ}

一名喜爱编程技术与专注于前端的程序员,将web前端领域、数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。分享web前端相关的技术文章、工具资源,精选课程、热点资讯。

推荐阅读

1、你知道多少this,new,bind,call,apply?那我告诉你

2、为什么学习JavaScript设计模式,因为它是核心

3、一篇文章把你带入到JavaScript中的闭包与高级函数

4、大厂HR面试ES6中的深入浅出面试题知识点

❤️ 不要忘记留下你学习的脚印 [点赞 + 收藏 + 评论]

作者Info:

【作者】:Jeskson

【原创公众号】:达达前端小酒馆。

【福利】:公众号回复 “资料” 送自学资料大礼包(进群分享,想要啥就说哈,看我有没有)!

【转载说明】:转载请说明出处,谢谢合作!~

大前端开发,定位前端开发技术栈博客,PHP后台知识点,web全栈技术领域,数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。谢谢支持,承蒙厚爱!!!


若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。


请点赞!因为你们的赞同/鼓励是我写作的最大动力!

欢迎关注达达的CSDN!

这是一个有质量,有态度的博客

一篇常做错的经典JS闭包面试题的更多相关文章

  1. 大部分人都会做错的经典JS闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...

  2. 经典JS闭包面试题(来理解闭包)(转)

    转载地址:http://www.cnblogs.com/xxcanghai/p/4991870.html 先看代码: function fun(n,o) { console.log(o) return ...

  3. 面试题常考&必考之--js闭包特性和优缺点 (外加小例子)

    当内部函数被返回到外部并保存时,一定会产生闭包.闭包会产生原来的作用域链,不释放. 闭包,可以理解为,写一个函数,然后产生闭包的这种现象. 概念: 基础: 主要是:add    reduce  被返回 ...

  4. javascript深入理解js闭包(转)

    javascript深入理解js闭包 转载  2010-07-03   作者:    我要评论 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...

  5. 一道经典JS面试题

    超过80%的候选人对下面这道JS面试题的回答情况连及格都达不到.这究竟是怎样神奇的一道JS面试题?他考察了候选人的哪些能力?对正在读本文的你有什么启示? 不起眼的开始 招聘前端工程师,尤其是中高级前端 ...

  6. web报表工具FineReport使用中遇到的常见报错及解决办法(二)

    web报表工具FineReport使用中遇到的常见报错及解决办法(二) 这里写点抛砖引玉,希望大家能把自己整理的问题及解决方法晾出来,Mark一下,利人利己. 出现问题先搜一下文档上有没有,再看看度娘 ...

  7. java常见报错及解决

    Java常见报错信息: Java 常见异常种类 Java Exception: 1.Error  2.Runtime Exception 运行时异常 3.Exception  4.throw 用户自定 ...

  8. 03:git常见报错解决方法

    1.1 git常见报错解决方法 1.warning: LF will be replaced by CRLF in .idea/workspace.xml. 参考博客:https://www.cnbl ...

  9. Django 连接 MySQL 数据库及常见报错解决

    目录 Django 连接 MySQL数据库及常见报错解决 终端或者数据库管理工具连接 MySQL ,并新建项目所需数据库 安装访问 MySQL 的 Python 模块 Django 相关配置 可能会遇 ...

随机推荐

  1. oracle 根据字段分组取第一条数据及rank函数说明

    当前有这样一个需求,根据外键对子表数据进行分组,取每组中的一条数据就行了,如图: 如:COMMANDID = 26的有两条,只取一条数据. sql语句: select * from(select SY ...

  2. 防止用iframe调用网页dom元素

    <system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Optio ...

  3. Object中defineProperty数据描述

    Object.defineProperty是对对象中的属性进行数据描述的 使用语法: Object.defineProperty(obj,prop,descriptor) 使用示例: var data ...

  4. css 平行四边形

    平行四边形 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  5. Redux 进阶之 react-redux 和 redux-thunk 的应用

    1. react-redux React-Redux 是 Redux 的官方 React 绑定库. React-Redux 能够使你的React组件从Redux store中读取数据,并且向 stor ...

  6. Tensorflow快餐教程(1) - 30行代码搞定手写识别

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/lusing/article/details ...

  7. .Net core3.0 集成swagger5.0上传文件

    .Net core 3.0已经更新了,相信有挺多博主大佬们都更新了如何在.Net core3.0使用swagger,这里就不详细说了. 我们知道,如果.net core 2.x使用swagger上传文 ...

  8. 使用NB Exploit Kit攻击的APT样本分析——直接看流程图,就是网页挂马,利用java和flash等漏洞来在你主机安装和运行恶意软件

    使用NB Exploit Kit攻击的APT样本分析 from:https://cloud.tencent.com/developer/article/1092136 1.起因 近期,安恒工程师在某网 ...

  9. php中的设计模式---工厂模式及单例模式

    这两个练习放在一起处理. 在python中,这些模式都有的. 要记得三大类模式:创建型,结构型,行为型. NotFoundException.php <?php namespace Bookst ...

  10. 关于 " i=i++"是否等价于"i=i+1"的问题探讨 -Java版/C版

    如题 (总结要点) 假设i=1 ,那么 i=i++ 的结果也就是2, 对吗? 不对,是1. -总结如下: 测试过程如下: 1.java版 public class Test { public stat ...