您的位置:首页 > Web前端 > JavaScript

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

2020-08-18 20:08 162 查看

作者:姚一豪

日期: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(); // 520

add();

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 

 

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: