JavaScript进阶(三)闭包原理与用法详解


Posted in Javascript onMay 09, 2020

本文实例讲述了JavaScript闭包原理与用法。分享给大家供大家参考,具体如下:

为了更好的理解,在阅读此文之前建议先阅读上一篇《JavaScript词法作用域与作用域链》

1.什么是闭包

闭包的含义就是闭合,包起来,简单的来说,就是一个具有封闭功能与包裹功能的结构。所谓的闭包就是一个具有封闭的对外不公开的,包裹结构,或空间。

在JS中函数构成闭包。一般函数是一个代码结构的封闭结构,即包裹的特性,同时根据作用域规则只允许函数访问外部的数据,外部无法访问函数内部的数据,即封闭的对外不公开的特性,因此说函数可以构成闭包。

概括:闭包就是一个具有封闭与包裹功能的结构。函数可以构成闭包。函数内部定义的数据函数外部无法访问,即函数具有封闭性;函数可以封装代码即具有包裹性,所以函数可以构成闭包。

2.闭包有什么用(解决什么问题)?

  1. 闭包不允许外部访问
  2. 要解决的问题就是间接访问该数据

函数就可以构成闭包,要解决的问题就是如何访问到函数内部的数据

function foo () {
 var num = 123;
 return num;
}
var res = foo();
console.log( res ); // =>123

这里的确是访问到函数中的数据了。但是该数据不能第二次访问,因此第二次访问的时候又要调用一次foo,表示又有一个新的num = 123出来了。

在函数内的数据,不能直接在函数外部访问,那么在函数内如果定义一个函数,那么在这个函数内部中是可以直接访问的

function foo() {
 var num = Math.random();
 function func() {
  return mun;
 }
 return func;
}
var f = foo();
// f 可以直接访问这个 num
var res1 = f();
var res2 = f();

我们使用前面学习的绘制作用域链结构图的方法来绘制闭包的作用域链结构图,如下:

JavaScript进阶(三)闭包原理与用法详解

3.闭包使用举例

如何获得超过一个数据

function foo () {
 var num1 = Math.random();
 var num2 = Math.random();
 return {
  num1: function () {
   return num1;
  },
  num2: function () {
   return num2;
  }
 }
}

如何完成读取一个数据和修改这个数据

function foo () {
 var num = Math.random();
 return {
  get_num : function () {
   return num;
  },
  set_num: function( value ) {
   return num = value;
  }
 }
}

4.基本的闭包结构

一般闭包的问题就是要想办法简洁的获取函数内的数据使用权,那么我们就可以总结出一个基本的使用模型。

  1. 写一个函数,函数内部定义一个新函数,返回新函数,用新函数获得函数内的数据
  2. 写一个函数,函数内部定义个一个对象,对象中绑定多个函数(方法),返回对象,利用对象的方法访问函数内的数据

5.闭包的基本用法

闭包是为了实现具有私有访问空间的函数的

带有私有访问数据的对象

function Person() {
 this.name = "张三";
 // setName( '' )
}

所有的私有数据,就是说只有函数内部可以访问的数据,或对象内部的方法可以访问的数据

最简单的实现:

function createPerson() {
 var __name__ = "";
 return {
  getName: function () {
   return __name__;
  },
  setName: function( value ) {
   // 如果不姓张就报错
   if ( value.charAt(0) === '张' ) {
    __name__ = value;
   } else {
    throw new Error( '姓氏不对,不能取名' );
   }
  }
 }
}
var p = createPerson();
p.set_Name( '张三丰' );
console.log( p.get_Name() );
p.set_Name( '张王富贵' );
console.log( p.get_Name() );

带有私有数据的函数

var func = function () {}
function func () {}
var foo = (function () {
 // 私有数据
 return function () {
  // 可以使用私有的数据
  ...
 };
});

6.闭包基本模型

对象模型

function foo () {
 // 私有数据
 return {
   method : function(){
    // 操作私有数据
   }
 }
}

函数模型

function foo(){
 // 私有数据
 return function(){
   // 可以操作私有数据
 }
}

7.沙箱模式(闭包应用的一个典范)

7.1 沙箱的概念

沙盘与盒子,就可以在一个笑笑的空间内模拟显示世界,特点是执行效果与现实世界一模一样,但是在沙箱中模拟与现实无关.

7.2 沙箱模式

沙箱模式就是一个自调用函数,代码写到函数中一样会执行,但是不会与外界有任何的影响

例如,在jQuery中

