关于闭包的知识总结下:

一、闭包

1、定义

闭包的关键是作用域,概念是:能有读取其他函数内部的函数

使用的场景有很多,最常见的是函数封装的时候,再就是在使用定时器的时候,会经常用到;

//闭包:有参数的加载事件(空参数形式)

(function($){

alert("123");

})(jQuery);

//有参数的加载事件

(function(\(){
alert(\));

})(456);

(function($){

$("div p").click(function(){alert("cssrain!")});

})(jQuery); //一个闭包

这里面的$只是形参,但jquery是全局变量,所以不需要调用该函数就会自动执行,或者分两步

就是转化成正常的函数,先写函数,后调用。

2、实例
(1)
function Test()
{
var str="Test里面的局部变量"; retrun function()
{
alert(str);
} } var func=Test(); func();
//运行后将打印"Test里面的局部变量" 按照变量的生命周期 Test函数运行完毕 str变量就会被销毁 但是在这里它并没有 因为它被匿名函数给捕获了 延长了它的生命周期 这就是闭包
(2)
(function($){
$("div p").click(。。。);
})(jQuery);
就是等于
function tempFunction($){ //创建一个以$为形参的函数
$("div p").click(....);
}
TempFunction(jQuery); //传入实参jQuery执行函数. $(function(cssrain){
cssrain("div p").click(.... );
})(jQuery); //一个闭包 闭包的基本写法:
$(function(){do someting})();
//这个你就理解为定义一个匿名函数并立即执行
带参数的话就这样:
$(function(形参){do someting})(实参);
另外
$(function(){var upc="i am upc"})();
alert(upc);
会提示undefined。
因为闭包后,里面的变量就相当于局部了。 闭包的好处:
不增加额外的全局变量,
执行过程中所有变量都是在匿名函数内部。
3、闭包的优点和缺点
(1)、优点:不增加额外的全局变量,

执行过程中所有变量都是在匿名函数内部。

(2)、缺点:可能造成内存泄漏。
关于js闭包这个总结的很好,地址:https://www.cnblogs.com/darrenji/p/3804993.html

本篇的标题虽然是"jQuery闭包之浅见...",但实际上本篇涉及的多半是javascript"闭包"相关内容,之所以写这个标题,完全是因为自己平常用jQuery写前端习惯了。还有一个原因是:javascript"闭包"很容易造成"内存泄漏", 而jQuery已经自动为我们规避、处理了由"闭包"引发的"内存泄漏"。

javascript的"闭包"是这样定义的:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

定义高度提炼,还是从代码实例来体验"闭包"的来龙去脉吧。本篇主要包括:

  • 从面向对象的角度来理解"闭包"
  • 调用内部函数的几种方式
  • 变量的作用域
  • jQuery中的"闭包"
  • 内存泄漏
从面向对象的角度来理解"闭包"

在面向对象中,我们是这样定义类:

public class SomeClass
{
private string _someVariable; public void SomeMethod()
{
//TODO:
}
}

在javascript中,有一种情况是,一个函数中包含了另外一个内部函数:

function OutFunction()
{
var temp = 0;
function innerFunction(){
}
}

我们把OutFunction称为外部函数,把innerFunction称为内部函数。

这里的外部函数OutFunction相当于一个类。

外部函数变量temp相当于类的私有字段。

内部函数innerFunction相当于类的方法。

就像我们需要实例化类来调用类的实例方法一样,在javasript中,当我们在外部函数之外调用内部函数的时候,我们把此时的内部函数叫做"闭包",相当于面向对象的实例方法。

接下来,从"如何调用内部函数"这个角度,来循序渐进地体会何谓"闭包"。

调用内部函数的几种方式

在外部函数之外直接调用内部函数

 <script type="text/javascript">
function outerFunction() {
console.log('外部函数执行了');
function innerFunction() {
console.log('内部函数执行了');
}
} $(function() {
innerFunction();
});
</script>

结果报错:Uncaught ReferenceError: innerFunction is not defined

以上情况,内部函数的作用域是在外部函数之内,当在外部函数之外调用内部函数的时候,自然就报错。

在外部函数之内调用内部函数:

 <script type="text/javascript">
function outerFunction() {
console.log('外部函数执行了');
function innerFunction() {
console.log('内部函数执行了');
} innerFunction();
} $(function() {
outerFunction();
});
</script>

结果:

外部函数执行了

内部函数执行了

以上情况,内部函数的作用域是在外部函数之内,当在外部函数之内调用内部函数,自然不会报错。

□ 在外部函数之外调用内部函数,把内部函数赋值给一个全局变量

<script type="text/javascript">

        //全局变量
