前端初级的js闭包理解

418 阅读4分钟

闭包

  • 菜鸟教程的原话: 闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。

  • 菜鸟的原话:访问上层函数的作用域的内层函数就是闭包

  • 大白话:一个函数内部返回一个函数

function foo(){
    var a = 2;
    return function bar(){
        return a
    }
}
foo(); // 2

这样看大概知道了,但是如果是这样勒

var Book = (function(){
    var _repertory = 2020
    function _subtractRepertory(){
        _repertory--
    }
    function book(name){
        this.name = name
        this.getRepertory = function(){
            return _repertory
        }
    }
    book.prototype.buy = function() {
        _subtractRepertory() 
    }
    book.prototype.getName = function(){
        return this.name
    }
    return book
})()


let book1 = new Book('js进阶') //2020
let book2 = new Book('vue进阶') // 2020

book1.getRepertory() // 2020
book2.getRepertory() // 2020

book2.buy()

book2.getRepertory() // 2019
book1.getRepertory() // 2019

简言之,实例化了两本书,库存都是2020,我买了《vue进阶》,所以库存变成了2019,但是《js进阶》我没买,但是库存也变成了2019,代码有点多。换个简单的。


function add() { 
  var count = 0; 
  return  function callBack() { 
      count++; 
      console.info(count); 
  } 
} 
var add1 = add();
add1();     //1 
add1();     //2 

var add2 = add();
add2();     //1 
add2();     //2 

add函数一看就形成了闭包,但是为什么add1执行的时候记住了上一次的值,而add2一开始又忘记了,然后又记住了。

如果你只靠上面三句话,是无法快速理解的

那就再加一句话:闭包的最外层函数每次执行时,都会导致重新开辟一个空间,而内部形成闭包的函数会始终记录外部函数开辟的那一个空间

回看上一个例子

function add() { // 是最外层函数
  var count = 0; 
  return  function callBack() { 
      count++; 
      console.info(count); 
  } 
} 
var add1 = add(); // 最外层函数执行 创造了第一个空间 此时count=0
//注意此时的add1 不是等于add函数,而是等于add函数执行过后,返回的内部callBack函数,而这个callBack函数指向的是第一个空间

add1(); //1  // 第一次执行 就等于callBack在执行,其内部的count指向的是第一个空间,所以这时count 从0自加变成了1
add1(); //2  // 第二次执行 这里的callBack也是指向第一个空间的,所以count 从1变成了2

var add2 = add(); // // 最外层函数执行 创造了第二个空间,此时count=0
add2();     //1 // 同理
add2();     //2 // 同理

此时在回头看买书的例子

// 不用看
var Book = (function(){
    var _repertory = 2020
    function _subtractRepertory(){
        _repertory--
    }
    function book(name){
        this.name = name
        this.getRepertory = function(){
            return _repertory
        }
    }
    book.prototype.buy = function() {
        _subtractRepertory() 
    }
    book.prototype.getName = function(){
        return this.name
    }
    return book
})()

let book1 = new Book('js进阶') //2020
let book2 = new Book('vue进阶') // 2020

// 以上逻辑,不用看直接打印book1和book2,和Book

console.log(book1,book2,Book) // book,book,book 都是内部的return出来的闭包函数

// 找最外层的函数,是立即执行函数,且只执行了一次,只开辟了一个空间,所以所有的book闭包函数都是指向同一个空间的。
// 所以book1和book2,访问到的_repertory也就是书的库存都是同一个了。答案揭晓

book1.getRepertory() // 2020
book2.getRepertory() // 2020

book2.buy()

book2.getRepertory() // 2019
book1.getRepertory() // 2019

个人以为在学习闭包的时候,要特别注意 “=”,等于号很容易就麻痹你了,多来几个变量,反复等于,或者实例化。基本上人就懵了。很简单,直接打印他,看他是什么就完事。别被复杂的,很长的函数吓到。其原理往往可能会特别简单。回到菜鸟教程的原话

  • 闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。

我反复阅读才发现,私有变量,有很多的函数写法,其实就是满足某种特定的需求。如果不了解 私有变量私有方法等,诸如此类的“专业名词”,那么大概率是不会知道别人为什么这么写。

所以我觉得在学习的过程中,不要专挑难的、复杂的,又或者什么原理,定义等,在那里死记硬百度。可以先了解这里面的“目录”,或者说各种各样的模式啊,名称什么的。看看为什么会有这些东西存在,再考虑怎么实现它,然后再看看有什么问题,再优化,那么大概率,你就写出了,别人的代码。

菜鸟们,加油fly吧!!!