(function () {
 var jQuery = function () { // 所有的算法 }
 // .... // .... jQuery.each = function () {}
 window.jQuery = window.$ = jQuery;
})();
$.each( ... )

8.带有缓存功能的函数

以 Fibonacci 数列为例,改进传统计算斐波那契数列方法
我们来回顾一下传统递归方式求斐波那契数列方法,我们定义一个count变量来查看递归了多少次:

var count = 0;
function fibo( n ){
 count++;
 if( n ==0 || n == 1 ) return 1;
 return fibo( n - 1 ) + fibo( n - 2 );
}
fib1( 20 );
console.log( count1 );
// 5: 15
// 6: 25
// ...
// 20: 21891

当 n = 5 式,count = 15,当时当 n = 20 的时候,count就达到惊人的21891次,性能太低了

性能低的原因是 重复计算。如果每次将计算的结果存起来

  • 那么每次需要的时候先看看有没有存储过该数据,如果有,直接拿来用。
  • 如果没有再递归,但是计算的结果需要再次存储起来,以便下次使用

改进版:

var data = [ 1, 1 ];
var count = 0;
function fibo( n ) {
 count++;
 var v = data[ n ];
 if( v === undefined ){
   v = fibo( n - 1 ) + fibo( n - 2 );
   data[ n ] = v;
 }
 return v;
}
fibo( 100 );
console.log( count ); // 199

改进之后, n = 100的时候也才199次,大大提高了性能。

9.闭包的性能问题

函数执行需要内存,那么函数中定义的变量,会在函数执行结束后自动回收,凡是因为闭包结构的,被引出的数据,如果还有变量引用这些数据的话,那么这些数据就不会被回收。

因此在使用闭包的时候如果不适用某学数据了,一定要赋值一个null

var f = (function () {
 var num = 123;
 return function () {
  return num;
 };
})();
// f 引用着函数,函数引用着变量num
// 因此在不适用该数据的时候,最好写上
f = null;

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
jquery 打开窗口返回值实现代码
Mar 04 Javascript
基于Bootstrap+jQuery.validate实现Form表单验证
Dec 16 Javascript
JS动态添加iframe的代码
Sep 14 Javascript
封装的dialog插件 基于bootstrap模态对话框的简单扩展
Aug 10 Javascript
关于Javascript回调函数的一个妙用
Aug 29 Javascript
微信小程序实现全局搜索代码高亮的示例
Mar 30 Javascript
js实现简单模态框实例
Nov 16 Javascript
JavaScript类的继承操作实例总结
Dec 20 Javascript
JS立即执行函数功能与用法分析
Jan 15 Javascript
es6 for循环中let和var区别详解
Jan 12 Javascript
Vue使用轮询定时发送请求代码
Aug 10 Javascript
Echarts在Taro微信小程序开发中的踩坑记录
Nov 09 Javascript
JavaScript进阶(二)词法作用域与作用域链实例分析
May 09 #Javascript
JavaScript进阶(一)变量声明提升实例分析
May 09 #Javascript
JavaScript面向对象核心知识与概念归纳整理
May 09 #Javascript
Vue列表如何实现滚动到指定位置样式改变效果
May 09 #Javascript
Node.js API详解之 util模块用法实例分析
May 09 #Javascript
Vue实现PC端靠边悬浮球的代码
May 09 #Javascript
Auto.JS实现抖音刷宝等刷视频app,自动点赞,自动滑屏,自动切换视频功能
May 08 #Javascript
You might like
模板引擎Smarty深入浅出介绍
2006/12/06 PHP
PHP连接SQLServer2005 的问题解决方法
2010/07/19 PHP
基于curl数据采集之正则处理函数get_matches的使用
2013/04/28 PHP
Laravel框架模板继承操作示例
2018/06/11 PHP
Javascript load Page,load css,load js实现代码
2010/03/31 Javascript
跟着Jquery API学Jquery之一 选择器
2010/04/07 Javascript
JQuery从头学起第三讲
2010/07/06 Javascript
IE无法设置短域名下Cookie
2010/09/23 Javascript
JS复制内容到剪切板的实例代码(兼容IE与火狐)
2013/11/19 Javascript
网站繁简切换的JS遇到页面卡死的解决方法
2014/03/12 Javascript
原生JS实现LOADING效果
2015/03/16 Javascript
Jquery数字上下滚动动态切换插件
2015/08/08 Javascript
基于JQuery实现图片轮播效果(焦点图)
2016/02/02 Javascript
怎么限制input的text里输入的值只能是数字(正则、js)
2016/05/16 Javascript
实用又漂亮的BootstrapValidator表单验证插件
2016/05/30 Javascript
ajax图片上传,图片异步上传,更新实例
2016/12/30 Javascript
js封装成插件的步骤方法
2017/09/11 Javascript
js实现掷骰子小游戏
2019/10/24 Javascript
[58:12]Ti4第二日主赛事败者组 LGD vs iG 3
2014/07/21 DOTA
[46:38]完美世界DOTA2联赛PWL S2 Magma vs PXG 第三场 11.28
2020/12/02 DOTA
python实现跨文件全局变量的方法
2014/07/07 Python
Python的Flask站点中集成xhEditor文本编辑器的教程
2016/06/13 Python
python中abs&map&reduce简介
2018/02/20 Python
Appium Python自动化测试之环境搭建的步骤
2019/01/23 Python
Python3简单实现串口通信的方法
2019/06/12 Python
PyCharm更改字体和界面样式的方法步骤
2019/09/27 Python
高中三年学习生活的自我评价
2013/10/10 职场文书
小学学校评估方案
2014/06/08 职场文书
横幅标语大全
2014/06/17 职场文书
地球一小时宣传标语
2014/06/24 职场文书
党员个人对照检查材料范文
2014/09/24 职场文书
审计局2014法制宣传日活动总结
2014/11/01 职场文书
爱心捐款活动总结
2015/05/09 职场文书
奖金申请报告模板
2015/05/15 职场文书
闪闪红星观后感
2015/06/08 职场文书
利用python实时刷新基金估值(摸鱼小工具)
2021/09/15 Python