什么是闭包?———>是一个函数,一个可以访问其他函数内部数据的函数。

栗子一:

function foo() {
var a = 1;
}
function fn() {
console.log(a);//报错,因为这里是无法访问到a的
}
function foo() {
var a = 1;
function fn() {
console.log(a);//这样是可以访问到a的
}
return fn;//fn就是闭包,其在foo内部调用没有意义,所以将其返回,交由外部来决定调用时机,更具开发意义,当执行外部函数时,才会创建闭包fn
}

一、闭包基本结构:
1.定义外层函数;
2.定义内部函数;
3.内层函数引用外层函数定义的数据;
4.要将内层函数作为外层函数的返回值; 
function outer() {
var data = {name: "xiaoming”};//外层函数的内部数据会一直缓存在内存中
function inner() {
return data;
}
return inner;
}
var closure1 = outer();//拿到闭包之后就可以决定什么时候执行它;
console.log(closure1());
var closure2 = outer();
console.log(closure2 == closure1);//false,由于调用了两次outer函数,从而创建了两个data对象,因此两个闭包访问到的数据(data)在内存中的地址是不同的;
var d1 = closure1();
Var d2 = closure1();
Console.log(d1 == d2);//true

一个闭包是共享一个数据的,两个闭包则是两个数据,所以不必创建两个闭包(即执行两次外部函数);

闭包实际应用: 点击每个li打印出每个li对应的数字

<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var lists = document.getElementsByTagName("li");
var i = 0,
l = lists.length;
for (; i<l; i++) {
lists[i].onclick = function() {//给每个li绑定了点击事件
console.log(i);//结果都打印5,因为当点击li的时候,i已经变成了5
}
}
</script>

运用闭包来解决问题:

var lists = document.getElementsByTagName("li");
var i = 0,
l = lists.length;
for (; i<l; i++) {
lists[i].onclick = (function(i) {//2.接收参数到外层函数内部,并作为内部数据缓存在内存中
function fn() {
console.log(i);//3.这里的i为外层函数的内部数据,分别为0,1,2,3,4
}
return fn;//4.返回到外部作为点击事件的处理函数,当发生点击事件时,触发这个闭包的执行,那时打印出来的i为外层函数缓存的i值
})(i);//1.自执行函数,将全局变量i依次传入外层函数中
}
 
iife(立即执行函数表达式):
(Function(){})();
!function(){}();
+function(){}();

二、闭包的作用
1.避免全局污染
var $ = function() {};//定义的事全局变量$
//但如果在这之前引入了一个jquery.js,那么jquery的$函数就会被这个全局变量$覆盖
在日常开发中,这种事情会很常见,因为你不能保证其他开发人员会定义怎样的变量,这时一个页面先引入了的js文件中的变量就有可能被后面的js文件中定义的同名的全局变量覆盖;
另外,全局变量生命周期是随着页面的存在而存在的,页面在,变量就会一直占用内存,耗费性能,所以不推荐过多的使用全局变量;
function outer() {//外层函数中的$和attr其实就相当于全局变量,只要闭包存在,这些变量就会一直在
var $ = function() {};
var attr = 10;
return {//这里面的$和attr不会被任何其他的全局变量污染
$ : $,
getAttr : function() {
return attr;
}
}
}
var query = outer();
query.$(“divs”);
console.log(query.getAttr);

以上栗子中将$和attr变量放在一个外层函数里面作为内部数据,而将闭包(指$和getAttr两个方法)返回并赋值给一个全局变量query,当全局变量较多时就能够极大的降低全局污染的概率。

2.缓存数据
1)可用全局变量做缓存———>生命周期太长耗费性能
2)可用cookie\localStorage等做缓存——>io流没有在内存中访问速度快
3)用闭包来做缓存
 举个栗子:求fibonancci数列第n项值 
function fib(n) {
if (n < 1) {
throw new Error("value not increct");
}
if (n == 1 || n == 2) {
return 1;
} else {
return fib(n - 1) + fib(n - 2);
}
}

