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 相关文章推荐
javascript-TreeView父子联动效果保持节点状态一致
Aug 12 Javascript
jquery查找父元素、子元素(个人经验总结)
Apr 09 Javascript
node.js使用require()函数加载模块
Nov 26 Javascript
函数window.open实现关闭所有的子窗口
Aug 03 Javascript
jQuery树形下拉菜单特效代码分享
Aug 15 Javascript
js实现的Easy Tabs选项卡用法实例
Sep 06 Javascript
JavaScript中日期的相关操作方法总结
Oct 24 Javascript
jquery实现横向图片轮播特效代码分享
Nov 19 Javascript
js实现数组冒泡排序、快速排序原理
Mar 08 Javascript
JavaScript累加、迭代、穷举、递归等常用算法实例小结
May 08 Javascript
vue+element 模态框表格形式的可编辑表单实现
Jun 07 Javascript
Vue3 的响应式和以前有什么区别,Proxy 无敌?
May 20 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
php数据结构与算法(PHP描述) 查找与二分法查找
2012/06/21 PHP
基于ubuntu下nginx+php+mysql安装配置的具体操作步骤
2013/04/28 PHP
PHP采集类Snoopy抓取图片实例
2014/06/19 PHP
PHP登录环节防止sql注入的方法浅析
2014/06/30 PHP
PHP使用fopen与file_get_contents读取文件实例分享
2016/03/04 PHP
深入解析PHP的Yii框架中的缓存功能
2016/03/29 PHP
php删除二维数组中的重复值方法
2018/03/12 PHP
NodeJS创建基础应用并应用模板引擎
2016/04/12 NodeJs
jquery动态切换背景图片的简单实现方法
2016/05/14 Javascript
详细分析Javascript中创建对象的四种方式
2016/08/17 Javascript
Bootstrap整体框架之CSS12栅格系统
2016/12/15 Javascript
Vue+Jwt+SpringBoot+Ldap完成登录认证的示例代码
2018/05/21 Javascript
详解webpack的proxyTable无效的解决方案
2018/06/15 Javascript
Jquery 动态添加元素并添加点击事件实现过程解析
2019/10/12 jQuery
python实现在无须过多援引的情况下创建字典的方法
2014/09/25 Python
Python中django学习心得
2017/12/06 Python
Python编程pygame模块实现移动的小车示例代码
2018/01/03 Python
浅谈利用numpy对矩阵进行归一化处理的方法
2018/07/11 Python
python 字符串只保留汉字的方法
2018/11/16 Python
钉钉群自定义机器人消息Python封装的实例
2019/02/20 Python
关于Python3 lambda函数的深入浅出
2019/11/27 Python
python 画函数曲线示例
2019/12/04 Python
pyecharts动态轨迹图的实现示例
2020/04/17 Python
Pandas的Apply函数具体使用
2020/07/21 Python
15个应该掌握的Jupyter Notebook使用技巧(小结)
2020/09/23 Python
python小技巧——将变量保存在本地及读取
2020/11/13 Python
Python中的流程控制详解
2021/02/18 Python
python实现杨辉三角的几种方法代码实例
2021/03/02 Python
英国马莎百货印度官网:Marks & Spencer印度
2020/10/08 全球购物
历史学专业毕业生求职信
2013/09/27 职场文书
小学一年级评语大全
2014/04/22 职场文书
2014年社会实践活动总结范文
2014/04/29 职场文书
2016年优秀共产党员先进事迹材料
2016/02/29 职场文书
Python基础之条件语句详解
2021/06/16 Python
Node与Python 双向通信的实现代码
2021/07/16 Javascript
golang使用map实现去除重复数组
2022/04/14 Golang