AI智能
改变未来

JavaScript学习之浅谈闭包概念 ——-2020-08-18

作者:姚一豪

日期:2020年8月18日

一、变量的作用域

     变量的作用域分为:全局变量和局部变量.

 Js作为弱类型语言,可以体现在变量的作用域上,函数内部可以访问到全局变量

[code]var y = 3;function foo() {console.log(y);}foo(); // 3

 函数外部无法访问到内部变量

[code]function foo(){var y = 3;}console.log(y);  //    y is not defined

 

二、外部访问函数内部变量方法

函数外部若想访问获取函数内部变量,可以在函数内部在定义一个函数.

[code]function foo() {var y = 3;function foo2() {console.log(y); //3}}

 

   可见函数foo2就被包含于foo1中,这时候foo中的局部变量对foo2中是可见的,但是foo2中的变量对foo1中是不可见的,子对象会一级一级向上寻找所有父对象的变量,父对象所有的变量对子对象是可见的,这是Javascript语言的\”链式作用域\”结构.

  foo2中可以访问foo1中的变量,所以我们外部想要获取foo中的变量,可以让foo2作为返回值.

[code]function foo() {var n = 521;function foo2() {console.log(n);}return foo2;}var result = foo();result(); // 521

 

三、闭包的定义

 \”闭包\”在我通俗的理解为定义在一个函数内部的函数,可以读取父函数的局部变量.在JavaScript语言中,只有函数内部的子函数才能读取局部变量,在上一段代码中,foo2就是闭包closure.

四.闭包的用处

  闭包在我目前认为,最大的作用体现在两个方面,一是前面提到的访问局部变量,另外一个是它可以使变量的值始终保存在内存中.

[code]function foo() {var y = 520;add = function () {y++;};function foo2() {console.log(y);}return foo2;}var result = foo();result(); // 520add();result(); // 521

 

    在这段代码中,result实际上就是闭包foo2函数。它一共运行了两次,第一次的值是520,第二次的值是521。这证明了,函数foo中的局部变量y一直保存在内存中,并没有在foo调用后被自动清除。

   为什么会这样呢?原因就在于foo是foo2的父函数,而foo2被赋给了一个全局变量,这导致foo2始终在内存中,而foo2的存在依赖于foo,因此foo也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

   这段代码中另一个值得注意的地方,就是\”add=function(){y++}\”这一行,首先在add前面没有使用var关键字,因此add是一个全局变量,而不是局部变量。其次,add的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以add相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

五、使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

、例题分析

[code]function test() {var n = 4399;function add() {n++;console.log(n);}return { n: n, add: add };}var result = test();var result2 = test();result.add();result.add();console.log(result.n);result2.add();//4400//4401//4399//4400

 

 

js在执行之前,会将所有带var和function的进行提前定义和声明。(带var的提前声明,function声明和定义都完成了)

首先,在全局作用域下,进行预解释:

test=xxxfff000(开辟一个堆内存,里面存的是代码字符串)

var result(声明一个变量result)

var  result2(声明一个变量result2)

——————————-

代码执行:

result=test()  –>将test执行的返回结果赋值给result,是一个对象,再开辟一个堆内存,test执行,形成一个私有作用域A

再进行预解释和代码执行等一系列操作

result2=test()  同理

result.add()  –>方法执行形成一个私有作用域

n++  顺着作用域链向上寻找到test作用域AA这个作用域不销毁,因为被全局变量result占用了中的n为4399,n++ 》4400

(这时test这个作用域A下的n变成4400)

(1)   console.log(n)  //4400   

==============================

result.add()   –>方法执行形成一个新的私有作用域

n++  顺着作用域链向上寻找到test作用域(A)中的n为4400,n++ 》4401

(2)   console.log(n)  //4401  

===============================

(3)   console.log(result.n)  //4399

此时找的只是result对应的那个堆内存中的n

===============================

result2.add()  –>方法执行形成一个私有作用域

n++ 顺着作用域链向上寻找到test作用域(B)中的n为4399,n++ 》4400

(3)   console.log(n)  //4400 

 

 

 

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » JavaScript学习之浅谈闭包概念 ——-2020-08-18