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 相关文章推荐
对象的类型:本地对象(1)
Dec 29 Javascript
jquery中获取元素的几种方式小结
Jul 05 Javascript
javascript实现图片切换的幻灯片效果源代码
Dec 12 Javascript
javascript之典型高阶函数应用介绍二
Jan 10 Javascript
了不起的node.js读书笔记之例程分析
Dec 22 Javascript
javascript中tostring()和valueof()的用法及两者的区别
Nov 16 Javascript
AngularJS基础 ng-model-options 指令简单示例
Aug 02 Javascript
SpringMVC简单整合Angular2的示例
Jul 31 Javascript
如何开发出更好的JavaScript模块
Dec 22 Javascript
详解webpack4升级指南以及从webpack3.x迁移
Jun 12 Javascript
express框架下使用session的方法
Jul 31 Javascript
JavaScript实现拖拽效果
Mar 16 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(1) php开发环境配置
2010/02/15 PHP
php自定义函数实现JS的escape的方法示例
2016/07/07 PHP
phpinfo()中Loaded Configuration File(none)的解决方法
2017/01/16 PHP
PHP标准库 (SPL)――Countable用法示例
2020/06/05 PHP
jquery 圆形旋转图片滚动切换效果
2011/01/19 Javascript
jquery中dom操作和事件的实例学习 仿yahoo邮箱登录框的提示效果
2011/11/30 Javascript
再谈javascript面向对象编程
2012/03/18 Javascript
基于jQuery捕获超链接事件进行局部刷新代码
2012/05/10 Javascript
jquery 通过name快速取值示例
2014/01/24 Javascript
使用jQuery设置disabled属性与移除disabled属性
2014/08/21 Javascript
javascript制作照片墙及制作过程中出现的问题
2016/04/04 Javascript
JavaScript 函数的执行过程
2016/05/09 Javascript
浅析JavaScript中的array数组类型系统
2016/07/18 Javascript
js前端日历控件(悬浮、拖拽、自由变形)
2017/03/02 Javascript
JavaScript中双向数据绑定详解
2017/05/03 Javascript
JS运动特效之链式运动分析
2018/01/24 Javascript
微信jssdk逻辑在vue中的运用详解
2018/11/14 Javascript
JavaScript命名空间模式实例详解
2019/06/20 Javascript
jquery实现上传图片功能
2020/06/29 jQuery
vue基于better-scroll实现左右联动滑动页面
2020/06/30 Javascript
[06:16]《DAC最前线》之地区预选赛全面回顾
2015/01/19 DOTA
基础的十进制按位运算总结与在Python中的计算示例
2016/06/28 Python
python OpenCV学习笔记直方图反向投影的实现
2018/02/07 Python
python实现微信每日一句自动发送给喜欢的人
2019/04/29 Python
使用Python 自动生成 Word 文档的教程
2020/02/13 Python
django中嵌套的try-except实例
2020/05/21 Python
使用darknet框架的imagenet数据分类预训练操作
2020/07/07 Python
德国baby-markt婴儿用品瑞士网站:baby-markt.ch
2017/06/09 全球购物
Java TransactionAPI (JTA) 主要包含几部分
2012/12/07 面试题
介绍一下如何利用路径遍历进行攻击及如何防范
2014/01/19 面试题
西式结婚主持词
2014/03/14 职场文书
个人融资协议书
2014/10/02 职场文书
个人催款函范文
2015/06/24 职场文书
幼儿园体操比赛口号
2015/12/25 职场文书
MySQL 数据库范式化设计理论
2022/04/22 MySQL
Android studio 简单计算器的编写
2022/05/20 Java/Android