javascript中的闭包概念与用法实践分析


Posted in Javascript onJuly 26, 2019

本文实例讲述了javascript中的闭包概念与用法。分享给大家供大家参考,具体如下:

闭包的概念:闭包是指有权访问另一个函数作用域中的变量的函数 (引自《javascript高级程序设计第三版》178页)。闭包的优点是不会产生全局变量,避免变量污染问题,但是闭包也有一个缺点就是闭包携带包含它的函数作用域会比其它函数占用更多的内存,过度使用会导致内存占用过多。

wiki上关于闭包的概念:

In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] A closure—unlike a plain function—allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

简单来说:闭包是一个存储了函数以及与这个函数相关的环境信息的记录。

闭包实践一:初次体验闭包

function a() {
 var temp = 100;
 function b() {
  console.log(++temp);
 }
 return b;
}
var fn = a(); // 此时fn属于全局的函数。
fn();// 101
fn();// 102

在函数a的内部return出了一个局部函数b。让函数fn接收函数a的返回值,也就是函数b。连续执行了两次fn,输出结果101,,102表示,函数fn一直引用着函数a中的局部变量temp。每次调用fn,temp都会被自增1。从此处说明了函数a一直没有被垃圾回收机制(GC)回收。以上代码还可以这样优化:

function a() {
 var temp = 100;
 return function() {
  console.log(++temp);
 }
}
var fn = a();
a = null;
fn();// 101
fn();// 102
fn = null; // 调用完毕后要,要解除对内部匿名函数的引用,以便释放内存

闭包实践二:闭包与变量

分析下面的代码

html结构:

<ul>
 <li>0</li>
 <li>1</li>
 <li>2</li>
</ul>

javascript结构:

var ul = document.querySelector('ul');// 为了演示方便,直接用html5的api
var lis = ul.children;
for(var i=0; i< lis.length; i++) {
 lis[i].οnclick=function(){
  console.log(i);
 }
}

当点击每个li时,输出的全都是3,在点击事件之前,for循环早已经执行完了,i的值为3。为了防止这种情况发生,for循环还可以修改成这样:

for(var i=0; i< lis.length; i++) {
 lis[i].οnclick=function(num){
  return function(){
   console.log(num);
  }
 }(i)
}

由于函数是按值传递的,所以就会将变量i的当前值赋给num,而在函数内部又返回了一个访问num的闭包。这样每次i的值就保存下来了。值得一提的是在ECMAScript6中可以用严格模式下用let 来声明i。这样可以直接保存i,有关es6,以后再深入学习,示例代码如下:

javascript结构:

'use strict'
let ul = document.querySelector('ul');
let lis = ul.children;
for(let i=0; i< lis.length; i++) {
 lis[i].οnclick=function(){
   console.log(i);
 }
}

闭包实践三:对实践二的深层剖析,闭包保存的是整个变量对象,而不是某个特殊的变量。(出自 《javascript高级程序设计第三版》 181页)

/* createFunctions方法返回一个函数数组 result */
function createFunctions() {
 var result = new Array();
 for(var i=0; i<10;i++) {
  result[i] = function() {
   return i;
  }
 }
 return result;
}
var arr = createFunctions();
// 我们拿到并输出第一个数组元素函数的返回值
var fn0 = arr[0];
var varible0 = fn0();
console.log(varible0); // 返回的是 10
// 我们拿到并输出第二个数组元素函数的返回值
var fn1 = arr[1];
var varible1 = fn1();
console.log(varible1); // 返回的是 10
// 可见闭包保存的是这个变量i对象,i的最终结果是10

我们只要对代码稍稍优化,用自执行函数来处理,就可以达到我们的预期了,如下:

function createFunctions() {
 var result = new Array();
 for(var i=0; i<10;i++) {
  result[i] = (function(num) {
   return function(){
    return num;
   }
  })(i)
 }
 return result;
}
var arr = createFunctions();
// 我们拿到并输出第一个数组元素函数的返回值
var fn0 = arr[0];
var varible0 = fn0();
console.log(varible0); // 返回的是 0
// 我们拿到并输出最后一个数组元素函数的返回值
var fn9 = arr[9];
var varible9 = fn9();
console.log(varible9); // 返回的是 9

闭包实践四:闭包与this  使用不同的编程方式使用闭包,this指向不同的对象

var color = 'black';
var person = {
 color:"yellow",
 getColorFun1:function(){
  return function(){
   return this.color;
  }
 },
 getColorFun2:function(){
  var that = this;
  return that.color;
 }
}
console.log(person.getColorFun1()()); // 指向了 black (备注:fn()()这种写法只限于非严格模式下)
console.log(person.getColorFun2()); // 指向了yellow

说明:当调用到person.getColorFun1()的时候,在全局变量中生成一个函数function(){return this.color},此时的this指向是window,所以执行到person.getColorFun1()()的时候,color为window对象下的变量color为black

而在person.getColorFun2函数中用that保存了当前对象person,而在闭包函数里面return出去的color是person的color,所以执行完person.getColorFun2()()的时,color是yellow。

实践四:闭包的高级应用

示例1:实现函数节流 