递归方式求Fib(6)值的过程图解:

                                                    

这里面有太多的重复计算,当要求的数值很大时,性能就会特别低;
用闭包来进行优化:

function createFib() {
var cache = [, 1, 1];//存储计算结果,计算过的结果得以缓存在内存中以便下次直接使用
return function(n) {//闭包这个函数的作用是求得地n项的值,闭包函数可拿到缓存中已经计算过结果的那些项的值;
var res = cache[n];//如果缓存中有这一项,那么直接用
if (!res) {//若没有这一项的值,就要重新计算
res = cache[n] = fib(n - 1) + fib(n - 2);//第n项的值为他的前两项值的和,依旧用递归,这里不同的是,计算过的值不用再重新计算,而是直接拿缓存中的结果,另外本次计算完成后,也要将结果存储到cache中以便下次使用,并且将值赋给res变量用来返回;
}
return res;
};
}
var fib = createFib();//需要递归的是fib这个闭包而不是外层函数createFib,因为闭包才是真正的执行求第n项值的功能函数,而外层函数的作用是用内部数据来做缓存;
console.log(fib(50));//大大提高计算效率

闭包的方式计算fib(6)值的过程如下:

                                                         
 

3、高阶函数
满足以下条件之一就是高阶函数
  • 函数类型作为参数(比如es5中forEach, map,filter,回调函数)
  • 返回函数(闭包)
举个栗子:求员工工资(底薪+提成),普通员工底薪1000,提成每个人都不一样,经理底薪2000,提成每个经理都不同; 

function calSalary(base, ext) {
var base = base * 10 + 20;//每次计算都要重复
return base + ext;
}
var s1 = calSalary(1000, 100);
var s2 = calSalary(1000, 200);
:
:
var p1 = calSalary(2000, 100);
var p2 = calSalary(2000, 300);

像这种有基数的计算,可以用闭包来进行优化:

function calSalary(base) {
var base = base * 10 + 20;//一次计算后作为缓存
return function(ext) {
return base + ext;//这里直接拿缓存中的base,省去了很多重复计算
}
}
var s = calSalary(1000);//拿到闭包
var s1 = s(500);//每次只执行闭包
var s2 = s(1000);
var p = calSalary(2000);//创建另一个闭包,因为共享数据不同
var p1 = p(500);//同样每次只执行闭包
var p2 = p(500);

4、回调函数传参:
栗子:给定时器的回调函数传参,实现div1秒后背景变成红色; 
setTimeout((function(color){//立即执行函数,接收color参数,缓存在内存中
var div = document.getElementById("div");
return function() {//返回一个函数作为延时回调函数,1秒后执行
div.style.background = color;
}
})('red'), 1000)

