js语法深入四:史上最简洁最准确的闭包描述

来源:互联网 发布:数控车螺纹编程 编辑:程序博客网 时间:2024/05/26 08:43

上一篇《对js函数的理解及深入讨论》

谈起闭包很多人都会挠头。我以前也是,因此我查阅了很多资料,综合分析和理解,自认现在已经识得了闭包的真面目。下面我就从闭包的产生条件闭包的作用闭包的实质闭包的本质闭包产生的时机闭包的应用 等几个方面具体和系统的阐述一下闭包。

前导

我们谈闭包有一个前提条件:

一般情况下,函数执行完毕,它内部的变量会被销毁。

现在我们在来看

闭包是一种语言特性,可以使嵌套的内部函数访问并保存嵌套的外部函数的局部变量。外部函数执行完后,只要内部函数还存在,那其中保存外部函数的局部变量也会保存下来。可能看这句话有些绕,咱们往下看

闭包产生的条件

1 存在函数嵌套,函数fun2在函数fun1执行时被定义,我们认为fun2是fun1的内部函数。

2 内部函数引用了外部函数的局部变量。

3 内部函数被暴露出来,可以在外部函数外调用,内部函数中访问的外部函数的局部变量被保存下来,就形成了闭包。

闭包的作用

1.延长了局部变量的生命周期

2.在函数外可以间接操纵内部局部变量

闭包的本质

一种语言特性,在浏览器的具体实现中是内部函数对象内部的一个隐式对象【Closure】,用以保存内部函数引用的外部函数的局部变量。

(隐式变量:存在于浏览器内存中,帮助完成语法功能,但不能被程序员操作的对象)

如果有兴趣可以用chrome开发者工具中Sources指令查一下函数执行时的栈结构附上一段调试代码

  function fn1 () {    var a = 2    var b = 'abc'    function fn2 () {       console.log(a)    }  }  fn1()

闭包产生的时机

访问了外部函数局部变量的内部函数被定义时产生了闭包。闭包的生命周期取决于内部函数的生命周期。

闭包到底是内部函数被定义是产生,还是被暴露出去的时候产生?

经常有人会纠结这个问题,其实两者说的都有道理,内部函数定义时就产生了闭包。但此时内部函数也是被外部函数的变量指向的。如果不把内部函数对象暴露出去,那等函数执行完内部变量对象也就被销毁了,闭包也无从谈起。

闭报的应用:

说起闭包,我们通常的认识这是一个高级语法,最大的作用就是提高逼格。
非也非也,其实我们也js代码从来没有离开过闭包。

我们曾经长期使用的Jq框架,就是必报的有力实践者。大家知道的,先进大部分的框架和模块都是用IIFE封装的。所以我们使用的方法和属性都是这个自调用函数的内部变量。

我们使用的$的方法中有保存着大量的IIFE的内部资源。这变量都保存在暴露出来的方法的闭包中。

我们在写较复杂的原生代码时也经常会使用到闭包,只是我们不经意而已。这就是所谓的语法糖。

案例

闭包的魅力和招人恨的的能力,从来不是因为这个概念有多难理解。而是它可以让代码有很多“玩法”,这些玩法通常会颠覆我们的直觉

案例一:

 var name = "The Window"; var object = {     name : "My Object",     getNameFunc : function(){        return function(){             return this.name;            };        } }; alert(object.getNameFunc()());//这个很简单,且没有应用到闭包,稍微有点难点的就是object.getNameFunc()是作为一个全局函数被调用的所以,他的this指向window。关于this稍微啰嗦一句,this是函数代码执行前js引擎预定义的一个变量,这个变量指向调用该函数或者所方法的对象。说白了,正在执行的函数是谁调用的,他里面的this就指向谁。

案例二:

  var name2 = "The Window";     var object2 = {        name2 : "My Object",        getNameFunc : function(){          var that = this;          return function(){             return that.name2;         };    }     };    alert(object2.getNameFunc()()); //?  my object    内部函数保存外部函数的局部变量that。

案例三:

function fun(n,o) {    console.log(o)     return {         fun:function(m){         return fun(m,n)        }     }}  var a = fun(0)  //(1)  a.fun(1)        //(2)  a.fun(2)      //(3)  a.fun(3)//undefined,0,0,0     //(4)  var b = fun(0).fun(1).fun(2).fun(3)//undefined,0,1,2    //(5)  var c = fun(0).fun(1)  //(6)  c.fun(2)      //(7)  c.fun(3)//undefined,0,1,1   //(8)    这个题很有意思,我建议看到这里的小伙伴试着做一做。    下面我们一行一行的解析一下    (1)先看输出了啥,o传实参,所以o为undefined,打印undefined;        再看返回什么,返回一个对象,这个对象中有一个方法fun。                      {                         fun:function(m){                         return fun(m,n)                        }                     }        此时a指向这个对象。    (2)a.fun方法是第一次执行是被定义的,且a.fun访问了fun函数的局部变量n。        此后在怎执行a.fun一直指向原来的函数。也就是说a.fun的闭包中始终有一个n = 0;        所以后面三条语句是fun函数执行了三次,分别是fun(1,0);fun(2,0);fun(3,0)    (5)第一个fun(0) 实在执行一个函数fun,与(1)相同, 输出undefined        fun(0).fun(1)  与(2)相同,输出 0;但其返回一个新的对象,这个对象中的方法与a.fun不指向        同一个函数对象了。这个方法闭包中的n是访问第二次fun(m,n)函数的内部变量得来的,所以此时n=1;        故fun(0).fun(1).fun(2)  输出1        同理fun(0).fun(1).fun(2).fun(3) 再输出一个3;    (6)同(1)(2)输出 undefined 和 0;并且返回的对象中fun方法的闭包中保存着n=1;    (7)(8)  调用的是同一个方法 都输出1
原创粉丝点击