对JS闭包的理解及常见应用场景

请您移步我的新个人博客: https://moweiwei.com,谢谢!

1、变量作用域

变量作用域两种:全局变量、局部变量。js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。

2、如何从外部读取函数内部的变量?

function f1(){
        var n = 123;
        function f2(){    //f2是一个闭包
            alert(n)
        }    
        return f2;
    }
js链式作用域:子对象会一级一级向上寻找所有父对象的变量,反之不行。
f2可以读取f1中的变量,只要把f2作为返回值,就可以在f1外读取f1内部变量

3、闭包概念

能够读取其他函数内部变量的函数。
或简单理解为定义在一个函数内部的函数,内部函数持有外部函数内变量的引用。

4、闭包用途

1、读取函数内部的变量
2、让这些变量的值始终保持在内存中。不会再f1调用后被自动清除。
3、方便调用上下文的局部变量。利于代码封装。
原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。

5、闭包理解

/**
 * [init description]
 * @return {[type]} [description]
 */
function init() {
    var name = "Chrome";	//创建局部变量name和局部函数alertName

    function alertName() { //alertName()是函数内部方法,是一个闭包
        alert(name); //使用了外部函数声明的变量,内部函数可以访问外部函数的变量
    }
    alertName();
}
init();
//一个变量在源码中声明的位置作为它的作用域,同时嵌套的函数可以访问到其外层作用域中声明的变量

/**
 * [outFun description]
 * @return {[type]} [description]
 */
function outFun(){
	var name = "Chrome";
	function alertName(){
		alert(name);
	}
	return alertName;	//alertName被外部函数作为返回值返回了,返回的是一个闭包
}

var myFun = outFun();
myFun();
/*
闭包有函数+它的词法环境;词法环境指函数创建时可访问的所有变量。
myFun引用了一个闭包,闭包由alertName()和闭包创建时存在的“Chrome”字符串组成。
alertName()持有了name的引用,
myFunc持有了alertName()的的访问,
因此myFunc调用时,name还是处于可以访问的状态。
 */


/**
 * [add description]
 * @param {[type]} x [description]
 */
function add(x){
	return function(y){
		return x + y;
	};
}

var addFun1 = add(4);
var addFun2 = add(9);

console.log(addFun1(2)); //6
console.log(addFun2(2));  //11
//add接受一个参数x,返回一个函数,它的参数是y,返回x+y
//add是一个函数工厂,传入一个参数,就可以创建一个参数和其他参数求值的函数。
//addFun1和addFun2都是闭包。他们使用相同的函数定义,但词法环境不同,addFun1中x是4,后者是5

6、闭包应用场景之setTimeout


	//原生的setTimeout传递的第一个函数不能带参数
	setTimeout(function(param){
		alert(param)
	},1000)


	//通过闭包可以实现传参效果
	function func(param){
		return function(){
			alert(param)
		}
	}
	var f1 = func(1);
	setTimeout(f1,1000);

7、闭包应用场景之回调

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title></title>
	<link rel="stylesheet" href="">
</head>
<style>
	body{
		font-size: 12px;
	}
	h1{
		font-size: 1.5rem;
	}
	h2{
		font-size: 1.2rem;
	}
</style>
<body>

	<p>哈哈哈哈哈哈</p>
	<h1>hhhhhhhhh</h1>
	<h2>qqqqqqqqq</h2>

	<a href="#" id="size-12">12</a>
	<a href="#" id="size-14">14</a>
	<a href="#" id="size-16">16</a>

<script>
	function changeSize(size){
		return function(){
			document.body.style.fontSize = size + 'px';
		};
	}

	var size12 = changeSize(12);
	var size14 = changeSize(14);
	var size16 = changeSize(16);

	document.getElementById('size-12').onclick = size12;
	document.getElementById('size-14').onclick = size14;
	document.getElementById('size-16').onclick = size16;
	//我们定义行为,然后把它关联到某个用户事件上(点击或者按键)。我们的代码通常会作为一个回调(事件触发时调用的函数)绑定到事件上
