前端面试(四):一起来看一下这一道console.log输出

169 阅读4分钟

前言

4月10日下午14:44, 我结束了今日的两场前端暑期实习生面试,面对没有offer的情况,加之刚刚面试被问到的崩溃的心理,还有未来几天的几场重要面试,疲惫的心灵很想得到放松。在躺了一个小时后,我心想,还是不要放弃自己,只是遇到了一点点的挫折,爬起来再向前走就好。

于是我想在这个有点昏暗的下午,把上午有趣的一道面试题解析一下,希望遇到相似问题的大家以及我,不用像我今天一样被绕的头晕。(我写的有点凌乱,希望有前辈可以指正)

面试题

请看下面的js代码,并写一下输出(在浏览器的执行环境下)

var num = 1 
var obj = {num: 2 } 
obj.fn = (function (num) { 
    this.num = num * 4 
    num++ 
    return function (n) { 
        this.num += n 
        num++ 
        console.log(num) 
    }
})(obj.num) 

var fn = obj.fn 
fn(6) 
obj.fn(7) 
console.log(num)
console.log(obj.num)

思路

现在我们来捋一下这个代码的执行过程吧。

  1. var num = 1 ,声明了一个全局变量num,并将其赋值为1

  2. var obj = {num: 2 } ,声明了一个全局对象obj,其属性num的值为2

  3.  obj.fn = (function (num) { 
         this.num = num * 4 
         num++
         return function (n) { 
             this.num += n 
             num++ 
             console.log(num) 
         }
     })(obj.num) 
    

    上这段代码为obj新增了一个属性fn,并用一个立即执行函数来为其赋值,立即函数的参数numobj.num即2。num是立即执行函数的参数,因为传进来是一个值,而非引用,所以这里num的作用域是函数内部的作用域,num的变化不会影响到全局作用域中的numobj.num

    this.num = num * 4,这里的this是指向全局对象的,因为立即执行函数是在全局作用域下执行的window.num = this.num = 立即执行函数的参数num * 4 = 2 * 4 = 8。

    num++,这里立即执行函数num的值为2++=3。

    在返回的闭包函数中,函数接收一个参数n,目前看来,闭包函数是真正赋值给obj的函数。

    函数中this指向的是函数调用者,假设闭包函数是被obj调用执行的话,那么闭包函数内this的指向为objthis.num += n中的this.num即 为obj.num, num++中变量num仍然为上层函数作用域中的立即执行函数参数numconsole.log(num)则会打印立即执行函数参数num的值。

    现在闭包函数还未执行,我们梳理一下这几个num变量的情况, 其中num0 === num3:

    num注释作用域
    num0全局变量num全局作用域8
    num1obj.numobj2
    num2立即执行函数的参数num立即执行函数的作用域3
    num3立即执行函数的this.num全局作用域8
    num4闭包函数的this.num函数调用者的作用域,未调用未知未知
  4. var fn = obj.fn用一个全局变量fn接收了obj.fn这个函数。

  5. fn(6)全局变量fn直接调用了方法,并传入参数n=6,在这里闭包函数的调用者为全局对象。num0 = this.num = 8(num0) + 6(n) = 14; num2 = num2++ = 4; 所以console.log输出num2为4,其余的变量情况如下:

    num注释作用域
    num0全局变量num全局作用域14
    num1obj.numobj2
    num2立即执行函数的参数num立即执行函数的作用域4
    num3立即执行函数的this.num全局作用域14
    num4闭包函数的this.num全局作用域14
  6. obj.fn(7)obj调用了fn,并传入参数n=7,在这里闭包函数的调用者为obj。num1 = this.num = 2(num1) + 7(n) = 9; num2 = num2++ = 5; 所以输出num2为5,其余的变量情况如下:

    num注释作用域
    num0全局变量num全局作用域14
    num1obj.numobj9
    num2立即执行函数的参数num立即执行函数的作用域5
    num3立即执行函数的this.num全局作用域14
    num4闭包函数的this.numobj9
  7. console.log(num)输出全局变量中的num即num0,14。

  8. console.log(obj.num)输出9。

最终的输出是 4 5 14 9

知识点

这道面试题目的代码只有十多行,但是其中考察的知识有很多。包括但不限于

  • 函数的参数作用域
  • js中this的指向问题
  • 闭包和作用域

函数的参数以及作用域

  • 函数里面的this指向函数的调用者。
  • 函数的参数存在于函数内部作用域中。

js中this指向问题

juejin.cn/post/735291…

闭包和作用域

闭包可以让外界访问到内部作用域中的变量。在这里由于闭包函数的存在,使得立即函数的参数变量num可以被继续访问到。

扩展

如果这段代码的执行环境为node,输出会不会不一样?

参考文档

  1. # 总结出五种场景下JavaScript中的this指向,覆盖项目中各种情况的this指向
  2. # this上下文就这样理解 😺🐱🐟