var globalInner; function outerFunction() {
console.log('外部函数执行了');
function innerFunction() {
console.log('内部函数执行了');
} globalInner = innerFunction;
} $(function() {
outerFunction();
console.log('以下是全局变量调用内部方法:');
globalInner();
});
</script>

结果:

外部函数执行了

以下是全局变量调用内部方法:

内部函数执行了

以上情况,全局变量globalInner相当于面向对象的委托,当把内部函数赋值给全局变量,调用委托方法就会调用内部函数。

□ 在外部函数之外调用内部函数,把内部函数赋值给一个变量:

 <script type="text/javascript">

        function outerFunction() {
console.log('外部函数执行了');
function innerFunction() {
console.log('内部函数执行了');
} return innerFunction;
} $(function() {
console.log('先把外部函数赋值给变量');
var temp = outerFunction();
console.log('再执行外部函数变量');
temp();
});
</script>

结果:

先把外部函数赋值给变量

外部函数执行了

再执行外部函数变量

内部函数执行了

以上情况,我们可以看到,内部函数不仅可以赋值给全局变量,还可以赋值给局部变量。

就像面向对象的方法会用到类的字段,内部函数也会用到变量,接下来体验变量的作用域。

变量的作用域

内部函数变量

 <script type="text/javascript">

        function outerFunction() {
function innerFunction() {
var temp = 0;
temp++;
console.log('内部函数的变量temp的值为:' + temp);
}
return innerFunction;
} $(function() {
var out1 = outerFunction();
out1();
out1(); var out2 = outerFunction();
out2();
out2();
});
</script>

结果:

内部函数的变量temp的值为:1

内部函数的变量temp的值为:1

内部函数的变量temp的值为:1

内部函数的变量temp的值为:1

从中我们可以看出内部函数变量temp的生命周期:

→当第一次执行内部函数,JavaScript运行时创建temp变量

→当第二次执行内部函数,JavaScript垃圾回收机制把先前的temp回收,并释放与该temp对应的内存,再创建一个新的内部函数变量temp

.....

所以,每次调用内部函数,内部函数的变量是全新的。也就是说,内部函数的变量与内部函数同生共灭。

□ 全局变量

 <script type="text/javascript">
//全局变量
var temp = 0;
function outerFunction() {
function innerFunction() {
temp++;
console.log('全局变量temp的值为:' + temp);
}
return innerFunction;
} $(function() {
var out1 = outerFunction();
out1();
out1(); var out2 = outerFunction();
out2();
out2();
});
</script>

结果:

全局变量temp的值为:1

全局变量temp的值为:2

全局变量temp的值为:3

全局变量temp的值为:4

可见,全局变量供外部函数和内部函数共享。

□ 外部函数变量

<script type="text/javascript">
function outerFunction() {
var temp = 0;
function innerFunction() {
temp++;
console.log('外部函数变量temp的值为:' + temp);
}
return innerFunction;
} $(function() {
var out1 = outerFunction();
out1();
out1(); var out2 = outerFunction();
out2();
out2();
});
</script>

结果:

外部函数变量temp的值为:1

外部函数变量temp的值为:2

外部函数变量temp的值为:1

外部函数变量temp的值为:2

可见,外部函数的变量与外部函数同生共灭。

以上情况,更接近与"闭包"的原型。有如下几个要素:

1、外部函数

2、外部函数变量

3、内部函数

当我们在外部函数之外调用内部函数的时候,这时的内部函数就叫做"闭包",可以理解为面向对象的实例方法。"闭包"与外部函数变量的"外部环境"是外部函数,他俩与外部函数同生共灭。

一个外部函数中可以有多个内部函数,当调用"闭包"的时候,多个"闭包"共享外部函数变量:

  <script type="text/javascript">
function outerFunction() {
var temp = 0;
function innerFunction1() {
temp++;
console.log('经innerFunction1,外部函数变量temp的值为:' + temp);
} function innerFunction2() {
temp += 2;
console.log('经innerFunction2,外部函数变量temp的值为:' + temp);
} return {'fn1' : innerFunction1, 'fn2' : innerFunction2};
} $(function() {
var out1 = outerFunction();
out1.fn1();
out1.fn2();
out1.fn1(); var out2 = outerFunction();
out2.fn1();
out2.fn2();
out2.fn1();
});
</script>

结果:

经innerFunction1,外部函数变量temp的值为:1

经innerFunction2,外部函数变量temp的值为:3

经innerFunction1,外部函数变量temp的值为:4

经innerFunction1,外部函数变量temp的值为:1

经innerFunction2,外部函数变量temp的值为:3

经innerFunction1,外部函数变量temp的值为:4

jQuery中的"闭包"

"闭包"在jQuery中最常见的应用是,当Document加载完毕再执行jQuery部分:

