闭包

1、什么是闭包

闭包函数:声明在一个函数中的函数,叫做闭包函数。

闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。

严格来说,闭包需要满足三个条件:【1】访问所在作用域;【2】函数嵌套;【3】在所在作用域外被调用

【1】访问所在作用域

  闭包是指可以访问其所在作用域的函数

  那这样说来,需要通过作用域链查找变量的函数就是闭包

//按照定义一的说法,需要通过作用域链在全局环境中查找变量n的函数foo()就是闭包
var n = 0;
function foo() {
    console.log(n)//0
}
foo();

【2】函数嵌套

  闭包是指有权访问另一个函数作用域中的变量的函数

  那这样说来,访问上层函数的作用域的内层函数就是闭包

//按照定义二的说法,嵌套在foo函数里的bar函数就是闭包
function foo(){
    var a = 2;
    function bar(){
        console.log(a); // 2
    }
    bar();
}
foo();

【3】在所在作用域外被调用

  闭包是指在函数声明时的作用域以外的地方被调用的函数

  在函数声明时的作用域以外的地方调用函数,需要通过将该函数作为返回值或者作为参数被传递

【1】返回值

//按照定义三的说法,在foo()函数的作用域中声明,在全局环境的作用域中被调用的bar()函数是闭包
function foo(){
    var a = 2;
    function bar(){
        console.log(a); //2
    }
    return bar;
}
foo()();

  可以简写为如下表示:

function foo(){
    var a = 2;
    return function(){
        console.log(a);//2
    }
}
foo()();

【2】参数

//按照定义三的说法,在foo()函数的作用域中声明,在bar()函数的作用域中被调用的baz()函数是闭包
function foo(){
    var a = 2;
    function baz(){
        console.log(a); //2
    }
    bar(baz);
}
function bar(fn){
    fn();
}

  因此,无论通过何种手段,只要将内部函数传递到所在的词法作用域以外,它都会持有对原始作用域的引用,无论在何处执行这个函数都会使用闭包

2、特点

  让外部访问函数内部变量成为可能;

  局部变量会常驻在内存中;

  可以避免使用全局变量,防止全局变量污染;

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

3、闭包的应用场景

/* 例子1 */

function funA(){
  var a = 10;  // funA的活动对象之中;
  return function(){   //匿名函数的活动对象;
        alert(a);
  }
}
var b = funA();
b();  //10


/* 例子2 */

function outerFn(){
  var i = 0; 
  function innerFn(){
      i++;
      console.log(i);
  }
  return innerFn;
}
var inner = outerFn();  //每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
inner();
inner();
inner();
var inner2 = outerFn();
inner2();
inner2();
inner2();   //1 2 3 1 2 3


/* 例子3 */

var i = 0;
function outerFn(){
  function innnerFn(){
       i++;
       console.log(i);
  }
  return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();     //1 2 3 4

/* 例子4 */

function fn(){
	var a = 3;
	return function(){
		return  ++a;                                     
	}
}
alert(fn()());  //4
alert(fn()());  //4    

/* 例子5 */

function outerFn(){
var i = 0;
  function innnerFn(){
      i++;
      console.log(i);
  }
  return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1();
inner2();
inner1();
inner2();    //1 1 2 2


/* 例子6 */

(function() { 
  var m = 0; 
  function getM() { return m; } 
  function seta(val) { m = val; } 
  window.g = getM; 
  window.f = seta; 
})(); 
f(100);
console.info(g());   //100  闭包找到的是同一地址中父级函数中对应变量最终的值


/* 例子7 */

function a() { 
  var i = 0; 
  function b() { alert(++i); } 
  return b; 
} 
var c = a(); 
c();      //1 
c();      //2 


/* 例子8 */

function f() { 
  var count = 0; 
  return  function() { 
      count++; 
      console.info(count); 
  } 
} 
var t1 = f();
t1();     //1 
t1();     //2 
t1();     //3 


/* 例子9 */

var add = function(x) { 
  var sum = 1; 
  var tmp = function(x) { 
      sum = sum + x; 
      return tmp;    
  } 
  tmp.toString = function() { 
      return sum; 
  }
  return tmp; 
} 
alert(add(1)(2)(3));     //6


/* 例子10 */

var lis = document.getElementsByTagName("li");
for(var i=0;i<lis.length;i++){
  (function(i){
      lis[i].onclick = function(){
           console.log(i);
      };
  })(i);       //事件处理函数中闭包的写法
}  


/* 例子11 */

function m1(){
     var x = 1;
     return function(){
          console.log(++x);
     }
}
 
m1()();   //2
m1()();   //2
m1()();   //2
 
var m2 = m1();
m2();   //2
m2();   //3
m2();   //4


/* 例子12 */

var  fn=(function(){
   var  i=10;
   function  fn(){
      console.log(++i);
   }
   return   fn;
})() 
fn();   //11
fn();   //12


/* 例子13 */

function love1(){
     var num = 223;
     var me1 = function() {
           console.log(num);
     }
     num++;
     return me1;
}
var loveme1 = love1();
loveme1();   //输出224


/* 例子14 */

function fun(n,o) {
    console.log(o);
    return {
         fun:function(m) {
               return fun(m,n);
         }
    };
}
var a = fun(0);  //undefined  return的是(m)=>f(m,0)
a.fun(1);  //0  
a.fun(2);  //0  
a.fun(3);  //0  
var b = fun(0).fun(1).fun(2).fun(3);   //undefined  0  1  2
var c = fun(0).fun(1);  
c.fun(2);  
c.fun(3);  //undefined  0  1  1


/* 例子15 */

function fn(){
   var arr = [];
   for(var i = 0;i < 5;i ++){
     arr[i] = function(){
         return i;
     }
   }
   return arr;
}
var list = fn();
for(var i = 0,len = list.length;i < len ; i ++){
   console.log(list[i]());
}  //5 5 5 5 5


/* 例子16 */

function fn(){
  var arr = [];
  for(var i = 0;i < 5;i ++){
    arr[i] = (function(i){
        return function (){
            return i;
        };
    })(i);
  }
  return arr;
}
var list = fn();
for(var i = 0,len = list.length;i < len ; i ++){
  console.log(list[i]());
}  //0 1 2 3 4

4、闭包与this

我们知道this对象是基于函数的执行环境绑定的,在全局的时候,this等于window,而当函数作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此this常常指向window。

var name = 'The Window';
var obj = {
    name: 'My obj',
    getName: function() {
        return function() {
            return this.name;
        };
    }
};

obj.getName()(); // 'The Window'

前面说过,函数在被调用时会自动取得两个特殊变量: this和arguments,内部函数在搜索这两个变量时,只会搜索到其活动对象,所以永远不会访问到外部函数的这两个变量。如果我们想满足需求,可以固定this对象并更名即可。

var name = 'The Window';
var obj = {
    name: 'My obj',
    getName: function() {
        // 固定this对象,形成闭包,防止跟特殊的this重名
        var that = this;
        return function() {
            return that.name;
        };
    }
};

obj.getName()(); // 'My obj'

箭头函数绑定,因为箭头函数没有自己的this,箭头函数的this指向它的调用者,如果没有调用者则默认指向window.

function foo() {
    setTimeout(() => {
        // 这里的`this`是词法上从`foo()`采用
        console.log( this.a );
    },100);
}
var a=3;
var obj = {
    a: 2
};

foo.call( obj ); // 2

ES6还有另外四种绑定形式参考之前的文章:JavaScript的绑定与this指向

本文参考链接

https://www.cnblogs.com/xiaohuochai/p/5728577.html

https://blog.csdn.net/weixin_43586120/article/details/89456183

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值