2021-06-01

闭包

1. 概念

声明一个函数中的函数

闭包:内部函数总是可以访问到外部函数中生命的参数和变量,即使在其外部函数被返回(寿命终结)

外部函数a和内部函数b,

        function a() {
            var val_a = "我是a函数里的变量";

            function b() {
                console.log(val_a);
            }
            //a函数将b返回
            return b;
        }
        //全局变量fn,a执行返回了b给fn
        var fn = a();
        //调用fn,能够在全局作用域访问a函数里的变量
        fn();

这里fn能够访问到a的变量,因为b中引用着a的活动对象,所以即使a函数执行完了,a的活动对象还是不会被销毁。这就说明过度使用闭包会导致内存泄漏

2. 特点

(1) 让访问函数部变量;

(2) 局部变量常驻在内存中;

(3)可以避免使用全局变量,避免全局污染

(4)会造成内存泄漏。(有一块内存空间被长期占用,而不释放)

每次外部函数执行的时候,外部函数的引用地址不同,都会创建一个新地址,但是当前活动对象中又被内部自己引用的数据,那么这个时候,这个数据不删除,保留一根指针给内存活动对象。大概意思就是当外部函数运行结束甚至销毁时,局部变量key=value,尽管key被垃圾回收机制给回收了,但是value仍不会被回收,会变成一个自由变量下引用的指针

内存溢出(out of memory)内存泄漏(memory leak)
要求分配的内存超出了系统给你的,系统不能满足需求向系统申请分配内存进行使用(new),可是使用完之后却不归还,结果你申请到的那块地址自己也不不能访问(/地址弄丢了),系统不能再次将它分配给需要的程序。

结论:闭包找到的是同一地址中父级函数中对应变量最终的值

function addCount(){
	var count = 0;
	return function(){
		count += 1;
		console.log(count);
	}
}
var fun1 = addCount();
var fun2 = addCount();
fun1();//1
fun1();//2
fun1();//3
fun2();//1
fun2();//2
function fn(){
	var a = 3;
	return function(){
		return ++a;
	}
}
console.log(fn()());//4
console.log(fn()());//4
console.log(fn()());//4
var newFn = fn();
console.log(newFn());//4
console.log(newFn());//5
console.log(newFn());//6
//闭包找到的是同一地址中父级函数中对应变量最终的值。
(function() { 
  	var m = 0; 
  	function getM(){ 
  		return m; 
  	}
  	function seta(val){
  		m = val;
  	}
  	window.g = getM;
  	window.f = seta;
})();
//(function(){ /* code */ })()是立即执行函数,
#立即执行做了两件事:
//1、m的初始值为0,
//2、全局变量g和f分别指向两个函数
f(100);
//因为已经调用过全局变量f,所以,m的值已经成为了100,存储在内存空间中
//内存 存储m=100;但是此时的变量变成了一个 自由变量下引用的 指针
console.log(g());//100
var lis = document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
  //立即执行
  	(function(i){
      	 lis[i].onclick = function(){
           	  console.log(i);
      	 };
  	})(i);
}

标题隐式声明的全局变量

// 
var lis = document.getElementsByTagName("li");
        for (var i = 0; i < lis.length; i++) {
            lis[i].onclick = function() {
                console.log(i);
            };
        }
//
        var a = 0;

        function fn() {
            var a = 1;
        }
        fn();
        console.log(a); //0 
//
        var a = 0;

        function fn() {
            var a = b = 1;
        }
        fn();
        console.log(a, b); //0 1
        // a 是全局变量。
        // b 是隐式声明的全局变量。
        var a = 0;

        function fn() {
            var b = 1;

        }
        fn();
        console.log(a, b); //0 b is not defined
 function fnnn() {
            var arr = [];
            for (var i = 0; i < 5; i++) {
                arr[i] = function() {
                    return i;
                }
            }
            console.log(i); //5
            return arr;
        }
        var list = fnnn();
       // console.log(list); //[ƒ, ƒ, ƒ, ƒ, ƒ]
//函数的返回值  :外部变量list可以访问内部变量,arr中的每一项都指向了函数,第一次调用之后,局部变                      量i已经加到了5
        for (var i = 0, len = list.length; i < len; i++) {
            console.log(list[i]()); //5 5 5 5 5
        }
//再继续循环调用list的每一项函数,得到的i的值都是5

//闭包知识点:即使外部函数返回,但是内部函数的i=5保存在了内存中没有销毁(自由指针)
//没有销毁的原因是 内部函数还在使用外部函数的活动对象

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory!

内存溢出的原因以及解决方法

1.引起内存溢出的原因有很多种

1.1 内存加载数据量庞大,如一次从数据库取出过多数据;

1.2 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;(Java Virtual Machine)JAVA虚拟机

1.3代码中存在死循环或循环产生过多重复的对象实体

1.4使用的第三方软件中的BUG;

1.5启动参数内存值设定的过

2.内存溢出的解决方案:

第一步:修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

第二步:检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

第三步:对代码进行走查和分析,找出可能发生内存溢出的位置。

3.重点排查以下几点:
  1. 检查代码中是否有死循环递归调用
  2. 检查是否有大循环重复产生新对象实体
  3. 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
  4. 检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

使用内存查看工具动态查看内存使用情况

回调函数

//定义主函数,回调函数作为参数
function A(callback) {
    callback();  
    console.log('我是主函数');      
}

//定义回调函数
function B(){
    setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作  
}

//调用主函数,将函数B传进去
A(B);

//输出结果
我是主函数
我是回调函数
// 上面的代码中,我们先定义了主函数和回调函数,然后再去调用主函数,将回调函数传进去。

//定义主函数的时候,我们让代码先去执行callback()回调函数,但输出结果却是后输出回调函数的内容。
//这就说明了主函数不用等待回调函数执行完,可以接着执行自己的代码。所以一般回调函数都用在耗时操作上面。
//比如ajax请求、处理文件等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值