<script type="text/javascript">
$(document).ready(function() {
var temp = 0; function innerFunction() {
temp++;
console.log(temp);
} innerFunction();
innerFunction();
});
</script>

结果:

1

2

可见,$(document).ready()的参数就是一个匿名外部函数,匿名函数内的函数是内部函数。

把元素的事件也可看作是内部函数:

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<script src="../Scripts/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var counter = 0; $('#btn1').click(function(event) {
counter++;
console.log(counter);
}); $('#btn2').click(function(event) {
counter--;
console.log(counter);
});
});
</script>
</head>
<body>
<input id="btn1" type="button" value="递增"/>
<input id="btn2" type="button" value="递减"/>
</body>

可见,2个input元素的click事件看作是内部函数,共享同一个外部函数变量counter。

在循环体遍历中,把每次遍历的元素事件也可看作是内部函数:

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<script src="../Scripts/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
for (var i = 0; i < 3; i++) {
$('<div>Print ' + i + '</div>').click(function() {
console.log(i);
}).insertBefore('#results');
}
});
</script>
</head>
<body>
<div id="results"></div>
</body>

页面呈现的结果如预期:

Print 0

Print 1

Print 2

可当点击每个div的时候,原本以为控制器台应该显示:0, 1, 2,但实际显示的始终是3,为什么?

--i看作是匿名外部函数的"自由变量",当页面加载完毕后,i就变成了3。div的每次点击看作是内部函数的闭环,而所有的闭环都共享了值为3的这个变量。

我们可以使用jQuery的each()方法来解决以上问题,遍历一个数组,每一次遍历元素值都不同:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<script src="../Scripts/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$.each([0, 1, 2], function(index, value) {
$('<div>Print ' + value + '</div>').click(function() {
console.log(value);
}).insertBefore('#results');
});
});
</script>
</head>
<body>
<div id="results"></div>
</body>
内存泄漏

内存泄漏可以狭隘地理解为:应用程序中变量对应一块内存,由于某些原因(比如代码上的漏洞),始终没有对变量及时实施手动或自动垃圾回收,内存没有得到及时释放,造成内存泄漏。

在JavaScript中,如果对象间的关系比较简单:

1

以上,A有一个属性指向B,B有一个属性指向C,当A被回收的时候,没有依赖B的对象,B随之被自动回收,C也被最终回收。

当对象间的关系比较复杂,比如存在循环引用的时候,如下:

2

以上,A有一个属性指向B, B有一个属性指向C,而C又有一个属性指向B,B和C之间存在循环引用。当A被回收之后,B和C是无法自动被回收的,在JavaScript中应该手动处理回收。

JavaScript闭包有可能会造成内存泄漏:

→内部函数闭包引发的内存泄漏

   $(function() {
var outerVal = {}; function innerFunction() {
console.log(outerVal);
} outerVal.fn = innerFunction;
return innerFunction;
});

以上,outVal是在内存中的一个对象,而内部函数innerFunction同样是内存中的一个对象。对象outVal的属性fn指向内部函数,而内部函数通过console.log(outerVal)引用outVal对象,这样outVal和内部函数存在循环引用的情况,如果不手动处理,就会发生"内存泄漏"。

如果,我们在内部函数中不显式引用outerVal对象变量,会造成"内存泄漏"吗?

$(function() {
var outerVal = {}; function innerFunction() {
console.log('hello');
} outerVal.fn = innerFunction;
return innerFunction;
});

答案是:会的。因为,当内部函数被引用、调用的时候,即使内部函数没有显式引用外部函数的变量,也会隐式引用外部函数变量。

→元素事件引发的内存泄漏

在IE中,如下写法会造成内存泄漏:

$(document).ready(function(){
var button = document.getElementById("btn");
button.onclick = function(){
console.log('hello');
return false;
}
});

而如下JavaScript写法不会造成内存泄漏:

function hello(){
console.log('hello');
return false;
} $(document).ready(function(){
var button = docuemtn.getElementById('btn');
button.onclick = hello;
});

而在jQuery中,类似的写法就不用担心内存泄漏了,因为jQuery为我们做了自动处理来规避内存泄漏。

$(document).ready(function(){
var $button = $('#btn');
$button.click(function(event){
event.preventDefault();
console.log('hello');
});
});
总结

与"闭包"相关的包括:变量的作用域、javascript垃圾回收、内存泄漏,需在实践多体会。

