javascript闭包以及闭包的作用

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

栗子一:

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)    

原文地址:https://www.cnblogs.com/youyang-2018/p/9330871.html

时间: 2024-10-12 07:34:01

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

JavaScript——初理解闭包及作用

js是一个函数级语言,变量的作用域是: 内部可以访问内部,内部可以访问外部,外部不能访问内部. 如果要在外部,访问函数内部的变量,就要用到闭包.闭包就是指访问到了本不该访问的变量. 闭包作用1:实现封装 先来看一个关于封装的例子,在person之外的地方无法访问其内部的变量,而通过提供闭包的形式来访问: 1 var person = function(){ 2 //变量作用域为函数内部,外部无法访问 3 var name = "default"; 4 5 return { 6 getN

javascript面向对象之闭包

javascript面向对象之闭包 学习javascript一段时间了,自己对闭包作出如下总结,如有某点不妥,请君指出,不胜感激! 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量,而在函数外部无法读取函数内的局部变量. 注意点,函数内部声明变量的时候,一定要使用var命令.否则变为全局变量. 简而言之,闭包就是一个受到保护的变量空间. 闭包案例 functon(

JavaScript葵花宝典之闭包

闭包,写过JS脚本的人对这个词一定不陌生,都说闭包是JS中最奇幻的一个知识点,  虽然在工作中,项目里经常都会用到~  但是是不是你已经真正的对它足够的了解~~ 又或者是你代码中出现的闭包,并不是你刻意而为之的行为~ 又或者是因为能达到效果,也知道是闭包,但是原理却不知道?.... 一千个人就有一千个哈姆雷特~  每个人也许都有自己对闭包的理解, 我也不例外, 曾经N次百度过闭包,却没有真正的消化过这个知识点, 也曾工作中无数次运用过闭包, 却不知其所以然~~ 所以,我想把我理解的闭包,自己总结

javascript中的闭包解析

学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去,在上一篇随笔中,和大家分享了JavaScript中独有的类中的继承方式,今天呢,就跟大家分享一下我这几天一直在搞,却还是搞的不是很透彻的闭包问题,对于一个初学者而言,JavaScript中的闭包无疑是一个难点,而且也是我们必须要掌握的一个重点,那么今天我就跟大家分享一下我在学习闭包的时候的感悟以及

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

本系列用了大量的篇幅讲解了上下文环境和作用域,有些人反映这两个是一回儿事.本文就用一个小例子来说明一下,作用域和上下文环境绝对不是一回事儿. 再说明之前,咱们先用简单的语言来概括一下这两个的区别. 00 上下文环境: 可以理解为一个看不见摸不着的对象(有若干个属性),虽然看不见摸不着,但确实实实在在存在的,因为所有的变量都在里面存储着,要不然咱们定义的变量在哪里存? 另外,对于函数来说,上下文环境是在调用时创建的,这个很好理解.拿参数做例子,你不调用函数,我哪儿知道你要给我传什么参数? 01 作

Javascript中的闭包(转载)

前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到闭包以及在后面提到了一些闭包的高级用法.下面大家一起来学习Javascript中的闭包. 谈一谈JavaScript作用域链 当执行一段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建一个作用域又称为执行上下文(Execution Context),在页面加载后会

夺命雷公狗---javascript NO:30 闭包

1.什么是闭包 所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 例1:运行一下代码,得出结论:在全局作用域没有办法直接引用局部变量 <!DOCTYPE html> <html> <head> <meta charset=’utf-8′> <title></title> </head> <body> <script> funct

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

深入理解javascript原型和闭包(1)--一切都是对象 http://www.cnblogs.com/wangfupeng1988/p/3977987.html "一切都是对象"这句话的重点在于如何去理解"对象"这个概念. --当然,也不是所有的都是对象,值类型就不是对象. 首先咱们还是先看看javascript中一个常用的函数--typeof().typeof应该算是咱们的老朋友,还有谁没用过它? typeof函数输出的一共有几种类型,在此列出: funct

JavaScript学习--Item10 闭包(closure)

JavaScript 闭包究竟是什么? 用JavaScript一年多了,闭包总是让人二丈和尚摸不着头脑.陆陆续续接触了一些闭包的知识,也犯过几次因为不理解闭包导致的错误,一年多了资料也看了一些,但还是不是非常明白,最近偶然看了一下 jQuery基础教程 的附录,发现附录A对JavaScript的闭包的介绍简单易懂,于是借花献佛总结一下. 1.定义 闭包:是指有权访问另外一个函数作用域中的变量的函数.创建闭包的常见方式就是在一个函数内部创建另外一个函数. 直接上例子 function a(){ v