关于javascript中的闭包
Mar 20, 2016
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称之为“闭包”
要了解闭包,首先要了解“词法作用域”与“作用域链”:
JavaScript使用的是函数作用域,而非块级作用域。(区别请自行google)
每一段JavaScript代码(全局代码或函数)都有一个与之相关联的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。
var goble_num = 1; function demo() { var local_num = 0; var a = 1; function add() { var b = 1; var c = 1; local_num = goble_num + c; return local_num ; } return add; } var tools_add = demo();//返回add函数 var new_num = tools_add();//返回一个新的数 console.log(local_num);//undefined,函数作用域
内部函数add的作用域链(在解析时就决定了这个作用域链)为:
- —add函数的私有变量对象(包含变量b和c)
- ——demo函数的私有变量对象(包含变量a和local_num和函数add)
- ———goble scope
变量解析:
- 当JavaScript执行到tools_add函数中的
local_num = goble_num + c;
时需要从add函数的作用域链上查找变量c、goble_num和local_num。 - 它会从链表中的第一个对象(tools_add函数的私有变量对象)开始查找,它找到了变量c,但并没有找到goble_num和local_num。
- 此时JavaScript会继续查找链上的下一个对象(demo函数的私有变量对象),它找到了变量local_num,但还是没有找到goble_num。
- 此时JavaScript则会继续查找链上的下一个对象,以此类推。
- 如果作用域链上没有任何一个对象含有属性goble_num,JavaScript就会认为这段代码的作用域链上不存在goble_num,并抛出引用错误(ReferenceError)异常。
- 如果找到了goble_num则终止查找。
闭包的特性“变量常驻内存”:
- 每次调用JavaScript函数的时候,都会为之创建一个新的对象用来来保存局部变量,把这个对象添加至作用域链中。
- 当函数返回的时候,就从作用域链中将这个绑定对象的变量删除。如果不存在嵌套函数,也没有其他引用指向这个变量绑定对象,它就会被当做垃圾回收掉。 (因为demo函数含有嵌套函数add,“demo函数的私有变量对象”就不会被当做垃圾回收)
- 如果定义了嵌套的函数,每个嵌套的函数都各自对应一条作用域链,并且这个作用域链都会指向一个共同的变量绑定对象。 (同时存在多个类add的函数,它们的作用域链都同时指向了“demo函数的私有变量对象”)
- 但如果这些嵌套的函数对象在外部函数中被保存了下来,那么它们也会和所指向的变量绑定对象一样当做垃圾回收。
(当执行到
var tools_add = demo();
时,嵌套函数add的变量绑定对象会被当做垃圾回收) - 但如果这个嵌套函数被作为返回值返回或者储存在某处的属性里,这时就会有一个外部引用指向这个嵌套的函数。它就不会被当做垃圾回收,并且他所指向的变量绑定对象也不会被当做垃圾回收
function test1() { return demo(); } //或 function test2() { this.add = demo(); //... } //如果存在以上2种函数,则上面函数中的"add函数的私有变量对象"则不会被当做垃圾回收 //===分割线=== var tools_add = demo(); //demo函数的私有变量绑定对象,还能被引用的到! var new_num = tools_add(); //demo函数的私有变量绑定对象,还能被引用的到! new_num = tools_add(); //... new_num = tools_add(); //所以它在此以前不会被垃圾回收机制回收,也就是所谓的“常驻内存”了