JavsScript 闭包 (Closure)

欢迎各位大佬指导!!!

前言:

​ 要理解闭包首先需要了解javascript的变量作用域,JavaScript的变量作用域分为两种:

  1. 全局作用域
  2. 局部作用域

全局变量的作用域是全局性的,在整个javaScript程序中都在,而在函数内声明的变量作用域,只在函数内起作用。

eg: 在JavaScript中函数内部可以直接访问全局作用域的变量

var a = 123; // 定义全局变量 a
function fn(){
	alert(a); // 在函数内部访问全局变量a
}
fn(); // 调用函数 fn() 弹框结果 123

但是函数内的局部变量,在外面不能访问到。

function fn(){
	var a = 123; // 定义局部变量
}
alert(a); // a 未定义

注意:在函数内部定义局部变量时,如果没有用 var 声明,那这个变量就是全局变量。即外部就可以访问到这个变量。

如果想要获取函数内定义的局部变量,那么用闭包就可以获取到函数内部的局部变量。

闭包的概念

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,且外部(父)函数被调用,就产生了闭包,简单的说,JavaScript 允许使用内部函数,即函数的定义和函数的表达式位于另一个函数体内,并且这些函数引用所在外部函数内中声明的局部参数、变量、和声明的其它内部函数,在最外层window作用域被调用时,就形成了闭包。

闭包的作用:

  • 使函数的内部变量(函数)在函数执行完后,仍然存活在内存中,延长了局部变量的生命周期,变量始终保存在内存中,不会随着函数的结束而自动销毁。
  • 将变量(函数)定义在闭包中使用,避免污染全局变量。
  • 定义JS模块 模块中一般包含大量的变量和函数,如果不使用闭包,将会使这些变量(函数)暴露在全局作用域中,这就很有可能会与用户自己的代码产生冲突

闭包的隐患

  • 函数执行完成后,函数内部的变量没有被释放,占用内存时间会变长容易造成内存泄露 ,应及时释放,将存在闭包的函数置为null。

是否使用闭包要考虑这两点:

隔离和数据保存。如果需要隔离数据并形成命名空间,可以使用匿名自执行函数 ;如果还需要在隔离状态下保存数据,保存之后后面还可以继续使用,那就可以使用闭包,如果及不需要隔离和数据保存,那就使用普通函数,在函数调用完后,数据就释放了。

来看下面的两端代码

代码一:

var name ="The Window";
var object = {
	name : "My Object",
	getNameFunc:function(){
		return funcion(){
			return this.name;
		}	
	}
};

alert(object.getNameFunc() ());

运行结果: The Window

代码二:

var name = "The Window";
var object = {
	name: "My Object",
	getNameFunc: function(){
		var that = this;
		return function(){
			return that.name;
		}
	}
};

alert(object.getNameFunc() ());

运行结果: My Object

思考一下,这是为何??

我们来分析一下,首先来看代码一:

// 首先在Javascript中 定义全局变量、全局对象、全局函数 都自动属于window对象的成员。也就是说它们都是window对象的属性,可以用window.出来。
var name = "The Window"; // 这是定义了一个name 的全局变量
var object = {   // 这是定义了一个object 的全局对象
	name: "My Obejct", // 局部变量 name
	getNameFunc: function(){ // 局部函数 getNameFunc 返回的是一个匿名函数
		return function(){
            // 此处的this 指的是window对象,为什么这样说呢,是因为哪个对象调用this所在的函数,this指的就是哪个对象。
             // alert(object.getNameFunc() ()) 首先调用了 object.getNameFunc的函数返回了一个匿名函数function(){return this.name} 此时这个匿名函数是全局函属于window对象,所以在调用匿名函数的时候,return this.name 的结果是 The Window
			return this.name; 									 
		}
	}
};

alert(object.getNameFunc() ());

我们再来分析一下 代码二:

var name = "The Window"; // 同样定义全局的name变量
var object = {  // 定义全局的object全局对象
    name: "My Object",  // 定义局部变量
    getNameFunc: function(){  // 定义局部函数 getNameFunc()
        var that = this; // 此时当alert(object.getNameFunc()()) 调用时object调用getNameFunc函数,哪个对象调用this,this指的就是哪个对象,显然this指向的是object对象。然后 var that = this 将 object对象赋值给 that 然后返回匿名函数此时匿名函数中引用了that变量,所有当再次的调用匿名函数是return that.name 就等同于 return object.name 即输入结果为 My Object
        return function(){
            return that.name;
        }
    }
    
};

alert(object.getNameFunc() ());

OK,我们来看一道经典题

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); //输入结果: undefined 0 0 0
var b = fun(0).fun(1).fun(2).fun(3); //输入结果: undefined 0 1 2

思考一下,OK ,我们来分析一下产生结果的原因。

var a = fun(0);a.fun(1);a.fun(2);a.fun(3); // 我们来拆分一下这段代码

var a= fun(0); 
a.fun(1);
a.fun(2);
a.fun(3);
// 当执行第一行代码时,调用最外面的fun(n,o)函数,传入0,所以console.log(o)的结果为undefined,因为只传了一个参数。然后返回一个对象
// {fun:function(m){return fun(m,n)}}
// 将这个对象赋值给了变量a 即 a = {fun:function(m){return fun(m,n)}}

// 执行第二行代码时,调用fun:function(m)函数,此时m为1;因为a是第一次的对象传入了0。 所以此时的 n 为0;即 return fun(1,0) 再次调用最外面的fun(n,o)函数,打印o为0;

// 当执行第三行代码时,还是调用fun:function(m)函数,此时m为2;因为a是第一次的对象传入了0。 所以此时的 n 为0;即 return fun(2,0) 再次调用最外面的fun(n,o)函数,打印o为0;

// 当执行第四代码时,同上;打印o为0;

Ok ,第一个分析完毕,我们再来分析一下第二个输入出结果。

var b = fun(0).fun(1).fun(2).fun(3); // 同样我们把这行代码拆分一下

var b = fun(0)
    .fun(1)
    .fun(2)
    .fun(3);

// 当执行第一行代码时,依然是调用最外面的fun(n,o)函数,并且传入n=0; 即第一次打印的o为undefined,然后返回一个对象
// {fun:function(m){return fun(m,n)}}

// 当执行第二行代码时,调用fun:function(m)函数,因 .fun(1) 此时m为1;此时 n 为上一次的值 0 ,即 return fun(1,0),再次调用最外面的函数 fun(n,o)
// 此时n 为 1; 打印 o 为 0 ,然后 return 返回。

// 当执行第三行代码时,调用fun:function(m)函数,因 .fun(2) 此时m为2;此时 n 为第二行执行后的值, n=1; 即 return fun(2,1) ,再次调用最外面的函数 fun(n,o) 此时 n 为 2 , o 为 1 ,即 打印 1,然后返回。

// 当执行第四行代码时,调用fun:function(m)函数,因 .fun(3) 此时m为3;此时 n 为第三行执行后的值, n=2; 即 return fun(3,2) ,再次调用最外面的函数fun(n,o) 此时 n 为 3 , o 为 2,即 打印 2, 然后返回。
原文地址:https://www.cnblogs.com/1204it-ly/p/13903189.html