08_javascript闭包

引子实例

// 需求: 点击某个按钮, 提示"点击的是第n个按钮"

// html:
  <button>测试1</button>
   <button>测试2</button>
   <button>测试3</button>
    
// js: (错误实现)
var btns = document.getElementsByTagName('button')
//遍历加监听 
for (var i = 0,length=btns.length; i < length; i++) {
	var btn = btns[i]
	btn.onclick = function () {
		alert('第'+(i+1)+'个')
	}
} // 无论点击哪个按钮都会显示第4个


// 改进方法:
for (var i = 0,length=btns.length; i < length; i++) {
	var btn = btns[i]
	//将btn所对应的下标保存在btn上
	btn.index = i
	btn.onclick = function () {
		alert('第'+(this.index+1)+'个')
	}
}

//利用闭包
for (var i = 0,length=btns.length; i < length; i++) {
	(function (j) {
		var btn = btns[j]
		btn.onclick = function () {
			alert('第'+(j+1)+'个')
		}
	})(i)
}

理解闭包

如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
闭包到底是什么?
使用chrome调试查看
  * 理解一: 闭包是嵌套的内部函数
  * 理解二: 包含被引用变量(函数)的对象
  * 注意: 闭包存在于嵌套的内部函数中
产生闭包的条件?
  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)
function fn1 () {
	var a = 2
	var b = 'abc'
	function fn2 () { //执行函数定义就会产生闭包(不用调用内部函数)
		console.log(a)
	}
	// fn2()
}
fn1()

function fun1() {
	var a = 3
	var fun2 = function () {
		console.log(a)
	}
}
fun1()

常见的闭包

将函数作为另一个函数的返回值
function fn1() {
	var a = 2
	function fn2() {
		a++
		console.log(a)
	}
	return fn2
}
var f = fn1()
f() // 3
f() // 4
将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
	setTimeout(function () {
		alert(msg)
	}, time)
}
showDelay('hello', 2000)

闭包的作用

1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

  1. 函数执行完后, 函数内部声明的局部变量是否还存在?

一般是不存在, 存在于闭包中的变量才可能存在

  1. 在函数外部能直接访问函数内部的局部变量吗?

不能, 但我们可以通过闭包让外部操作它

function fn1() {
	var a = 2
	function fn2() {
		a++
		console.log(a)
		// return a
	}
	function fn3() {
		a--
		console.log(a)
	}
	return fn3
}
var f = fn1()
f() // 1
f() // 0

闭包的生命周期

  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时
function fn1() {
	//问题2: 此时闭包产生了吗? 
	//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
	var a = 2
	function fn2 () {
		a++
		console.log(a)
	}
	return fn2
}
//问题1: 此时闭包产生了吗?
var f = fn1()
//问题3: 此时闭包释放了吗?  
f() // 3
f() // 4
//问题4: 此时闭包释放回收了吗?   
//问题5: 如何让闭包释放回收呢?
f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)

闭包的应用: 自定义JS模块

闭包的应用 : 定义JS模块
	*具有特定功能的js文件
	*将所有的数据和功能都封装在一个函数内部(私有的)
	*只向外暴露一个包含n个方法的对象或函数
	*模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
// 例如:自定义js模块,命名为:myModule.js
function myModule() {
	//私有数据
	var msg = 'Hello World!'
	//操作数据的函数
	function doSomething() {
		console.log('doSomething() '+msg.toUpperCase())
	}
	function doOtherthing () {
		console.log('doOtherthing() '+msg.toLowerCase())
	}
	//向外暴露对象(给外部使用的方法)
	return {
		doSomething: doSomething,
		doOtherthing: doOtherthing
	}
}


// 外部使用时:
// 引入js模块 
<script type="text/javascript" src="myModule.js"></script>

// 调用模块中方法
var module = myModule()
module.doSomething()
module.doOtherthing()

闭包缺点

缺点
  • 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
  • 容易造成内存泄露
解决
  • 能不用闭包就不用
  • 及时释放
function fn1() {
	var arr = new Array[100000]
	function fn2() {
		console.log(arr.length)
	}
	return fn2
}
var f = fn1()
f()
// 让内部函数成为垃圾对象-->回收闭包
f = null 

面试题

面试题1
//代码片段一
var name = "The Window";
var object = {
	name : "My Object",
	getNameFunc : function(){
		//console.log(this)//object
		return function(){
			//console.log(this) //window
			return this.name;
		};
	}
};
//console.log(object.getNameFunc())

/*
ƒ (){
	//console.log(this) //window
	return this.name;
}
* */
* 
//console.log(object.getNameFunc)

/*
ƒ (){
	console.log(this)//object
	return function(){
		//console.log(this) //window
		return this.name;
	};
}
* */
console.log(object.getNameFunc()()); //The Window
1.以函数的形式调用时,this永远都是window
2.以方法的形式调用时,this就是调用方法的那个对象
3.当以构造函数的形式调用时,this就是新创建的那个对象
4.使用call和apply调用时,this是指定的那个对象
//代码片段二
var name2 = "The Window";
var object2 = {
	name2 : "My Object",
	getNameFunc : function(){
		//console.log(this) //Object2
		var that = this;
		return function(){
			//console.log(this)//window
			return that.name2;
		};
	}
};
console.log(object2.getNameFunc()())//My Object 
面试题2
function fun(n,o) {
	console.log(o)
	return {
		fun:function(m){
			//console.log('m =' + m,'n =' + n)
			return fun(m,n);
		}     
	};
}
//var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);//undefined,0,0,0
/*
var a = fun(0)  //undefined
a.fun(1)//0 (m=1,n=0)  执行完后m,n被GC回收
a.fun(2)//0 (m=2,n=0)
a.fun(3)//0 (m=3,n=0)
* */
//var b = fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined,0,1,1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值