闭包

(一)什么是闭包?

闭包就是有权访问另一个函数作用域中变量的函数.
下面列举一些闭包的例子:

function fn(){
    var num=100;
    return function(){
        num++;
        return num;
    }
}
var b=fn();//获得函数(闭包)
/**因为执行fn()()后,fn函数已经执行完毕了,没有其他资源引用fn,它会被立即释放,
也就是说,fn()()执行完后,立即就释放了。**/
console.log(fn()());//101 本身的函数调用
console.log(fn()());//101 num每次都会被初始化
//b拥有fn的内部函数的引用,而内部函数又有num的引用,故垃圾回收机制并不会在b()执行后将fn的内存释放,
//所以局部变量num并没有在内存当中消失
console.log(b());//101
console.log(b());//102
//简单闭包
function foo() {
	var a = 2;
	function bar() {
		console.log( a );
	}
	return bar;
}
var baz = foo();
baz(); // 2 闭包
/**
**功能实现逐秒输出1,2,3,4,5
**/
//失败,在外部只有一个作用域只存在一个i,故每次setTimeout都是拿到循环结束的 i=6
for (var i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log( i );//6 6 6 6 6
	}, i*1000 );
}
//失败,虽然有了自己的作用域,但是并未将i转换内存中的i依然是循环结束的6
for (var i=1; i<=5; i++) {
	(function() {
		setTimeout( function timer() {
			console.log( i );//6 6 6 6 6
		}, i*1000 );
	})();
}
//成功,在IIFE(立即执行函数表达式)中,成功将i存储进自身的函数作用域j中,实现功能
for (var i=1; i<=5; i++) {
	(function(j) {
		setTimeout( function timer() {
		console.log( j );//1 2 3 4 5
		}, j*1000 );
	})( i );
}
//成功,let会每一次都创建块级作用域,故有5块i的块级作用域,i能正常传递
for (let i=1; i<=5; i++) {
	setTimeout( function timer() {
	console.log( i );//1 2 3 4 5
	}, i*1000 );
}

(二)闭包的特点,优点,缺点?

闭包的优点:1.能够读取函数内部的变量 2.让这些变量存在于内存中,不会在调用结束后,被垃圾回收机制回收

闭包的缺点:正所谓物极必反,由于闭包会使函数中的变量保存在内存中,内存消耗很大,所以不能滥用闭包,解决办法是,退出函数之前,将不使用的局部变量删除。

(三)闭包何时从内存中销除?

  • 当对内部变量的引用消失时,闭包就会被垃圾回收机制回收。
//运用之前的例子
function fn(){
    var num=100;
    return function(){
        num++;
        return num;
    }
}
var b=fn();//获得函数(闭包)
/**因为执行fn()()后,fn函数已经执行完毕了,没有其他资源引用fn,它会被立即释放,
也就是说,fn()()执行完后,立即就释放了。**/
console.log(fn()());//101 本身的函数调用
console.log(fn()());//101 num每次都会被初始化

//b拥有fn的内部函数的引用,而内部函数又有num的引用,故垃圾回收机制并不会在b()执行后将fn的内存释放,
//所以局部变量num并没有在内存当中消失
console.log(b());//101
console.log(b());//102

/**添加以下代码即可**/
// 手动释放fn的引用
b= null;
// fn的引用b被释放了,现在fn的作用域也被释放了。
  • 如果是点击事件调用闭包函数,会在何时删除引用?
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<html>
	<body>
		<button id="test" >测试点击事件绑定闭包</button>
		
	</body>
</html>
<script type="text/javascript">
function fn(){
    var num=100;
    return function(){
        num++;
        return num;
    }
}
var a = fn();
document.getElementById("test").onclick=function(){
	var b = fn();
	console.log(b());//101 每次进入都是
	console.log(b());//102 每次进入都是
	
	if(typeof a === "function"){
		var aNum = a();
		if(aNum > 108){//超过108,则释放闭包占用内存
			a = null;
		}else{
			console.log(aNum);//按次数累加 101 102 103 ... 108
		}
	}
};
</script>

由此可见,点击事件会一直持有闭包的引用,除非将引用改变,才能释放闭包的内存。

(四)实际开发中闭包的使用?

  • 封装

var person = function(){    
    //变量作用域为函数内部,外部无法访问    
    var name = "default";       
 
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();    
 
print(person.name);//直接访问,结果为undefined    
print(person.getName());    
person.setName("abruzzi");    
print(person.getName());    
 
得到结果如下:  
 
undefined  
default  
abruzzi
  • 结果缓存
    我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,

    那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

var CachedSearchBox = (function(){    
    var cache = {},    
       count = [];    
    return {    
       attachSearchBox : function(dsid){    
           if(dsid in cache){//如果结果在缓存中    
              return cache[dsid];//直接返回缓存中的对象    
           }    
           var fsb = new uikit.webctrl.SearchBox(dsid);//新建    
           cache[dsid] = fsb;//更新缓存    
           if(count.length > 100){//设定缓存的大小<=100    
              delete cache[count.shift()];    
           }    
           return fsb;          
       },    
 
       clearSearchBox : function(dsid){    
           if(dsid in cache){    
              cache[dsid].clearSelection();      
           }    
       }    
    };    
})();    
 
CachedSearchBox.attachSearchBox("input");
//模拟容器
function isFirstLoad() {
    var _list = []

    return function (id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id)
            return true
        }
    }
}

// 使用
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
  • 类和继承
function Person(){    
    var name = "default";       
 
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
    };   
 
    var p = new Person();
    p.setName("Tom");
    alert(p.getName());
    var Jack = function(){};
    //继承自Person
    Jack.prototype = new Person();
    //添加私有方法
    Jack.prototype.Say = function(){
        alert("Hello,my name is Jack");
    };
    var j = new Jack();
    j.setName("Jack");
    j.Say();
    alert(j.getName());
  • 模块模式

模拟模块机制:

//自定义模块
var MyModules = (function Manager() {
	var modules = {};
	function define(name, deps, impl) {
		for (var i=0; i<deps.length; i++) {
			deps[i] = modules[deps[i]];
		}
		modules[name] = impl.apply( impl, deps );
	}
	function get(name) {
		return modules[name];
	}
	return {
		define: define,
		get: get
	};
})();
//使用模块
MyModules.define( "bar", [], function() {
	function hello(who) {
		return "Let me introduce: " + who;
	}
	return {
		hello: hello
	};
} );
MyModules.define( "foo", ["bar"], function(bar) {
	var hungry = "hippo";
	function awesome() {
		console.log( bar.hello( hungry ).toUpperCase() );
	}
	return {
		awesome: awesome
	};
} );
var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );
console.log(
	bar.hello( "hippo" )
); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO

ES6中的模块机制:

//bar.js文件
function hello(who) {
	return "Let me introduce: " + who;
}
export hello;

//foo.js文件
// 仅从 "bar" 模块导入 hello()
import hello from "bar";
var hungry = "hippo";
function awesome() {
	console.log(
		hello( hungry ).toUpperCase()
	);
}
export awesome;

//baz.js文件
// 导入完整的 "foo" 和 "bar" 模块
module foo from "foo";
module bar from "bar";
console.log(
	bar.hello( "rhino" )
); // Let me introduce: rhino
foo.awesome(); // LET ME INTRODUCE: HIPPO
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Funnee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值