1.闭包概念
定义:能够访问到其他函数作用域中的对象的函数,称为闭包
误区:闭包不是函数里面嵌套函数
2.闭包的两种写法
2.1函数嵌套写法
// 闭包写法1: 内部嵌套函数
function fn(){
var a = 1;
function fn2(){
console.log(a+1);
}
fn2();
}
fn(); //2
2.2回调函数写法
// 闭包写法2:回调函数写法
function fn(cb){
var a = 1;
return cb;
}
function callback(a){
console.log(a+1);
}
fn(callback(2));//3
3.完整示例及分析
分析过程
- 1. 全局执行上下文创建作用域链,作用域链包含了全局变量对象 [作用域链:[全局变量对象]]
- 2. books函数调用时创建作用域链,具体操作为先复制全局的作用域,然后创建活动对象AO推入当前作用域的顶端 [作用域链:[book活动变量, 全局变量对象]],books函数执行完毕后当前作用域会被销毁
- 3. bag函数调用创建作用域,首先复制上层作用域[作用域链:[book活动变量, 全局变量对象]],然后创建活动对象AO推入当前作用域的顶端[作用域链:[匿名函数func, book活动变量, 全局变量对象]],bag函数执行完毕后当前作用域会被销毁
function books() {
var book = '书包里面的书本'
return function () {
console.log(book)
}
}
// 1.全局作用域中,创建books()函数前会创建一个全局对变量对象GLobal,放在Scopes中
// 2.调用函数books()时,会先复制一份全局的GLobal对象,然后会创建一个局部作用域对象Local,然后函数作用域作为AO对象推入Local作用域顶端
// 3.books函数执行完成后,Local作用域被销毁,只剩下Global全局作用域
// 4.bag()函数调用时,首先会赋值一份上级books函数活动变量和全局的Global对象,然后创建一个自己的Local对象,再将自己的函数AO对象推如Local作用域链顶端
// 5.bag()执行结束后,销毁自己的Local作用域
var bag = books()
bag()
1:全局对象中创建Global
2:函数books()调用时先赋值全局Global然后创建自己的Local。books()执行完成后销毁自己的Local
3:bag()调用时先复制一份books和Global的对象,然后创建自己的Local,bag调用完成后,销毁自己的Local
4.面试中常问到的问题
- 1.如何在外部访问函数作用域内的变量,请手写一段代码进行验证
- 2.闭包为什么会造成内存的泄露 (内存泄漏在哪个环节有问题)
5.面试题
function fn() {
var arr = []
for (var i = 0; i< 10; i++) {
arr[i] = function() {
return i
}
}
return arr
}
var arr = fn()
console.log(arr[0]())
以上代码输出10。循环时不断创建function()函数,返回i,因为var存在变量预编译,所以i最终会被覆盖为10。
解决:使用自执行函数将i放到私有作用域里面就不会被外部var的i 改变。
ES6中使用let也会有块级作用域
function fn() {
var arr = []
for (var i = 0; i< 10; i++) {
(function(i){
arr[i] = function() {
return i
}
})(i)
}
return arr
}
var arr = fn()
console.log(arr[0]())