JS 闭包

1 为什么需要闭包

1 如果想对一个变量实现累加,可以定义为全局变量
  var num=0
  function add(){ num++ }
  add()//1
  add()//2

  如果这样做
  function add(){ var num=0  return num++ }
  add()//1
  add()//1
  因为函数多次调用时,代码重复申请空间,互相独立

2 定义为全局变量的做法可以实现累加,但是在所有js里都可以访问修改num,
  那么能不能将num变为局部变量,又能实现功能呢?

  答案是可以的,这就是闭包.实现思路与 1 中一样:
  //这是伪代码,说明实现思路
  function fn(){
    var num=0
	function add(){ return num++ }
  }
  add()//1
  add()//2
  将num放在fn里,是局部变量,若是fn()一次,用于生成num=0,之后每次都调用add()实现累加

  具体实现:
  var add = (function(){
              var num=0
              return function(){ return num++ }
            })()
  add()//1
  add()//2

2 闭包的实现原理

1 作用域链(内层数据可以上边的外层数据),使得add()可以访问fn()里的num
2 当函数在被使用时就不删除(回收),内层的数据被使用,外层的数据也不删,使得立即执行函数(function)()中的num得以保存

function fn(){
 	let n = 1
 	return function(){
 		let m = 1
 		return function () {
 			console.log('n:' + ++n)
 			console.log('m:' + ++m)
 		}
 	}
}
let a = fn()();
a() //n:2 m:2
a() //n:3 m:3

3 构造函数也是用了闭包

function FN(){
	let n = 1
	this.sum (){
		console.log('n:' + ++n)
	}
}
相当于:
function FN(){
	let n = 1
	function sum (){
		console.log('n:' + ++n)
	}
	return { 
		sum: sum
	}
}

let a = new FN()   //sum被使用,不删
a.sum()  //n:2
a.sum()  //n:3
a.sum()  //n:4
let b = new FN()   //新内存空间
b.sum()  //n:2
b.sum()  //n:3

4 内存泄漏

内存泄漏指函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。

function  showId() {
    var el = document.getElementById("app")
    el.onclick = function(){
      console.log(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
}

// 改成下面
function  showId() {
    var el = document.getElementById("app")
    var id  = el.id
    el.onclick = function(){
      console.log(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
    el = null    // 主动释放el,但注意id没有释放,若id=null onclick就没参数了
}

5 补充

for--异步与作用域

// 输出十个 10
for (var i = 0; i < 10; i++) {
  setTimeout(function(){
    console.log(i);
  })
}

// 输出 0123456789
for (let j = 0; j < 10; j++) {
  setTimeout(function(){
    console.log(j);
  })
}
  1. 异步

1 JS是单线程环境.
2 对于同步和异步, 先执行同步代码, 碰到异步的代码会将其插入到任务队列当中等待, 直到同步代码执行完毕,才执行异步代码
3 setTimeout是延时,比如上面这段代码中,第二个参数延时时间是0,也就是说执行到它的时候会在0ms之后将它插入到任务队列当中。(此时setTimeout里的回调没有执行)
4 同步代码都执行完成之后,那么JS引擎就空闲了,这个时候就轮到任务队列中的异步代码依次加载了。

  1. 作用域

立即执行函数?其实是函数独立作用域, 函数赋值与调用

for(var i=1;i<10;i++){
	(function(a){
		setTimeout(function(){console.log(a);},10000);
	})(i)
}
//十秒之后,立刻输出1 2 3 4 5 6 7 8 9(中间无间隔)
//与立即执行函数无关,其实是将i的值保存到函数内的局部变量里了
for(var i=1;i<10;i++){
	function fn(a){
		setTimeout(function(){console.log(a);},10000);
	}
	fn(i)
}
posted @ 2022-02-23 22:48  波吉国王  阅读(31)  评论(0编辑  收藏  举报