面试题答案
一键面试生命周期
- 形参:在
outer
函数被调用时,形参被创建并初始化,随着outer
函数执行完毕,正常情况下形参的生命周期结束并被销毁。但是由于闭包的存在,inner
函数引用了outer
函数的形参,使得这些形参的生命周期被延长,直到引用它们的inner
函数对象也不再被引用,才会被垃圾回收机制回收。 - 实参:实参在调用
outer
函数时被传递进来,其生命周期取决于调用者的作用域。如果实参是局部变量,在调用者函数结束时,若没有其他引用,实参所占用的内存可能会被回收。但只要outer
函数内的闭包(inner
函数)存在,实参通过形参间接被闭包引用,就不会被过早回收。
作用域
- 形参:形参的作用域局限于
outer
函数内部。然而,由于闭包的特性,在inner
函数中也可以访问到这些形参。inner
函数可以看作是在outer
函数的作用域链基础上创建的,它能够访问到outer
函数的作用域中的变量,包括形参。 - 实参:实参本身的作用域在调用者的作用域内。但当实参传递给
outer
函数的形参后,在outer
函数及其内部的闭包(inner
函数)中,通过形参对实参进行访问和操作,尽管这种访问是间接的。
可能出现的问题
- 内存泄漏:如果闭包一直存在,不会被垃圾回收机制回收(例如将闭包函数赋值给一个全局变量),那么被闭包引用的形参以及相关数据结构也不会被回收,可能导致内存泄漏。因为即使
outer
函数执行完毕,形参所占用的内存由于闭包的引用而无法释放。 - 变量值意外改变:如果闭包函数在不同时间被调用,由于形参的生命周期被延长,可能会出现形参的值被意外改变的情况。例如在
outer
函数执行过程中,对形参进行了修改,后续闭包函数调用时,使用的是修改后的值,这可能与预期不符。
避免问题的方法
- 解决内存泄漏:
- 确保闭包函数在不再需要时,及时解除对其的引用。例如,如果闭包函数被赋值给一个全局变量,在适当的时候将该全局变量设置为
null
,以便垃圾回收机制能够回收闭包以及其引用的形参等资源。 - 在闭包内部,避免对外部变量进行不必要的强引用。可以使用
WeakMap
等弱引用数据结构来存储外部对象的引用,这样当外部对象没有其他强引用时,能够被垃圾回收机制正常回收。
- 确保闭包函数在不再需要时,及时解除对其的引用。例如,如果闭包函数被赋值给一个全局变量,在适当的时候将该全局变量设置为
- 防止变量值意外改变:
- 在
inner
函数内部,如果只是读取形参的值,不进行修改,可以将形参的值复制到inner
函数内部的局部变量中使用。这样即使外部outer
函数对形参进行了修改,也不会影响inner
函数中使用的值。 - 如果
outer
函数中形参的值需要保持不变,可以在outer
函数内部对形参进行深拷贝,将拷贝后的值传递给inner
函数使用。这样inner
函数操作的是拷贝后的值,不会影响原始形参的值。
- 在