闭包

94 阅读4分钟

闭包


闭包:函数中又定义了一个新的函数,外部函数将内部函数返回 。

在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

  • 全局变量:
    1. 位置:一对script标签内,函数外。
    2. 作用范围:全局,整个页面。
    3. 生命周期:随着页面的关闭而销毁。
  • 局部变量:
    1. 位置:一对函数花括号内,函数内。
    2. 作用范围:只能在函数内使用。
    3. 生命周期:从函数开始执行到函数执行完毕而销毁。

闭包的用途:

闭包的用途有两个:

  • 让函数外部能够得到内部的变量。
// 让函数外部能够得到函数内部的变量	
	function outside(){
        // 局部变量num1
        var num1 = 10
        // inside就是outside的闭包函数
        // 连接了函数内外
        function inside(){
            return num1
        }
        return inside
    }
	// 相当于:var fn = function inside(){ ... }
    var fn = outside()
    console.log(fn); //inside函数:function inside(){ return num1 }
    var fn1 = fn()
    console.log(fn1); //10
  • 让函数内部的变量始终保存在内存中。
// 让函数内部的变量始终保存在内存中
	function fn1(){
        var num1 = 10
        add = function(){
            num1++
            console.log(num1);
            console.log("add");
        }
        function fn2(){
            console.log(num1);
        }
        return fn2
    }
    var a = fn1()
    console.log(a); //fn2函数
    console.log(add()); //11 add undefind:(add())
    add() //12 add
    add() //13 add
    a() //13

闭包的缺点

闭包可以让函数外部拿到函数内部的变量,也能将函数内部的变量始终保存在内存中。

这是闭包的优点,也是它的缺点。

因为一旦滥用闭包就会让内存消耗变大,情况严重时可能会造成内存泄露。

解决办法

使用面向对象的思想进行代码的编写,将需要使用的函数和变量尽量封装到一个对象中去。

// 面试题1:	
	var name = "window"
        var obj = {
            name:"obj",
            // getName:function(){}为obj对象中的属性
            getName:function(){
                return function(){
                    return this.name
                }
            }
        }
      // obj.getName()执行结果是返回的getName函数:function(){return this.name}。
      // obj.getName()()执行时,function(){return this.name}变成普通函数,this指向window。
	 console.log( obj.getName()()) // "window"        
// 面试题2:    
    var name = 'window';
    var obj = {
        name:'obj',
        getName:function(){  // 若此处变为箭头函数()=>{},this就指向了window,结果就会变成'window'
            // this指向对象本身obj,锁定this指向
            var _this = this
            return function(){ // 若此处变为箭头函数()=>{},且不锁定this,this就指向了obj,结果就会变成'obj'
                // this指向已经被锁定为obj
                return _this.name;
            }
        }
    };
    alert(obj.getName()()); // 'obj'
// 面试题3:
	function getSum(num1){ // num1 = 5
        return function(num2){ // num2 = 10
            return num1+num2;
        }
    }
    let num = getSum(5)(10);
	console.log(num) // 15
// 面试题4:
	function fn(n,o){ //fn1
        console.log(n,o);
        // 返回一个对象,对象的花括号没有作用域
        return {
            fn:function(m){ //fn2
                return fn(m,n); //fn3
            }
        }
    }
    var a = fn(0); a.fn(1); a.fn(2); a.fn(3);
    var b = fn(0).fn(1).fn(2).fn(3);
    var c = fn(0).fn(1); c.fn(2); c.fn(3);


	// 首先将a,b,c分解

	var a = fn(0); a.fn(1); a.fn(2); a.fn(3);  可以分解为:
    //0赋值给 fn(n,o); n=0,o=undefined
    var a = fn(0); //0 undefined
    // a调用对象fn2, 给m传值1,返回fn3,fn3(m,n)=>(1,0) 此处fn3变成全局变量,fn3中的n与fn1中的n相等
    a.fn(1) // 1 0
    // 同上,给m传值2,fn3中m为2,n为fn1中的0
    a.fn(2) // 2 0
    // 给m传值3,fn3中m为3,n为fn1中的0
    a.fn(3) // 3 0
    

	var b = fn(0).fn(1).fn(2).fn(3);   可以分解为:
    var a = fn(0) // 0  undefined
    // a调用对象fn2,m传值1,n往上找找到fn1中的n等于0,n的值变成0
    var b = a.fn(1) // 1 0
    // b调用对象fn2,m传值2,此时因为是在变量a的基础上调用,fn1的n现在为1
    var c = b.fn(2) // 2 1
    // c调用对象fn2,m传值3,此时因为是在变量b的基础上调用,fn1的n现在为2
    c.fn(3) // 3 2


	var c = fn(0).fn(1); c.fn(2); c.fn(3);   可以分解为:
    var a = fn(0) //0 undefined
    // a调用对象fn2,m传值1,n往上找找到fn1中的n等于0,n的值变成0
    var b = a.fn(1); //1 0
    // b调用对象fn2,m传值2,此时因为是在变量a的基础上调用,fn1的n现在为1
    b.fn(2); //2 1
    // c调用对象fn2,m传值3,此时还是在变量a的基础上调用,fn1的n现在还是1
    b.fn(3); //3 1