window.onresize = throttle(function(){
  var width = window.innerWidth || document.documentElement.clientWidth;
  console.log(width);
},300); // 调节浏览器窗口,在松手后的0.3s后执行
function throttle(fn,delay) {
   var timer = null;
   return function() {
     clearTimeout(timer);
     timer = setTimeout(fn,delay);
   }
}

示例2:实现封装对象

var Person = (function(){
 var haha = 0; // 这里表示可以定义一能够使用到的参数
 return function(name, age){
  ++ haha; // 这里表示去使用定义到的参数,虽然在此处并没有实际意义。
  this.name = name;
  this.age = age;
 };
})();
Person.prototype = {
 say : function(){
  console.log(this.name + ' say hi');
 }
}
var p1 = new Person('zhang san', 10);
var p2 = new Person('li si', 20);
console.log(p1.name); // zhang san
p1.say(); // zhang san say hi
p2.say(); // li si say hi

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

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

Javascript 相关文章推荐
javascript Array.remove() 数组删除
Aug 06 Javascript
JavaScript中的稀疏数组与密集数组[译]
Sep 17 Javascript
使用JavaScript 实现对象 匀速/变速运动的方法
May 08 Javascript
js中浮点型运算BUG的解决方法说明
Jan 06 Javascript
javaScript的函数对象的声明详解
Feb 06 Javascript
javascript实现跨域的方法汇总
Jun 25 Javascript
Vue.js 2.0 移动端拍照压缩图片预览及上传实例
Apr 27 Javascript
利用vscode编写vue的简单配置详解
Jun 17 Javascript
详解react-router4 异步加载路由两种方法
Sep 12 Javascript
Vuejs 2.0 子组件访问/调用父组件的方法(示例代码)
Feb 08 Javascript
使用vue打包进行云服务器上传的问题
Mar 02 Javascript
vue路由的配置和页面切换详解
Sep 09 Javascript
layui自定义插件citySelect实现省市区三级联动选择
Jul 26 #Javascript
微信小程序自定义头部导航栏和导航栏背景图片 navigationStyle问题
Jul 26 #Javascript
jQuery-Citys省市区三级菜单联动插件使用详解
Jul 26 #jQuery
微信小程序—setTimeOut定时器的问题及解决
Jul 26 #Javascript
layUI实现三级导航菜单效果
Jul 26 #Javascript
layui实现三级联动效果
Jul 26 #Javascript
layui实现三级导航菜单
Jul 26 #Javascript
You might like
基于php上传图片重命名的6种解决方法的详细介绍
2013/04/28 PHP
is_uploaded_file函数引发的不能上传文件问题
2013/10/29 PHP
CodeIgniter框架过滤HTML危险代码
2014/06/12 PHP
在Linux系统的服务器上隐藏PHP版本号的方法
2015/06/06 PHP
win平台安装配置Nginx+php+mysql 环境
2016/01/12 PHP
ThinkPHP5.0框架控制器继承基类和自定义类示例
2018/05/25 PHP
jQuery中add实现同时选择两个id对象
2010/10/22 Javascript
Javascript获取HTML静态页面参数传递值示例
2013/08/18 Javascript
jquery统计复选框选中示例
2013/11/05 Javascript
js交换排序 冒泡排序算法(Javascript版)
2014/10/04 Javascript
JavaScript表单焦点自动切换代码
2016/07/24 Javascript
JavaScript trim 实现去除字符串首尾指定字符的简单方法
2016/12/27 Javascript
原生JS实现隐藏显示图片 JS实现点击切换图片效果
2021/01/27 Javascript
关于jQuery里prev()的简单操作代码
2017/10/27 jQuery
ejsExcel模板在Vue.js项目中的实际运用
2018/01/27 Javascript
使用imba.io框架得到比 vue 快50倍的性能基准
2019/06/17 Javascript
vue全局使用axios的操作
2020/09/08 Javascript
在Django的URLconf中使用多个视图前缀的方法
2015/07/18 Python
浅谈Python2.6和Python3.0中八进制数字表示的区别
2017/04/28 Python
python查看模块,对象的函数方法
2018/10/16 Python
Django上线部署之IIS的配置方法
2019/08/22 Python
python自动化UI工具发送QQ消息的实例
2019/08/27 Python
使用TFRecord存取多个数据案例
2020/02/17 Python
Python3 pickle对象串行化代码实例解析
2020/03/23 Python
python_matplotlib改变横坐标和纵坐标上的刻度(ticks)方式
2020/05/16 Python
通过实例解析python创建进程常用方法
2020/06/19 Python
Python select及selectors模块概念用法详解
2020/06/22 Python
css3 transform 3d 使用css3创建动态3d立方体(html5实践)
2013/01/06 HTML / CSS
HTML5 body设置自适应全屏
2020/05/07 HTML / CSS
白俄罗斯在线大型超市:e-dostavka.by
2019/07/25 全球购物
安全承诺书格式
2014/05/21 职场文书
2014民事授权委托书范本
2014/09/29 职场文书
党员检讨书
2014/10/13 职场文书
2015年高校辅导员工作总结
2015/04/20 职场文书
汤姆索亚历险记读书笔记
2015/06/29 职场文书
关爱留守儿童主题班会
2015/08/13 职场文书