看这段代码:
function foo() { var i = 1 function show() { i += 1 console.log(i) } return show } var a = foo() a() 正常情况,函数定义时,开辟一个内存空间,使用结束后,该变量取消指向内存地址,内存地址准备被销毁 如果内部函数引用了外部函数的值,那么就会形成闭包闭包是一个存在内部函数 的引用关系
该引用指向的是 外部函数 的局部变量对象
为什么内部函数一定要被使用?
因为函数变量提升的时候,如果内部函数没用被使用,在预解析的过程中,不会被定义
优点:
延长外部函数变量对象的生命周期间接使得函数外部可以操作(读写)到 函数内部的数据(变量/函数)浏览器为了性能,后期将外部函数中,不被内部函数使用的变量清除了
缺点:
延长外部函数变量对象的生命周期**(占内存,如果不及时清除,容易造成内存溢出、泄露)**将函数作为另一个函数的返回值
function fn1() { var a = 2 function fn2() { a++ console.log(a) } return fn2 } var f = fn1() f() // 3 f() // 4将函数作为实参传递给另一个函数调用
function showMsgDelay(msg, time) { setTimeout(() => { console.log(msg) }, time); } showMsgDelay('hello', 1000)这种使用方式,就跟module的使用方式很相似
作用:定义JS模块
具有特定功能的js文件将所有的数据和功能都封装在一个函数内部(私有的)只想外暴露一个包含 n 个方法的对象或函数模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能 // js模块 function mathModule() { var result = 0 function add(x) { return result += x } function subtract(x) { return result -= x } return { add, subtract } }使用:
<script type="text/javascript" src="mathModule.js"></script> <script> var mathObj = mathModule() console.log(mathObj.add(1)) // 1 console.log(mathObj.subtract(2)) // -1 </script>答案: The Window
解析:
/* obj.getName()() 可以分解为以下代码 */ var result = obj.getName() // 1. 返回为: function(){ return this.name } result() // 2. 就是执行 function(){ return this.name } // 3. result() == window.result() // 4. 所以此时,this为 window // this上面有一个变量,name = The Window,在全局作用域就有了 // 所以输出 The Window答案:My Object
解析:
/* obj.getName()() 可以分解为以下代码 */ var result = obj.getName() // 1. 返回为: function(){ return that.name } result() // 2. 就是执行 function(){ return that.name } // 3. 由于此时的that,闭包对象指定了that = this,而在getName的this为obj // 4. 所以此时,that为 obj // obj上面有一个变量,name = My Object,在局部作用域就有了 // 所以输出 My Object明白这道题,闭包就可以出师了
function fun(n, o) { console.log(o) return { fun: function(m) { return fun(m, n) } } } var a = fun(0) a.fun(1) a.fun(2) a.fun(3) var b = fun(0).fun(1).fun(2).fun(3) var c = fun(0).fun(1) c.fun(2) c.fun(3)答案:
undefined 0 0 0 undefined 0 1 2 undefined 0 1 1解析:
解析之前,首先要明确两个语法:
var a = fun(0)、var b = fun(0)
这两个并没有共享同一个 闭包对象,而是分别创建两个不同的闭包对象,也就是对于a、b来说,最开始的console.log(o),都是undefined
a.fun(1)、a.fun(2) 和 fun(1).fun(2)
这两个并不是等同的,第一个a.fun(1)、a.fun(2)都只是使用同一个函数;第二个fun(1).fun(2)等同于fun(2)在使用fun(1)基础上的函数
/* 如果fun有一个变量是 1, 调用一次就会 +1 */ a,fun(1) //那么这两个都是1 a.fun(2) fun(1).fun(2) // fun(1) 的变量是 1, fun(2) 的变量 就会改变,为2那么,明确之后,现在正式开始解析
function fun(n, o) { console.log(o) return {// 满足产生闭包的条件,产生了一个闭包引用->外部函数fun的变量对象{n:0,o: undefined} fun: function(m) { return fun(m, n) } } } // 意思是:fun调用后,返回一个对象,对象内有一个函数,函数内返回的是这个外部函数fun // 即 第一个是 window.fun(n, o),第二个是 对象.fun(m),这个返回 window.fun(n, o)a的调用方式:
/* 开拓一个变量对象,此时: window.fun的 n = 0, o = undefined a.fun 的 o = 0 */ var a = fun(0) // 输出 undefined /* 以下都是调用同一个 a.fun,上面可得知,a.fun 的 o = 0 */ a.fun(1) // 输出 0 a.fun(2) // 输出 0 a.fun(3) // 输出 0b的调用方式:
var b = fun(0).fun(1).fun(2).fun(3) /* 以上可以分解为: var b0 = fun(0) var b1 = b0.fun(1) var b2 = b1.fun(2) var b3 = b2.fun(3) */ /* 开拓一个变量对象,此时: window.fun的 n = 0, o = undefined b0.fun 的 o = 0 */ var b0 = fun(0) // 输出 undefined /* 开拓一个变量对象,此时: b0.fun 的 o = 0 b1.fun 的 o = 1 */ var b1 = b0.fun(1) // 输出 0 /* 开拓一个变量对象,此时: b0.fun 的 o = 0 b1.fun 的 o = 1 */ var b2 = b1.fun(2) // 输出 1 /* 开拓一个变量对象,此时: b0.fun 的 o = 0 b1.fun 的 o = 1 */ var b3 = b2.fun(3) // 输出 2c的调用方式
var c = fun(0).fun(1) c.fun(2) c.fun(3) /* 以上可以分解为: var c0 = fun(0) var c1 = c0.fun(1) c1.fun(2) c1.fun(3) */ /* 开拓一个变量对象,此时: window.fun的 n = 0, o = undefined c0.fun 的 o = 0 */ var c0 = fun(0) // 输出 undefined /* 开拓一个变量对象,此时: c0.fun 的 o = 0 c1.fun 的 o = 1 */ var c1 = c0.fun(1) // 输出 0 /* 以下都是调用同一个 c1.fun,上面可得知,c1.fun 的 o = 1 */ c1.fun(2) // 输出 1 c1.fun(3) // 输出 1