[JavaScript高级]闭包

(一)闭包

1.闭包是什么

(1)闭包包含自由(未绑定到特定对象)变量;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。

(2)“闭包” 一词来源于以下两者的结合:

①要执行的【代码块】(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)
②为自由变量提供绑定的计算环境【(作用域)】。

(3)在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等语言中都能找到对闭包不同程度的支持。

2.JavaScript中闭包

在JavaScript中闭包就是【函数】

闭包就是一个封闭包裹的范围.。
函数可以限定变量的作用域。
一个变量在函数内部声明,那么在函数外部是无法访问的.。
那么这个就是一个【封闭】的范围. 广义上说就是一个闭包了。

应用场景:用闭包追加的时候,使用限制长度的队列

3.实例

①实例分析:

如果函数中又定义了函数,并将这个函数以返回值的形式返回,那么,在JavaScript中"子域访问父域"的规则就
会打破了. 因为这个时候,在函数外就可以访问函数内的变量. 看下面代码:

var func = function() {
	var num = 10;
	return function() {
		alert(num);
	};
};
var foo = func();
foo();

②实例解析:

这段代码中,函数foo是0级链,而变量num是在1级链中,这个时候,0级链的函数就访问了1级
链中的变量num,这段代码运行结果是打印出10. 这样就实现了JavaScript中的闭包.

③小结

JavaScript中的闭包就是在函数中定义变量,然后通过【返回值】,将可以访问这个变量的函数返回,这样在函数外就可以访问函数内的变量了. 这样就形成了闭包.

(二)闭包的使用案例及其说明

在JavaScript中,使用闭包就像C语言中使用指针一样. 其基本语法简单,但是使用灵活多变,使用其灵活的语法与特征就能实现许多非常强大的功能. 在此不能阐述闭包的所有用法。

1 模拟私有成员

这个案例是JavaScript实现面向对象的基础.

 var Person = function (name, age, sex) {//闭包,外部不可以修改
            return {
                get_Name: function () {
                    return name;
                },
                set_Name: function (value) {//非闭包,外部可以修改
                    name = value;
                },
                get_Age: function () {
                    return age;
                },
                get_Sex: function () {
                    return sex;
                }
            }
        };
        var p = Person("小宅", 19, "女");
        alert(p.get_Name());//闭包


        p.set_Name("xiaozhai");
        alert(p.get_Name());//非闭包

这段代码就是一个函数,函数带有三个参数,也就是说在函数内部有三个局部变量,分别表示姓
名(name)、年龄(age)和性别(gender). 然后在返回值中,返回一个对象,该对象提供四个方法.
分别给年龄提供读写方法,给性别与年龄提供读取的方法. 这四个函数都是这个函数的子域. 因
此返回的这个对象就可以直接访问这三个变量. 但是有了读写的访问权限的限制.

2 Fibonacci(斐波那契)数列

Fibonacci(斐波那契)数列就是:1, 1, 2, 3, 5, 8, 13, …
(从第三个数开始,每一个数字等于前面两个数字相加之和)

(1)未用闭包

	// 1	1	2	3	
	// 第n项是第n-1和n-2的和,开始的两项为1
			
   var count1 = 0;
         var fib1 = function (n) {
          count1++;
          if (n == 0 || n == 1) {
              return 1;
          }
         return fib1(n - 1) + fib2(n - 2);
         };	
         	
	       // fib1(11); // 287
	      // fib1(12); // 465
		  //fib1(13); // 753
		 //alert(count1);
		 	

通过输出结果我们可以看出,函数才经过过几次变化,数值就会非常大,影响性能,因此下面我们使用闭包来进行优化。

(2)闭包和未使用闭包对比

			var foo1 = function() {	};//未使用闭包	
			var foo2 = (function() {//使用闭包
				// 闭包的位置
				var num = "幽灵";
				return function() {
				
				};
			})();

(3)使用闭包

	
        var count2 = 0;
        var fib2 = (function () {
            var arr = [1, 1];//斐波那契数列
            return function (n) {
                count2++;
                var res = arr[n];
                if (res) {
                    return res;
                } else {
                    arr[n] = fib2(n - 1) + fib2(n - 2);
                    return arr[n];
                }
                };
          
            })();

            /*
            for (var i = 0; i < 10; i++) {
                alert(fib1(i) + "," + fib2(i));
            }
            */

使用闭包和没有使用闭包的次数对比:
	
			// fib1(11); // 287次
			fib2(11); // 21次
			// fib1(12); // 465次
			fib2(12); // 24	次					
			//alert(count1);
			alert(count2);

这个案例一般传统的做法就是使用递归,在JavaScript中,递归使用arguments.callee()表示当前调用函数(即递归函数). 那么这么做最直接的结果是,存在一个缓存,将计算得到的结果保存在缓存中,并且实现所有的计算只计算一次,那么可以大大的提高性能。

下面的图示就是(斐波那契数列)中递归的步骤:

在这里插入图片描述

3 html字符串案例

在很多js库中需要使用正则表达式处理一些数据,而如果每次执行都在方法中保存需要处理匹配的字符串,那么会大量的消耗内存,影响性能. 因此可以将重复使用的表达式都保存在闭包中,每次使用都是访问的这个字符串. 例如:

String.prototype.deentityify = function() {
	var entity = {
		lt		:	'<',
		gt		:	'>'
	};
	return function() {
		return this.replace(/&([^;]+);/g, function(a,b) {
			var r = entity[b]; 
			return typeof r === 'string' ? r : a; 
		});
	};
}();

这段代码会将任何一个字符串中的 < 和 > 都替换成尖括号<和>,对于页面html代码的复制非常好用.

4 事件处理方法的追加与移除

在JavaScript中并不支持事件处理函数的追加.那么怎么来实现追加呢?
请看下面的实例:


var loadEvent = function( fn ) {
	var oldFn = window.onload;
	if( typeof oldFn === "function" ) {
		window.onload = function() {
			oldFn();
			fn();
		};
	} else {
		window.onload = fn;
	}
};

不过这段代码没有办法移除已经追加的方法,那么使用闭包的缓存功能就可以轻易的实现.

var jkLoad = (function() {
	var events = {};
	var func = function() {
		window.onload = function() {
			for(var i in events) {
				events[i]();
			}
		};
	};
	return {
		add		:	function(name, fn) {
						events[name] = fn;
						func();
					},
		remove 	:	function(name) {
						delete events[name];
						func();
					}
	};
})();

这段代码就是得到用来追加和移出load事件的对象. 如果要追加事件,可以使用

	jkLoad.add("f1", function() {
		// 执行代码1
	});

如果要移除事件处理函数,就是用代码

	jkLoad.remove("f1");

(三)闭包的优缺点

1.优点:

使得函数有【记忆】功能,提高方法的执行效率。
模拟对象 实现事件的追加和移除。
设计私有的方法和变量,防止外部修改这些属性,保护函数内部变量的安全性,增加了封装性。

2.缺点:

闭包不仅常驻内存,而且还会在使用不当的的情况下产生无效内存,造成内存的浪费

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值