闭包的理解

闭包是Javascript语言特有的"链式做用域"结构(chain scope)变量的做用域有三种:全局做用域和局部做用域以及块做用域(ES6)。,子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立。node

闭包:JavaScript高级程序设计里写闭包是有权访问另外一个函数做用域中的变量的函数,使做用域获得了延长。咱们有时候在函数外部须要获得函数内的局部变量。而闭包就是将函数内部和函数外部链接起来的一座桥梁。浏览器

闭包的优势:服务器

  • 是闭包封住了变量做用域,有效地防止了全局污染
  • 能够读取其余函数内部的变量,让这些变量的值始终保持在内存中,不会随着函数的结束而自动销毁。
  • 能够很巧妙地实现静态私有变量、私有函数方法等

闭包的缺点: 因为闭包会使得函数中的变量都被保存在内存中,因此存在内存泄漏的风险闭包

  • 在浏览器端能够经过强制刷新解决,对用户体验影响不大
  • 在服务端,因为 node 的内存限制和累积效应,可能会形成进程退出甚至服务器沓机

使用场景 :函数内部变量只初始化一次dom

解决方法是显式对外暴露一个接口,专门用以清理变量:函数

/*1.清除失败,由于每次先执行mockData()后才会执行闭包,因此每次都会在局部做用域建立常量mem*/
function mockData() {
  const mem = {name:"lucas",age:22};

  return {
    clear: () => {
        for(let i in mem){
            delete mem[i];
        }
    }, // 显式暴露清理接口
    get: page => {
      if (page in mem) {
        return mem[page];
      }
      mem[page] = Math.random();
    }
  };
}

console.log(mockData().get('name')); //lucas
mockData().clear(); //清理变量
console.log(mockData().get('name')); //lucas
/* 输出结果
    这里执行屡次
    lucas
    这里执行屡次
    这里执行屡次
    lucas
*/


/*2.成功清除但代码不复用*/
const mem={name:"lucas",age:22};  //卸载外面
function mockData() {
  console.log("这里执行屡次")
  return {
    clear: () => {
        for(let i in mem){
            delete mem[i];
        }
    }, // 显式暴露清理接口
    get: (page) => {
      if (page in mem) {
        return mem[page];
      }
      mem[page] = "dwdwd";
    }
  };
}

console.log(mockData().get('name')); //lucas
mockData().clear(); //清理变量
console.log(mockData().get('name')); //undefined

/*
    这里执行屡次
    lucas
    这里执行屡次
    这里执行屡次
    undefined
*/

/*3.最好写法*/
function mockData() {
  const mem={name:"lucas",age:22};
  console.log("执行一次")
  return {
    clear: () => {
        for(let i in mem){
            delete mem[i];
        }
    }, // 显式暴露清理接口
    get: (page) => {
      if (page in mem) {
        return mem[page];
      }
      mem[page] = "dwdwd";
    }
  };
}

var result = mockData();

console.log(result.get('name')); //lucas
result.clear(); //清理常量对象
console.log(result.get('name')); //undefined

/*
    执行一次
    lucas
    undefined
*/

例如"变量只初始化一次"这样的需求可使用下面的例子this

销毁闭包产生的变量,实现递增例子1设计

//经过匿名函数能够实现闭包,简称匿名闭包函数

var foo = (function(varr) {
    var n = varr||0;
    return {
        add: function () {
            return ++n;
        },
        clearVariable: function () {
            n = null;
        }
    }
})(20); //因为匿名当即执行函数只会执行一次,因此这里实参数只能传一次(若须要传屡次请参考例子2)
foo.add()  //21
foo.add()  //22
foo.add()  //23
foo.clearVariable()  //n变量被销毁
foo.add()  //1

销毁闭包产生的变量,实现递增例子2code

/*写法1*/
function create_counter(initial) {
    var x = initial || 0;  //变量只会初始化一次
    return {
        inc: ()=> {
            return x++;
        },
        clear: ()=>{
            x=null;
        }
    }
}

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
c2.clear() //x变量被销毁
c2.inc(); // 1

/*写法2:这样写不方便销毁变量*/
function create_counter(initial) {
    var x = initial || 0;  //变量只会初始化一次
    function add(){
        return x++;
    }
    return add;
}

var c2 = create_counter(10);
c2();  //11
c2();  //12
c2();  //13
c2() = null; //清除函数也清除了变量
c2()   //报错不存在函数
var c2 = create_counter(20);
c2();  //21

销毁闭包产生的变量,实现递增例子3对象

function Class(){
 
 this.n = 0;
 this.func = ()=>{
  this.n ++;
  return this.n;   //闭包产生的变量需手动清除
 }
 
 this.clear = ()=>{
    return this.n=null; //销毁函数内部的变量,避免内存泄漏
 }
}
var obj = new Class();
obj.func()      //1
obj.func()      //2
obj.func()      //3
obj.clear()     //n变量被销毁
obj.func()      //1

后者的可扩展性更好. 当须要实现对一个变量的不一样操做时, 后一种能够只须要再定义一个不一样的函数(也就是简单线性扩展), 而前一种(闭包)则须要彻底重写。

若是仅仅是作一个简单的计数器,大可不用这样麻烦。下面这简短的代码就能轻松实现。

var a = 0;
function myFunction(){
    a++;
    document.getElementById("demo").innerHTML = a;
}

匿名闭包函数

var a = 1;
    (function test (){
        alert(a);
    })()

上面的function均可以称之为闭包(匿名闭包函数)。

闭包其实还有不少实用场景,好比,咱们想在页面上添加一些能够调整字号的按钮

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
相关文章
相关标签/搜索