jQuery中的闭包和js中的闭包总结的更多相关文章

  1. MVC中处理Json和JS中处理Json对象

    MVC中处理Json和JS中处理Json对象 ASP.NET MVC 很好的封装了Json,本文介绍MVC中处理Json和JS中处理Json对象,并提供详细的示例代码供参考. MVC中已经很好的封装了 ...

  2. 【转】MVC中处理Json和JS中处理Json对象

    事实上,MVC中已经很好的封装了Json,让我们很方便的进行操作,而不像JS中那么复杂了. MVC中: public JsonResult Test() { JsonResult json = new ...

  3. JavaScript 基础——使用js的三种方式,js中的变量,js中的输出语句,js中的运算符;js中的分支结构

    JavaScript 1.是什么:基于浏览器 基于(面向)对象 事件驱动 脚本语言 2.作用:表单验证,减轻服务器压力 添加野面动画效果 动态更改页面内容 Ajax网络请求 () 3.组成部分:ECM ...

  4. JS中的运算符和JS中的分支结构

    JS中的运算符 1.算术运算(单目运算符) + .-.*. /. %取余.++自增 .--自减 +:两种作用,链接字符串/加法运算.当+两边全为数字时,进行加法运算:当+两边有任意一边为字符串时,起链 ...

  5. bug日记之---------js中调用另一个js中的有ajax的方法, 返回值为undefind

    今天做一个OCR授权的需求, 需要开发一个OCR弹框, 让用户选择是否授权给第三方识别公司(旷世科技)保存和识别用户个人信息, 照片等. 其中用到了在一个js的方法中调用另外一个js的方法, 其中有一 ...

  6. 转:el表达式获取map对象的内容 & js中使用el表达式 & js 中使用jstl 实现 session.removeattribute

    原文链接: ①EL表达式取Map,List值的总结 ②在jsp中使用el表达式通过键获得后台的一个map<Long,String>的值 ③在javascript中使用el表达式(有图有真相 ...

  7. java:JavaScript3(innerHTML,post和get,单选框,多选框,下拉列表值得获取,JS中的数组,JS中的正则)

    1.innerHTML用户登录验证: <!DOCTYPE> <html> <head> <meta charset="UTF-8"> ...

  8. vue cli 中关于vue.config.js中chainWebpack的配置

    Vue CLI  的官方文档上写:调整webpack配置最简单的方式就是在vue.config.js中的configureWebpack选项提供一个对象. Vue CLI 内部的 webpack 配置 ...

  9. JS高级面试题思路(装箱和拆箱、栈和堆、js中sort()方法、.js中Date对象中的getMounth() 需要注意的、开发中编码和解码使用场景有哪些)

    1.装箱和拆箱: 装箱:把基本数据类型转化为对应的引用数据类型的操作: var num = 123 // num var objNum = new Num(123) // object console ...

随机推荐

  1. 在mac上配置cocos2d-x开发环境

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u012282115/article/details/24374539 一.首先下载cocos2d-x ...

  2. Linux中如何添加/删除FTP用户并设置权限

    在linux中添加ftp用户,并设置相应的权限,操作步骤如下: 1.环境:ftp为vsftp.被设置用户名为test.被限制路径为/home/test 2.创建建用户:在root用户下:   user ...

  3. ELK-全文检索技术-lucene

    ELK   :  ELK是ElasticSearch,LogStash以及Kibana三个产品的首字母缩写 一.倒排索引 学习elk,必须先掌握倒排索引思想, 参考文档: https://www.cn ...

  4. lnmp 中的laravel出现白屏的处理办法

    find / |grep fastcgi_params 其实是找fastcgi_params这个文件编辑 如果在知道目录的情况下也可以直接 vi /usr/local/nginx/conf/fastc ...

  5. linux上安装Eclipse

    之所以要在linux上安装Eclipse,是因为一开始我是通过Eclipse+MingW+Samba+GDBserver方式在Windows上远程操作,准备编译调试nginx源代码的,可是在编译调试过 ...

  6. liunx weblogic服务启停脚本

    #!/bin/bash #sh xx.sh start xx项目 例如:sh autoWeblogic.sh start bius #经测试发现weblogic 启动大概需要完全启动成功35秒左右 停 ...

  7. mysql批量替换zencart数据中的反斜杠\

    update products_description set products_description=replace(products_description, "\\'",& ...

  8. jquery 保留两位小数

    jquery 通过 toFixed 保留两位小数 <script> var num = 12.21654; console.log(num.toFixed(2)) </script& ...

  9. 【UOJ#394】[NOI2018] 冒泡排序

    题目链接 题意 求有多少个字典序严格大于给定排列 \(q_i\) 的排列满足其逆序对数(冒泡排序需要交换的次数)达到下限 \(\frac{1}{2}\sum_{i=1}^n |i-p_i|\) Sol ...

  10. hdu 6377 : 度度熊看球赛

    题目链接 题解: 将原问题转换为 对于全部 (2n)! 种情况,每种情况对ans的贡献为 D^k,其中k表示该情况下有k对情侣座位相邻. 预处理好共有 i (1<=i<=N)对情侣时,出现 ...