目录
- var a=10
- var bar=function(x){
- var b=5
- foo(x+b)
- }
- var foo=function(y){
- var c=5
- console.log(a+c+y);
- }
- bar(10)


- console.log('gb'+i);
- var i=1
- foo(1)
- function foo(i){
- if(i==4){
- return
- }
- console.log('fb'+i);
- foo(i+1)//递归调用:在函数内部调用自己
- console.log('fe'+1);
- }
- console.log('ge'+i);
输出结果:

1.
- function a(){}
- var a;
- console.log(typeof a);
注意:先执行变量提升,后执行函数提升。因此,typeof a为‘function’,而不是undefined
2.
- if(!(b in window)){
- var b=1
- }
- console.log(b);
在变量中,var会变量提升。所以,b定义了但没有赋值。在if语句中,if语句不管前置条件达不达到,后置语句中的变量会声明提升,所以结果为undefined
3.
- var c=1;
- function c(c){
- console.log(c);
- var c=3
- }
- c(2)//报错
注意:在提升之前,函数已经执行完了。因此,可以看成以下代码
- var c
- function c(c){
- console.log(c);
- }
- c=1
- c(2)
1.理解
2.分类
3.作用

区别1:
区别2:
联系

- var x=10
- function fn(){
- console.log(x);
- }
- function show(f){
- var x=20
- f()
- }
- show(fn)
由作用域划分可得以下,fn()会向上找x,因此为x=10

2.
- var fn=function(){
- console.log(fn);
- }
- fn();
在全局作用域里面找变量fn,所以输出为function(){console.log(fn); }
3.
- var obj={
- fn2:function(){
- console.log(this.fn2);
- console.log(fn2);
- }
- }
- obj.fn2()
调用obj中的fn2属性,属性的内容为function,输出this.fn2是在obj中找到fn2.function(){console.log(this.fn2);console.log(fn2);} 注意fn2是属性,不是变量,在查找fn2时,window里面没有fn2这个属性,就会报错
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Documenttitle>
- <script>
- window.onload = function () {
- var btns = document.getElementsByTagName("button");
- // 遍历加监听
- for (var i = 0, length = btns.length; i < length; i++) {
- var btn = btns[i];
- btn.index = i;
- btn.onclick = function () {
- alert("第" + (this.index+1) + "个");
- //alert("第"+(i+1)+"个");
- };
- }
- for(var i=0,length=btns.length;i
- (
- function (i){
- var btn=btns[i]
- btn.onclick=function(){
- alert('第'+(i+1)+'个')
- }
- }
- )(i)
- }
- };
- script>
- head>
- <body>
- <button>测试1button>
- <button>测试2button>
- <button>测试3button>
- body>
- html>
问:alert("第"+(i+1)+"个");为什么执行出来是i=3?
答:因为事件的响应函数是点击的时候才触发调用的,在点击之前只是声明了但并没有调用过响应函数
问:for (var i = 0, length = btns.length; i < length; i++)为什么不写var =0;i
答:当循环执行一次,btns.length就需要重新计算,因此需要计算多次,这样会提高系统的复杂度。当令length=btns.length时,就只要计算一次。
1.如何产生闭包?
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
2.闭包是什么?
- (使用chrome调试查看)
- 理解1:闭包是嵌套的内部函数(绝大部分人)
- 理解2:包含被引用变量(函数)的对象(极少数人)
- 注意:闭包存在于桥套的内部函数中
3.产生闭包的条件?
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
常见的闭包
1.将函数作为另一个函数的返回值
- function fn1(){
- var a=2
- function fn2(){
- a++
- console.log(a);
- }
- return fn2
- }
- var f=fn1()
- f()//3
- f()//4
2.将函数作为实参传递给另一个函数调用
- function showDelay(msg,time){
- setTimeout(function(){
- alert(msg)
- },time)
- }
- showDelay('atguigu',2000)
闭包的作用
- 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
- 让函数外部都可以操作(读写)到函数内部的数据(变量/函数)
问题:
1.函数执行完,函数内部声明的局部变量是否还存在?
- 一般是不存在的。存在于闭包中的变量才可能存在。fn1返回的是fn3的数据,fn3内部数据是地址值指向它在堆空间的对象,fn1将fn3的地址值赋值给了f,所以函数就算释放出栈了,f也能自己去访问到fn3的对象
2.在函数外部能直接访问函数内部的局部变量吗?
- 不能,但我们可以通过闭包让外部操作它
总结:闭包也是会被回收的,需要定义一个变量来一直指向闭包函数体的地址值,让闭包一直存在
闭包的生命周期
产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
死亡:在嵌套的内部函数称为垃圾对象时
- function fn1(){
- // 此时闭包就已经产生了(函数提升,内部函数对象已经创建了)
- var a=2
- function fn2(){
- a++
- console.log(a);
- }
- return fn2
- }
- var f=fn1()
- f()//3
- f()//4
- f=null//闭包死亡(包含闭包的函数对象成为垃圾对象)
闭包的应用——自定义JS模块
- 具有特定功能的js文件
- 将所有的数据和功能都封装在一个函数内部(私有的)
- 只向外暴露一个包含n个对象或函数
- 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <script src="92.myModule.js">script>
- <script>
- var module=myModule();
- myModule.doSomething();
- script>
- head>
- <body>
-
- body>
- html>
- function myModule(){
- // 私有数据
- var msg='My atguigu'
- // 操作数据的函数
- function doSomething(){
- console.log('doSomething()'+msg.toUpperCase());
- }
- function doOtherthing(){
- console.log('doOtherthing()'+msg.toLowerCase());
- }
- // 向外暴露对象(给外部使用的方法)
- return {
- doSomething:doSomething,
- doOtherthing:doOtherthing
- }
- }
- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <script src="92.myModule2.js">script>
- <script>
- var myModule2=myModule2()
- myModule2.doSomething();
- script>
- head>
- <body>
-
- body>
- html>
- (
- function (){
- // 私有数据
- var msg='My atguigu'
- // 操作数据的函数
- function doSomething(){
- console.log('doSomething()'+msg.toUpperCase());
- }
- function doOtherthing(){
- console.log('doOtherthing()'+msg.toLowerCase());
- }
-
- window.myModule2={
- doSomething:doSomething,
- doOtherthing:doOtherthing
- }
- })()
闭包的缺点及解决
缺点
- 函数执行后,函数内的局部变量没有释放,占用内存事件会变长
- 容易造成内存泄漏
解决
- 能不用闭包就不用
- 及时释放
内存溢出
- 一种程序运行出现的错误
- 当程序运行需要的内存超过了剩余的内存时,就抛出内存溢出的错误
内存泄漏
- 占用的内存没有被及时释放
- 内存泄漏积累多了就会内存溢出
常见的内存泄漏
- 意外的全局变量
- 没有及时清理的计时器或回调函数
- 闭包
- // 内存泄漏
- function fn(){
- a=new Array(100000)
- console.log(a);
- }
- fn()
-
- var intervalId=setInterval(function(){
- console.log('--------');
- },1000)
- // clearInterval(intervalId)
-
- function fn1(){
- var a=4
- function fn2(){
- console.log(++a);
- }
- return fn2;
- }
- var fn=fn1()
- f()
- // f=null
注意:this的指向,当以方法调用时this指向方法所在的对象,当以函数调用时,this就是全局