例1 :
let a =3;let b =addTwo(a);console.log(b);functionaddTwo(x){let ret = x +2;return ret;};1.解析真个js代码找到可提升的变量functionaddTwo(x),将addTwo(x)变量提升到最上面,之后找变量,将a和b变量放到栈存死区里面script
2.执行代码,将3赋值给了变量a,其次找到被提升变量的函数addTwo(a),通过作用域链,将3传入addTwo(3),进入函数体functionaddTwo(x)内,形参x也会被3赋值
然后let ret = x +2,计算成5,赋值给了ret,return返回,出函数5被赋值给了b,同时x和ret被销毁。然后调用console.log(b),b的值是5所以打印了5
我们在函数执行上下文中有变量,在全局执行上下文中有变量。JavaScript的一个复杂之处在于它如何查找变量,沿着作用域链查找而不是严格按照上下文的方式查找。
例2 :
let val =7functioncreateAdder(){functionaddNumbers(a, b){let ret = a + b
return ret
}return addNumbers
}let adder =createAdder()let sum =adder(val,8)
console.log('example of function returning a function: ', sum)1.adder,val,sum三个进入栈存死区,script,functioncreateAdder()函数变量提升,那么为何functionaddNumbers(a, b)不提升呢?因为不在当前的作用域内
2.let adder =createAdder()这里函数调用,像上线文寻找这个函数,然后进入,当进入的一瞬间functionaddNumbers(a, b)函数变量提升到当前作用域顶部。并且返回一个函数addNumbers,将返回函数的定义赋值给了adder。同时addNumbers被销毁
3.调用adder传入两个值(val,8),当前作用域val是7,将7传入functionaddNumbers(a, b) ,进行 ret = a + b赋值7+8 ,返回ret。函数执行完毕,a,b,ret销毁。最后结果函数的定义赋值给了sum,打印sum
真正的闭包
真正的闭包
functioncreateCounter(){let counter =0#2.定义了一个变量,这里为何用let不用var,因为let具有穿透函数的功能,var不具备这个功能,如果不用let,那么这个就变成全局变量了。
constmyFunction=function(){#3.定义函数
counter = counter +1#4.运算
return counter#5.返回函数,
}return myFunction#6.返回函数,并销毁
}const increment =createCounter()#1.执行进入
const c1 =increment();#1const c2 =increment();#2#为什么这里是2,因为闭包机制,counter被存储在increment()中,没有销毁,所以每次运算数字会网上叠加
const c3 =increment();#3
console.log('result', c1, c2, c3)1.c1,increment进入栈存死区,将functioncreateCounter() 提升到当前作用域最顶端,
真正的闭包
无论何时声明新函数并将其赋值给变量,都要存储函数定义和闭包。闭包包含在函数创建时作用域中的所有变量,它类似于背包。函数定义附带一个小背包,它的包中存储了函数定义创建时作用域中的所有变量。
在全局作用域中创建的函数创建闭包,但是由于这些函数是在全局作用域中创建的,所以它们可以访问全局作用域中的所有变量,闭包的概念并不重要。
functiona(){} 在全局作用域函数,在所有地方包含了所有的作用域,所以闭包的概念就不重要了
当函数返回函数时,闭包的概念就变得更加重要了。返回的函数可以访问不属于全局作用域的变量,但它们仅存在于其闭包中的内容,放了什么才能返回什么。
闭包的方法是通过背包的类比。当一个函数被创建并传递或从另一个函数返回时,它会携带一个背包。背包中是函数声明时作用域内的所有变量。
注意事项:闭包容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包。
MDN对闭包的官方解释:
A closure is the combination of a function bundled together(enclosed)with references to its surrounding state(the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
闭包是绑定在一起(封闭)的函数及其周围状态(词汇环境)的引用的组合。换句话说,闭包允许您从内部函数访问外部函数的scope。在JavaScript中,每次创建函数时,都会在函数创建时创建闭包。
闭包绝不可以理解为在函数里返回函数的函数。这是引发闭包问题的原因,而不是闭包概念的本身
思考题
1.let c =4;constaddX=x=>n=> n + x;const addThree =addX(3);let d =addThree(c);console.log('result', d)#箭头函数addX=x=>n=> n + x函数的嵌套, 当执行addx(3)的时候,constaddThree是箭头函数n=> n + x,x成为了3,然后c给了n,结果就是72.let c =4;constaddX=x=>n=>(x += n,n + x);const addThree =addX(3);let d =addThree(c);console.log('result', d)
#113.var data =[];for(var i =0; i <3; i++){data[i]=function(){console.log(i);};};data[0]();data[1]();data[2]()#3,3,34.var data =[];for(let i =0; i <3; i++){data[i]=function(){console.log(i);};};data[0]();data[1]();data[2]()#0,1,25.var result =[];let a =3;functionfoo(a){let total =0;for(var i =0; i <3; i++){result[i]=function(){total += i * a;console.log(total);}}}foo(1);result[0]();result[1]();result[2]();#3,6,9var result =[];let a =3;functionfoo(a){let total =0;for(let i =0; i <3; i++){result[i]=function(){total += i * a;console.log(total);}}}foo(1);result[0]();result[1]();result[2]();#0,1,36. 在4的环境基础下执行 result[0]();result[1]();result[2]();