闭包函数
闭包函数有3个特点:
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
优点:可以避免全局变量的污染。
缺点:闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
定义:当一个函数A的返回值是另一个函数B,而返回的那个函数B如果调用了父函数A的内部变量,如果返回的这个函数B再外部被执行,就产生了闭包。(有权访问 另一个 函数 作用域中变量的函数。)
表现形式:使函数外部能够调用函数内部的定义的变量。(涉及到了作用域)
例1:根据作用域链的规则来看,底层作用域没有声明的变量,会向上一级查找,如果找到就返回,如果没有的话就会一直找到window的变量,没有的话就会返回undefined。所以例子1中B函数中没有声明变量name,很明显返回的是函数A中的name变量。
// 例1
function A() {
let name = 'hello'
function B() {
console.log(name)
}
B()
}
A() // hello
在看例2前先看MDN中的代码:
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
MDN中解释说:
原因在于,JavaScript中的函数会形成了闭包。 闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。在本例子中,
myFunc
是执行makeFunc
时创建的displayName
函数实例的引用。displayName
的实例维持了一个对它的词法环境(变量name
存在于其中)的引用。因此,当myFunc
被调用时,变量name
仍然可用,其值Mozilla
就被传递到alert
中。
例2:与例1不同的是,此时调用的函数B只是内部函数的方法, 所以我们定义一个变量name调用闭包函数对象调用后,将B函数赋值给C变量;然后使用C()方式进行调用。
个人理解:通俗易懂的讲就是函数B只是一个方法,执行到var myFunc = makeFunc();
这一段代码发生的是将函数A执行后赋值给C变量,函数A正常执行到return
之前,B函数中就已经查找并保存了name
这个变量,函数A执行到return
后便得到了函数B方法,但是还没有调用,所以赋值给变量C,C就引用了这个方法(上文下划线中提到函数实例的引用)但是需要C()
调用这个方法。(!!!若有不妥的地方还望请教技术大佬留言纠正!!!)
// 例2
function A() {
let name = 'hello'
function B() {
console.log(name)
}
return B
}
const C = A()
C() // hello
// 可以执行一下下面这段代码
function makeFunc() {
var name = "Mozilla";
console.log(name, '1');
function displayName() {
console.log(name,'2');
}
console.log(name,'3');
console.log(displayName,'4')
return displayName;
}
var myFunc = makeFunc();
console.log(myFunc)
myFunc();
// Mozilla 1
// Mozilla 3
// ƒ displayName() {
// console.log(name,'2');
// } '4'
// ƒ displayName() {
// console.log(name,'2');
// }
// Mozilla 2
例3:当我们调用闭包hello对象时,一个传入1 一个传入2 将返回的函数对象分别给变量a b,也就是说 a b 都是闭包 他们共享相同的函数定义,但是保存在了不同的环境中。
function hello(x) {
return function fun(y) {
console.log(x + y)
}
}
var a = hello(1) // 返回函数对象
var b = hello(2) // 返回函数对象
console.log(a(2)) // 3
console.log(b(3)) // 5