</script>
</body>
</html>

8、闭包应用场景之封装变量

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>闭包模拟私有方法</title>
	<link rel="stylesheet" href="">
</head>
<body>
<script>
	//用闭包定义能访问私有函数和私有变量的公有函数。
	var counter = (function(){
		var privateCounter = 0;	//私有变量
		function change(val){
			privateCounter += val;
		}
		return {
			increment:function(){	//三个闭包共享一个词法环境
				change(1);
			},
			decrement:function(){
				change(-1);
			},
			value:function(){
				return privateCounter;
			}
		};
	})();

	console.log(counter.value());//0
	counter.increment();
	counter.increment();//2
	//共享的环境创建在一个匿名函数体内,立即执行。
	//环境中有一个局部变量一个局部函数,通过匿名函数返回的对象的三个公共函数访问。

</script>
</body>
</html>

9、闭包应用场景之为节点循环绑定click事件

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title></title>
	<link rel="stylesheet" href="">
</head>
<body>
	
	<p id="info">123</p>
	<p>E-mail: <input type="text" id="email" name="email"></p>
	<p>Name: <input type="text" id="name" name="name"></p>
	<p>Age: <input type="text" id="age" name="age"></p>

<script>
	function showContent(content){
		document.getElementById('info').innerHTML = content;
	};

	function setContent(){
		var infoArr = [
			{'id':'email','content':'your email address'},
			{'id':'name','content':'your name'},
			{'id':'age','content':'your age'}
		];
		for (var i = 0; i < infoArr.length; i++) {
			var item = infoArr[i];
			document.getElementById(item.id).onfocus = function(){
				showContent(item.content)
			}
		}
	}
	setContent()
	//循环中创建了三个闭包,他们使用了相同的词法环境item,item.content是变化的变量
	//当onfocus执行时,item.content才确定,此时循环已经结束,三个闭包共享的item已经指向数组最后一项。
	

	
	/**
	 * 解决方法1     通过函数工厂,则函数为每一个回调都创建一个新的词法环境
	 */
	function showContent(content){
		document.getElementById('info').innerHTML = content;
	};

	function callBack(content){
		return function(){
			showContent(content)
		}
	};

	function setContent(){
		var infoArr = [
			{'id':'email','content':'your email address'},
			{'id':'name','content':'your name'},
			{'id':'age','content':'your age'}
		];
		for (var i = 0; i < infoArr.length; i++) {
			var item = infoArr[i];
			document.getElementById(item.id).onfocus = callBack(item.content)
		}
	}
	setContent()

	/**
	 * 解决方法2 		绑定事件放在立即执行函数中
	 */
	function showContent(content){
		document.getElementById('info').innerHTML = content;
	};

	function setContent(){
		var infoArr = [
			{'id':'email','content':'your email address'},
			{'id':'name','content':'your name'},
			{'id':'age','content':'your age'}
		];
		for (var i = 0; i < infoArr.length; i++) {
			(function(){
				var item = infoArr[i];
				document.getElementById(item.id).onfocus = function(){
					showContent(item.content)
				}
			})()//放立即执行函数,立即绑定,用每次的值绑定到事件上,而不是循环结束的值
		}
	}
	setContent()

	/**
	 * 解决方案3		用ES6声明,避免声明提前,作用域只在当前块内
	 */
	function showContent(content){
		document.getElementById('info').innerHTML = content;
	};

	function setContent(){
		var infoArr = [
			{'id':'email','content':'your email address'},
			{'id':'name','content':'your name'},
			{'id':'age','content':'your age'}
		];
		for (var i = 0; i < infoArr.length; i++) {
			let item = infoArr[i]; 		//限制作用域只在当前块内
			document.getElementById(item.id).onfocus = function(){
				showContent(item.content)
			}
		}
	}
	setContent()
</script>
</body>
</html>

文中不当之处,欢迎指正。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页