前言
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)
思路
现在我们来捋一下这个代码的执行过程吧。
-
var num = 1
,声明了一个全局变量num,并将其赋值为1 -
var obj = {num: 2 }
,声明了一个全局对象obj,其属性num的值为2 -
obj.fn = (function (num) { this.num = num * 4 num++ return function (n) { this.num += n num++ console.log(num) } })(obj.num)
上这段代码为obj新增了一个属性fn,并用一个立即执行函数来为其赋值,
立即函数的参数num
是obj.num
即2。num
是立即执行函数的参数,因为传进来是一个值,而非引用,所以这里num
的作用域是函数内部的作用域,num
的变化不会影响到全局作用域中的num
和obj.num
。this.num = num * 4
,这里的this
是指向全局对象
的,因为立即执行函数是在全局作用域下执行的window.num
=this.num
=立即执行函数的参数num
* 4 = 2 * 4 = 8。num++
,这里立即执行函数num
的值为2++=3。在返回的闭包函数中,函数接收一个参数
n
,目前看来,闭包函数是真正赋值给obj的函数。函数中
this
指向的是函数调用者,假设闭包函数是被obj
调用执行的话,那么闭包函数内this
的指向为obj
,this.num += n
中的this.num
即 为obj.num
,num++
中变量num
仍然为上层函数作用域中的立即执行函数参数num
。console.log(num)
则会打印立即执行函数参数num
的值。现在闭包函数还未执行,我们梳理一下这几个num变量的情况, 其中num0 === num3:
num 注释 作用域 值 num0 全局变量num 全局作用域 8 num1 obj.num obj 2 num2 立即执行函数的参数num 立即执行函数的作用域 3 num3 立即执行函数的this.num 全局作用域 8 num4 闭包函数的this.num 函数调用者的作用域,未调用未知 未知 -
var fn = obj.fn
用一个全局变量fn接收了obj.fn
这个函数。 -
fn(6)
全局变量fn直接调用了方法,并传入参数n=6,在这里闭包函数的调用者为全局对象。num0 = this.num = 8(num0) + 6(n) = 14; num2 = num2++ = 4; 所以console.log输出num2为4,其余的变量情况如下:num 注释 作用域 值 num0 全局变量num 全局作用域 14 num1 obj.num obj 2 num2 立即执行函数的参数num 立即执行函数的作用域 4 num3 立即执行函数的this.num 全局作用域 14 num4 闭包函数的this.num 全局作用域 14 -
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 num1 obj.num obj 9 num2 立即执行函数的参数num 立即执行函数的作用域 5 num3 立即执行函数的this.num 全局作用域 14 num4 闭包函数的this.num obj 9 -
console.log(num)
输出全局变量中的num即num0,14。 -
console.log(obj.num)
输出9。
最终的输出是 4 5 14 9
知识点
这道面试题目的代码只有十多行,但是其中考察的知识有很多。包括但不限于
- 函数的参数作用域
- js中this的指向问题
- 闭包和作用域
函数的参数以及作用域
- 函数里面的this指向函数的调用者。
- 函数的参数存在于函数内部作用域中。
js中this指向问题
闭包和作用域
闭包可以让外界访问到内部作用域中的变量。在这里由于闭包函数的存在,使得立即函数的参数变量num可以被继续访问到。
扩展
如果这段代码的执行环境为node,输出会不会不一样?