扩展:
  • setInterval和setTimeout第三个参数可向函数中传参:setTimeout(function(){}, 200, 10);
  • bind方法可实现向回调函数传参
  • setTimeout(function(color){
    var div = document.getElementById("div”);
    div.style.background = color;
    }.bind(null, 'red'), 1000)

javascript闭包以及闭包的作用的更多相关文章

  1. javascript中的闭包

    闭包一直是javascript中的难点,也比较不容易被初学者所掌握,"官方"的解释是:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是 ...

  2. JavaScript葵花宝典之闭包

    闭包,写过JS脚本的人对这个词一定不陌生,都说闭包是JS中最奇幻的一个知识点,  虽然在工作中,项目里经常都会用到~  但是是不是你已经真正的对它足够的了解~~ 又或者是你代码中出现的闭包,并不是你刻 ...

  3. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  4. 【原】理解javascript中的闭包

    闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...

  5. 深入理解javascript原型和闭包(1)——一切都是对象

    “一切都是对象”这句话的重点在于如何去理解“对象”这个概念. ——当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数——typeof().typeo ...

  6. 深入理解javascript原型和闭包(18)——补充:上下文环境和作用域的关系

    本系列用了大量的篇幅讲解了上下文环境和作用域,有些人反映这两个是一回儿事.本文就用一个小例子来说明一下,作用域和上下文环境绝对不是一回事儿. 再说明之前,咱们先用简单的语言来概括一下这两个的区别. 0 ...

  7. javascript中的闭包、模块与模块加载

    一.前言 闭包是基于词法作用域(  和动态作用域对应,词法作用域是由你写代码时,将变量写在哪里来决定的,因此当词法分析器处理代码时,会保持作用)书写代码时所产生的自然结果,甚至不需要为了利用闭包而有意 ...

  8. 深入理解javascript原型和闭包(1)---一切都是对象

    深入理解javascript原型和闭包(1)---一切都是对象 type函数输出的类型,在此列出: function show (x){ console.log(typeof(x));//undefi ...

  9. 全面理解JavaScript中的闭包的含义及用法

    1.什么是闭包 闭包:闭包就是能够读取其他函数内部变量的函数;闭包简单理解成“定义在一个函数内部的函数”. 闭包的形式:即内部函数能够使用它所在级别的外部函数的参数,属性或者内部函数等,并且能在包含它 ...

随机推荐

  1. 使用JDBC连接操作数据库

    JDBC简介 Java数据库连接(Java Database Connectivity,JDBC),是一种用于执行SQL语句的Java API,它由一组用Java编程语言编写的类和接口组成. JDBC ...

  2. java~springboot~ibatis Invalid bound statement (not found)原因

    事实起因 最近在ORM上使用了ibatis,感觉挺繁琐的,没有jpa来的直接,但项目非要用也没有办法,最近在进行开发过程中出现了一个问题Invalid bound statement (not fou ...

  3. Linux最小系统移植之早期打印CONFIG_EARLY_PRINTK

    请先参考先前博文:  Linux最小系统移植之早期打印CONFIG_DEBUG_LL  , 因为eraly_printk其实就是对printch()封装的 一. 必要选项(在上面链接选中的前提下再新增 ...

  4. 【憩园】C#并发编程之异步编程(三)

      写在前面 本篇是异步编程系列的第三篇,本来计划第三篇的内容是介绍异步编程中常用的几个方法,但是前两篇写出来后,身边的朋友总是会有其他问题,所以决定再续写一篇,作为异步编程(一)和异步编程(二)的补 ...

  5. 【转载】解析 java 按值传递还是按引用传递

    原文链接  说明: (1):“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递. (2):在Java里面只有基本类型和 ...

  6. 类和对象,以及 LeetCode 每日一题

    所有类都是引用类型. 1 定义类 类是某一批对象的抽象. 1.1 定义类的语法: [修饰符] class 类名{ 零到多个构造器定义 零到多个成员变量 零到多个方法 } 对于一个类定义而言,可以包含三 ...

  7. HotSpot 虚拟机垃圾回收算法实现

    作为使用范围最广的虚拟机之一HotSpot,必须对垃圾回收算法的执行效率有严格的考量,只有这样才能保证虚拟机高效运行 枚举根节点 从可达性分析中从 GC Roots 节点找引用链这个操作为例,可以作为 ...

  8. Ueditor图片上传功能的配置

    之前的项目中碰到过图片上传功能的配置问题,但是没有记录下来,今天有个朋友突然又问到了我这个问题,当时没想起来之前怎么解决的,后来看了Ueditor的官方文档才回想起来. 官网文档巨多,一般大家遇到问题 ...

  9. Intel MKL FATAL ERROR: Cannot load mkl_intel_thread.dll

    Intel MKL FATAL ERROR: Cannot load mkl_intel_thread.dll 在使用Anaconda创建一个虚拟环境出来,然后安装了scikit-learn.nump ...

  10. 华途软件受控XML转EXCEL

    公司加密系统用的是华途的产品.最近公司高层想要重新梳理公司信息安全管理情况,华途加密系统的梳理和优化是重中之重. 今天公司领导要求IT导出目前系统中所有软件.后缀的受控情况,然后IT吭哧吭哧地把华途软 ...