面试题答案
一键面试可能出现的问题
- 悬垂引用(Dangling Reference):当外部变量生命周期结束,但闭包仍然持有对该变量的引用时,闭包中的引用就成为悬垂引用。后续使用该闭包可能导致程序崩溃或未定义行为,因为此时引用的内存已被释放。
解决方法 - 生命周期标注
-
Rust语言示例:
fn main() { let s = String::from("hello"); let closure = create_closure(&s); println!("{}", closure()); } fn create_closure<'a>(s: &'a str) -> impl Fn() -> &'a str { move || s }
在这个Rust示例中,
<'a>
是生命周期参数,它标注了s
的生命周期和闭包返回值的生命周期是相同的。move
关键字确保闭包获取s
的所有权(在这种情况下是引用的所有权),避免了悬垂引用的问题。 -
Swift语言示例:
class MyClass { let value: String init(_ value: String) { self.value = value } } func createClosure() -> () -> String { let obj = MyClass("example") return { return obj.value } } let closure = createClosure() print(closure())
在Swift中,闭包会自动捕获
obj
,由于obj
被闭包捕获,obj
的生命周期会延长到闭包不再被使用为止。这里没有显式的生命周期标注,但是Swift的自动引用计数(ARC)机制确保了对象不会在被闭包使用时被释放。 -
JavaScript示例(虽然JavaScript没有像Rust那样严格的生命周期标注,但有类似概念):
function createClosure() { let s = "hello"; return function() { return s; }; } const closure = createClosure(); console.log(closure());
在JavaScript中,闭包会自动捕获外部变量
s
。JavaScript的垃圾回收机制会确保只要闭包存在,s
所占用的内存就不会被释放。但是,如果不小心管理闭包,可能会导致内存泄漏,例如如果闭包一直存在于内存中(比如作为全局变量),而其中捕获的变量不再需要更新,但仍然占用内存空间。