JavaScript闭包详解

闭包是什么?

A说:闭包就是函数里面嵌套函数,然后return函数
B说: 可以访问其他函数作用域内变量的函数叫做闭包
C说:函数和函数内部能访问到的变量的总和就叫闭包

查一查JavaScript高级红宝书咋说的吧:闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的, 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

疑问
为啥要嵌套函数

只是为了设计出局部变量和方法,也可以说是私有变量和方法,避免污染全局变量

为啥要return函数

如果不return,外部就无法使用这个闭包,把return fn改成window.fn = fn也是一样的,所以闭包和return无关

闭包有什么用?

简单来说,让你间接访问一个变量,或者说是隐藏一个变量,再者就是让这个变量保存在内存中

为什么呢?解答来源参考链接1的故事:


假设我们在做一个游戏,在写其中关于「还剩几条命」的代码。如果不用闭包,你可以直接用一个全局变量:

window.lives = 30 // 还有三十条命

这样看起来很不妥。万一不小心把这个值改成 -1 了怎么办。所以我们不能让别人「直接访问」这个变量。怎么办呢? ----- 用局部变量

但是用局部变量别人又访问不到,怎么办呢? ----- 暴露一个访问器(函数),让别人可以「间接访问」

代码如下:

!function(){
  var lives = 50
  window.奖励一条命 = function(){
    lives += 1
  }
  window.死一条命 = function(){
    lives -= 1
  }
}()


故事讲完了,回到第一句话,作用就是让你间接访问一个变量,或者说是隐藏一个变量

闭包的应用场景
  • 闭包允许将函数和其所操作的某些数据关联起来

    function makeSizer(size){
        return function(){
            docment.body.style.fontSize = size + 'px'
        }
    }
    var size12 = makeSizer(12)
    size12()
    
  • 模拟私有方法

    var Counter = (function(){
    	var privateCounter = 0
        function changeBy(val){
            privateCounter += val
        }
        return {
            increment:function(){
                changeBy(1)
            },
            decrement:function(){
                changeBy(-1)
            },
            value:function(){
                return privateCounter
            }
        }
    })()
    
  • 解决循环中闭包的创建方法

    比如DOM事件回调、定时器回调等

    //定时器方法一
    for(var i = 0; i <= 5; i++){
        (function(i){
            setTimeout(function(){
                console.log(i)
            },1000)
        })(i)
    }
    //定时器方法二
    for(var i = 0; i <= 5; i++){
        setTimeout(function(i){
           return function(){
              console.log(i)
           }
        }(i),1000) 
    }
    
使用闭包的注意点

1、使用闭包存在内存泄漏,是因为IE,IE在在我们使用完闭包之后,仍然回收不了闭包里面引用的变量,解决办法就是使用完闭包之后手动将变量删除。

2、 因为闭包会保留它们包含函数的作用域,所以比其他函数更占用内存。过度使用闭 包可能导致内存过度占用,因此建议仅在十分必要时使用。V8等优化的 JavaScript引擎会 努力回收被闭包困住的内存,不过我们还是建议在使用闭包时要谨慎。

闭包中的this

在闭包中使用this会让代码变复杂,如果内部函数没有使用箭头函数定义,则this对象会在运行时绑定到执行函数的上下文。如果在全局函数中调用,在非严格模式下this等于window,严格模式下this等于undefined。如果作为某个对象的方法调用,则this指向这个对象。匿名函数在这种情况下不会绑定到某个对象,这意味着this指向window。看下面的例子:

window.identity = 'The Window'  
let object = {   
	identity: 'My Object',  
	getIdentityFunc() {     
		return function() {       
			return this.identity   
		} 
	}
} 
console.log(object.getIdentityFunc()()); // 'The Window' 

解析:

将最后一步拆分成两步走:

var first = object.getIdentityFunc() //返回一个匿名对象
var second = first() //函数调用

第一步中:getIdentityFunc()是作为object对象的方法调用的,如果在getIdentityFunc()中使用this,那么this指向的是object对象,这毫无疑问。

第二步:调用first函数,可知并没有在object对象中调用,因此是作为函数在全局作用域下调用的,first函数中this指向window,这也是可以理解的。

为什么匿名函数没有使用其包含作用域(getIdentityFunc())的this对象呢?

每个函数在被调用时都会自动创建两个特殊变量:thisarguments。内部函数永远不可能直接访问外部函数的这两个变量,但是,将this保存到闭包可以访问的一个变量中,则是行得通的。比如:

window.identity = 'The Window'  
let object = {   
	identity: 'My Object',  
	getIdentityFunc() {  
        let that = this
		return function() {       
			return that.identity   
		}
	}
} 
console.log(object.getIdentityFunc()()); // 'My Object' 

getIdentityFunc()中的that指向object,所以调用object.getIdentityFunc()()会返回“My Object”

参考链接

https://zhuanlan.zhihu.com/p/22486908

https://github.com/GZ0759/StudyNotes/blob/master/INTERVIEW/JavaScript%E5%AE%9E%E8%B7%B5%E7%AF%87.md

https://www.cnblogs.com/nuanriqingfeng/p/5789003.html

JavaScript高级程序设计(第4版)

你可能感兴趣的:(javascript,函数闭包)