当前位置: 首页 > news >正文

闭包问题

前言:基础知识点

  1.    js的作用域:在js中 函数内部可以读取函数外部变量,相反不可以。
  2.    作用域销毁:只是切断函数与AO(执行期上下文)的链接,并不是真实销毁AO的存在

总结:

  1. 闭包是什么:内部函数被保存到外部时,生成闭包
  2. 闭包缺点:闭包会导致原有作用域链不释放,造成内存泄漏
  3. 闭包用处:

实现公有变量 ;

实现缓存;

实现封装,属性私有化;

模块化开发,防止污染全局变量

闭包举栗子,如下

function fn(){
	var num = 100;
	function a(){
		num++;
		console.log(num);
	}
	return a
}
var myArr = fn();
myArr();

//打印结果为 101

上面栗子中,作用域以及执行期上下文时如何演变的?,以至于函数外部调用内部的值num呢?看下面

1.初始:
fn() defined  [[scope]] ——>0: GO {fn:function(){...},myArr:undefined}


2.var myArr = fn()执行时:
fn() doing    [[scope]] ——>0: fnAO  其中fnAO {num:100, a:function(){...} }
                           1: GO 略

a()  defined  [[scope]] ——>0: fnAO 略
                           1: GO 略
return a将 function a(){...}返回赋予全局变量myArr,结果 myArr=function a(){...}


3.var myArr = fn()执行完毕:
fn() 切断链接fnAO  [[scope]] ——>0: GO  
     但fnAO依旧存在,函数a仍有链接指向fnAO

4.myArr()执行时:  
a()  doing  [[scope]] ——>  0:aAO {}
                           1: fnAO  num=101
                           2: GO
 执行num++ ——> 作用域依次向下寻找num ——> 找到最近的fnAO中的num值 ,num++

上面过程解释了闭包是什么以及如果产生的,下面来看看再实际使用过程中会因为闭包造成的问题举栗:

function demo(){
	var arr = [];
	for (var i = 0; i < 10; i++){
		arr[i] = function () {
			console.log(i);
		}
	}
	return arr;
}
var m_demo = demo();

for (var j = 0; j < 10; j++){
	m_demo[j]();
}

///打印结果为10个10,而非想象的0到9
1.初始:
demo() defined  [[scope]] ——>0: GO


2.var m_demo = demo()执行时:
demo() doing  [[scope]] ——>0: demoAO {arr:[function(){...},function(){...}...] ,i=10 }
                           1: GO

10个function()  defined  [[scope]] ——>0: demoAO 
                                      1: GO
return arr将 arr[...]返回赋予全局变量m_demo,结果 m_demo=arr[...]


3.var m_demo = demo()执行完毕:
demo() 切断链接demoAO  [[scope]] ——>0: GO  
     但demoAO依旧存在, m_demo[j]()有链接指向demoAO

m_demo[j]()  defined  [[scope]] ——>0: demoAO  其中num=100
                                   1: GO

4.m_demo[j]()执行,生成自己的AO{}——> 执行打印i ——> 顶层依次向下寻找 ——> demoAO中的i ——> 打印值
m_demo[j]()  doing  [[scope]] ——>  0:m_demoAO
                                   1: demoAO  i=10
                                   2: GO

如果想打印出从0 到9 ,就用到立即执行函数了,解决方法

function demo(){
	var arr = [];
	for (var i = 0; i < 10; i++){
		(function(j){
			arr[j]=function(){
				console.log(j)
			}
		}(i))
	}
	return arr;
}
var m_demo = demo();

for (var j = 0; j < 10; j++){
	m_demo[j]();
}

/*

1.初始:
demo() defined  [[scope]] ——>0: GO { m_demo:undefined,demo:function(){...} }

2.var m_demo = demo()执行时:
demo() doing  [[scope]] ——>0: demoAO {arr:[function(){...},function(){...}...] ,i=10 }
                           1: GO
立即执行函数 doing 立即Ao { j:0} {j:1}....
10个function()  defined  [[scope]] ——>0: 立即Ao 
                                      1: demoAO 
                                      2: GO
return arr将 arr[...]返回赋予全局变量m_demo,结果 m_demo=arr[...]

3.var m_demo = demo()执行完毕:
demo() 切断链接demoAO  [[scope]] ——>0: GO  
     但demoAO依旧存在, m_demo[j].[[scope]][1]指向demoAO

m_demo[j]()  defined  [[scope]] ——>0: 立即Ao 
                                   1: demoAO 
                                   2: GO

4.m_demo[j]()执行,生成自己的m_demoAO{}——> 执行打印j ——> 顶层依次向下寻找 ——> demoAO中的i ——> 打印值
m_demo[j]()  doing  [[scope]] ——>  0:m_demoAO {} 
                                   1: 立即Ao  {j=1} {j=1}
                                   2: demoAO 
                                   3: GO
*/

知道闭包如何解决,现在看一下闭包的优势应用吧

1、使用闭包进行模块化开发,防止污染全局变量

var name = 'hh';
var initLi = (function(){
    var name = 'Li';
    function sayName () {
       console.log(name);
    }
	return function () {
		sayName();
	}
}())
initLi();

//结果为Li

var initZhao = (function(){
    var name = 'zhao';
    function sayName () {
       console.log(name);
    }
	return function () {
		sayName();
	}
}())
initZhao();

//结果为zhao

如有错误,欢迎指正。

相关文章:

  • js --- 面向对象之原型与原型链
  • 对象枚举以及判断数组与对象的3种方法
  • git的小白入门
  • windows如何配置mysql的环境变量
  • js的变量类型判断和类型的转换
  • vscode 常用设置和插件
  • 可迭代对象和类数组
  • js数组遍历方法总结与对比
  • PAT乙级 我要通过(1003)的详细解答过程
  • PAT乙级 成绩排名(1004) c++题解
  • PAT乙级 继续(3n+1)猜想(1005) c++题解(打表越界的段错误)
  • PAT乙级 素数对猜想(1007)c++实现
  • PAT乙级 说反话(1009)c++新手易懂版
  • 图的深度遍历(邻接表)SCAU c++
  • 图的广度遍历(邻接表)SCAU c++
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Codepen 每日精选(2018-3-25)
  • extjs4学习之配置
  • PAT A1050
  • Protobuf3语言指南
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 山寨一个 Promise
  • 听说你叫Java(二)–Servlet请求
  • 微信小程序设置上一页数据
  • ionic入门之数据绑定显示-1
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​MySQL主从复制一致性检测
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • # 数据结构
  • #162 (Div. 2)
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (实战篇)如何缓存数据
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)平衡树
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .bashrc在哪里,alias妙用
  • .net framework profiles /.net framework 配置
  • .net FrameWork简介,数组,枚举
  • .NET HttpWebRequest、WebClient、HttpClient
  • .NET MVC第三章、三种传值方式
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .NET 中 GetProcess 相关方法的性能
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .net网站发布-允许更新此预编译站点
  • .NET业务框架的构建
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • /usr/bin/env: node: No such file or directory
  • @angular/cli项目构建--http(2)
  • @JoinTable会自动删除关联表的数据
  • @Transactional类内部访问失效原因详解