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>
文中不当之处,欢迎指正。
热门工具 换一换