js 闭包

 闭包:闭包的功能是当父作用域执行完,仍能访问自身声明的作用域和父作用的函数,对这句话我有下面的两个理解

  1 闭包是一种函数(是对function对象的引用),在它的定义中存在对父作用域变量和函数的引用,才能导致父作用域执行完仍能访问父作用域

  2 闭包是一种嵌套函数函数声明,将它返回给外部的变量(本身也是一种引用),这样一直存在对父作用域的引用,就能使父作用域不被垃圾回收机制回收,达到相应的功能

 

 function test() {
    var name = ‘haha‘;
    function sayName() {
      console.log(name);
    }
    sayName();
  }
  test();//haha

 

上面的例子中就是一种嵌套函数的声明,在test()内部定义了一个局部的变量(属性)name,并且在它内部定义了一个函数sayName()并且引用了它的属性name

在javascript中调用函数的时候会隐式的生成call对象,也就是活动对象,活动对象中包含着它的作用域和所访问的属性,在结合作用链域达到对付作用域属性的访问

上面的例子中,调用test(),在test()中调用sayName(),此时生成sayName的活动对象,沿着作用链域找到父作用域的name属性,sayName执行完毕后,它的活动对象被销毁,到达test的活动对象,最后销毁test的活动对象

  我们可以换一种方案  

 

 function test() {
   var name = ‘haha‘;
    return function sayName() {
      console.log(name);
    }
  }
  var func = test();
  func();//haha

 

  test()执行完毕,它的name属性应该不能被外部访问的(理应被垃圾回收),这是为什么呢?

  在test()内部定义了一个这样的函数(可以理解函数是test()的一个属性),它中存在对父作用域属性的访问,当我们把这个函数(属性)放回给外部的变量,在外部就存在对test()的属性的访问,导致test()不会被垃圾回收,所以我们仍能保证对test()的属性的访问,这就是闭包的基本原理,并且闭包引用的是当时的test()活动对象

闭包是一种特殊的函数,它在调用的时候会保持当时的变量名的查找的执行环境,也可以将它理解为具有状态的函数(javascript编程全解)

一个需要注意的问题 

function test() {
  var num = 1;
  function sayNum1() {
  console.log(num);
  }
  num++;
  function sayNum2() {
  console.log(num);
  }
  return [sayNum1,sayNum2];
}

var func = test();
func[0]();//2
func[1]();//2

在上面的例子中,返回了两个函数,他们都引用了test的活动对象形成闭包,之所以要用这个例子是因为声明两个函数的时候num的值虽然不一致但是他们最终都是引用test的活动对象,所以会返回相同的结果,由此我想起我去面试的一次尴尬经历,在<li><li>上绑定onclick事件,单击相应的列表项是现实显示相应的序号,我当时的写的就不贴了,就是全部返回一个值,下面把正确的贴下来(还好回来又考虑了那个问题)

function test() {
  var lis = document.getElementById(‘container‘).getElementsByTagName(‘li‘);
    for(var i = 0;i < lis.length;i++) {
      lis[i].onclick = function(j){
        return function() {
          console.log(j);
          }
            }(i);
       }
}

 下面在介绍闭包的几种用法,也算回答了另一次面试的问题

1)可以通过闭包实现对信息的隐藏

  结构是这样的 (function(){函数体})();

  我们当即的调用匿名函数,但是在利用闭包使得变量在调用后依然存在的特点 

  

var obj = (function(){
                var position = {x:2,y:3};

                function sum_internal(a,b) {
                    console.log(Number(a) + Number(b));
                }//position和sum_internal(a,b)相当于私有变量和函数(属性)

                return {
                    sum:function(a,b) {return sum_internal(a,b);},
                    x: position.x,
                    y: position.y

                };
            })();

            obj.sum(1,5);//6
            console.log(obj.x);//2

上面的例子通过返回字面量的形式实现了对信息的隐藏

利用上面的方法可以将闭包与类进行结合,实现访问控制

function Person(name) {
				this.name = name;

				this.sayName = function() {
					console.log(this.name);
				}
			}

			var person1 = new Person(‘haha‘);
			person1.sayName();

 上面是一个简单的类的定义,在我的另一篇博客中也对这个例子进行了说明  原型继承 javascript 

 我们可以将上面的定义写成下面的形式,结合闭包

function Person(name) {
                return {sayName: function(){console.log(name);}};
            }

            var obj = Person(‘haha‘);
            obj.sayName();

闭包与回调函数

 我们可以将闭包注册为回调函数,也避免了this引用的问题

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。