likes
comments
collection
share

你很懂闭包嘛

作者站长头像
站长
· 阅读数 53

闭包是什么?

什么是闭包?是函数里面return函数?;是内部函数可以访问到其所在外部函数中声明的参数和变量?;要真正的理解闭包我们就要先去理解作用域。

作用域

什么是作用域,简单的来说就是这个变量可以使用的范围,也就是在程序中我们可以调用的范围。作用域可以分为函数作用域(局部作用域),全局作用域。

全局作用域

全局作用域就是可以被全局访问和使用的全局变量。优点是可以重复的使用,缺点是容易出现错误,变量容易出现被其他的地方窜改,代替的问题。一般来说没有在函数的的大括号之内声明的变量一般都会是全局作用域范围的全局变量。比如在最外层声明的函数和变量,window对象内置的属性和方法也都是全局的,还有没有声明直接定义值的也会被自动定义为全局对象。

    function nameFn(){
       name='a';
        window.alert("我是全局的方法")
    }
    nameFn();
    var nameOne='b';

如下图一样,我们可以看到在浏览器的调试scope中三种类型的值都在全局的scope中。 你很懂闭包嘛

函数作用域

函数作用即局部作用域,外部无法访问到该作用域内的变量和方法,里面的变量也就是局部变量一般会随着外部函数的消失而被消失,被浏览器的垃圾回收机制所回收。函数作用域中的所声明的变量就是局部变量(包含的函数的参数)。优点是不会被污染,缺点是不可以重复使用。

作用域链

当多重的作用域嵌套的时候就会产生一个由里向外的作用域链。作用域链的规则是:js引擎从当前执行的作用域开始,先查看当前作用域是否存在,若如果不存在则继续向外寻找,直接找到最外层的全局作用域,才会停止查找。

你很懂闭包嘛

作用域小测

//第一题 四个console会输出什么?
console.log(a)
if(false){
  var a='我是一个什么变量?';
}
console.log(a)

console.log(b)
function test(){
  var b='我又是一个什么变量?';
}
test();
console.log(b)

//第二小题
var a=10
function test(){
  var a=100
  a++
  console.log(a)
}
test();
console.log(a)

var b=10;
function test(){
  b=100
  b++
  console.log(b)
}
test()
console.log(b)

在第一小题中,a是两个都是undefined。因为在条件判断的大括号中,不产生作用域,所以a是全局变量,又因为变量提示的原因,所以提前声明到了前面,但是因为条件判断是false,没有进行赋值,所以等于undefined。b则会报错,因为在函数作用域内部声明,所以是局部变量,无法进行访问。

在第二小题aconsole的结果是101和10。第一个a因为都知道在a++后变成了101,第二个a是10,a++已经在本级的作用域找到了局部变量a,所以不会向上层的作用域找了,把局部变量a变成了101,函数执行后,局部变量就消失了,所以外面的console就是10。b的console的打印的结果是101和101,因为在函数中没有声明只是赋值了,所以是全局变量,所以都是101。

然后我们再看看一道有点意思的题目。

var a=10
function test(a){
     a++;
     console.log(a)
}
test(a)
console.log(a)

这道题中,第一个console打印的是11,第二个console打印又成了10,因为我们传入的参数也是一个局部变量,所以会a++后消失,所以第二个打印的是外层的全局变量a,它的值是10。

看完作用域后我们发现,全局作用域的全局变量可以重复使用,却容易被污染,函数作用域的局部变量不会被污染却会随函数执行而消失,不可以重复使用,那怎么可以即重复使用又不容易被污染呢?答案就是闭包

闭包

闭包的定义

什么是闭包呢?我们先来看下官方解释。

1.小"黄"书(你不知道的JavaScript): 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行. 2.红宝书(JavaScript高级程序设计): 闭包是指有权访问另一个函数作用域中的变量的函数. 3.MDN:闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。

function f1(){
    var a=10
   function f2(){
      a++;
      console.log(a)    
    }
    return f2;
}
var retult= f1();
 retult()//11
 retult()//12

我们直接上案例,我们可以看到我们的变量不会随着函数执行结束而消失。可以一直被重复使用,那么什么是闭包呢,我的理解是外层的作用域中局部变量被里面的函数作用域链所牵引,不会随函数执行消失,从而产生闭包。

闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。上面的代码就已经反映出来了闭包的两个作用。

闭包的缺陷

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

闭包小测

function f1(){
    function f2(){
        var a=10;
        a++;
        console.log(a)
    }   
    return f2;
}
var result=f1();
result();//11
result();//11

因为用的是函数作用域中的变量,所以没有产生闭包。不会被保存下来,都是11.

 var name = "The Window";
  var object = {
    name : "My Object",

    getNameFunc : function(){
          
      return function(){
   
        return this.name;
      };

    }

  };
let result=object.getNameFunc()
console.log(result());//The Window

为什么是这个答案呢?因为我们result在window中被调用,所以this指向的是window,所以this.name指向的是全局的name。

var a = 2;
var obj = {
    a: 4,
    f1: (function () {
        this.a *= 2;
        var a = 3;
        return function () {
            this.a *= 2;
            a *= 3;
            console.log(a);
        };
    })(),
};
var f1 = obj.f1;
console.log(a);//4
f1();//9
obj.f1();//27

第一个a是4,因为我们f1是一个自执行函数,所以f1被执行this.a被*2所以等于4,这个也是因为this指向window,所以调用的是全局的a,和上面一个题一样的道理。function的a没有,所以向外找,在f1中找到了a,停止寻找。因为调用的外部的函数中的变量所以不会随函数执行而消失,所以一直累乘,出现9和27.

(完)。

转载自:https://juejin.cn/post/7248157339811823672
评论
请登录