闭包:内部函数保存到外部
当内部函数被保存到外部时,将会生成闭包。
闭包会导致原有作用域链不释放,造成内存泄漏(内存占用)
闭包的作用
- 实现公有变量: eg:函数累加器
- 可以做缓存(存储结构):eg:eater
- 可以实现封装,属性私有化:eg:new Person();
- 模块化开发,防止污染全局变量
累加器:
function add(){
var count=0;
function demo(){
count++;
console.log(count);
}
return demo;
}
var counter=add();
counter();->输出1
counter();->输出2
缓存:
function eater(){
var food="apple";
var obj={
eat:function (){
if(food!=""){
console.log("i am eating "+ food);
food="";
}else{
console.log("eat emtpy ");
}
}
push:function(myFood){
food = myFood;
}
}
return obj;
}
var eat1= eater();
eat1.eat();->输出apple
eat1.eat();->输出empty
eat1.push('banana');
eat1.eat();->输出banana
私有化:下面例子输入deng.prepareWife是undefind 形成了私有化变量
function Deng(name,wife){
//正常情况 函数里的var对象 函数执行完了就会被销毁,但是在这里因为被this.divorce的函数使用被返回了形成了闭包,所以无法销毁。
var prepareWife="xiaozhang";
this.name=name;
this.wife=wife;
this.divorce=function(){
this.wife=prepareWife;
}
this.changePrepareWife=function(target){
prepareWife=target;
}
this.sayPraprewife=function(){
console.log(prepareWife);
}
}
var deng=new Deng('deng','xiaoliu');
立即执行函数解决闭包作用域问题:
立即执行函数(执行完立即销毁) 针对初始化功能的函数
定义:此类函数没有生命,在一起执行过后释放。适合做初始化工作。
function test(){
var arr=[];
for(var i=0;i<10;i++){
(function (j){
arr[j]=function(){
document.write(j+" ");//输为0,1,2,3,4,5,6,7,8,9
}
}(i));//用立即执行函数(立即执行函数也可以生成自己的作用域) i作为参数传给j j不会随着i改变而改变
}
return arr;
}
var myArr=test();
for(var j=0;j<10;j++){
myArr[j]();
}
<ul>
<li id="myli">a</li>
<li id="myli">a</li>
<li id="myli">a</li>
</ul>
<script type="text/javascript">
function test(){
var liList=document.getElementsByTagName("li");
for (i = 0; i < liList.length; i++) {
(function(j){
liList[j].onclick=function(){
console.log(j); //输出1 2 3 如果不用立即执行函数会报错
}
}(i))
}
}
test();
下面内容引用自:
https://www.cnblogs.com/smyhvae/p/8578716.html
闭包的应用:定义具有特定功能的js模块
-
将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的对象或函数。
-
模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能。
方式一
(1)myModule.js:(定义一个模块,向外暴露多个函数,供外界调用)
function myModule() {
//私有数据
var msg = 'Smyhvae Haha'
//操作私有数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase()); //字符串大写
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase()) //字符串小写
}
//通过【对象字面量】的形式进行包裹,向外暴露多个函数
return {
doSomething1: doSomething,
doOtherthing2: doOtherthing
}
}
上方代码中,外界可以通过doSomething1和doOtherthing2来操作里面的数据,但不让外界看到。
(2)index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_闭包的应用_自定义JS模块</title>
</head>
<body>
<!--
## 闭包的应用 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 【重要】只向外暴露一个包含n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
var module = myModule();
module.doSomething1();
module.doOtherthing2();
</script>
</body>
</html>
方式二
同样是实现方式一种的功能,这里我们采取另外一种方式。
(1)myModule2.js:(是一个立即执行的匿名函数)
(function () {
//私有数据
var msg = 'Smyhvae Haha'
//操作私有数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase())
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase())
}
//外部函数是即使运行的匿名函数,我们可以把两个方法直接传给window对象
window.myModule = {
doSomething1: doSomething,
doOtherthing2: doOtherthing
}
})()
(2)index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>05_闭包的应用_自定义JS模块2</title>
</head>
<body>
<!--
闭包的应用2 : 定义JS模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包信n个方法的对象或函数
* 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<!--引入myModule文件-->
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
myModule.doSomething1()
myModule.doOtherthing2()
</script>
</body>
</html>
上方两个文件中,我们在myModule2.js里直接把两个方法直接传递给window对象了。于是,在index.html中引入这个js文件后,会立即执行里面的匿名函数。在index.html中把myModule直接拿来用即可。
总结:
当然,方式一和方式二对比后,我们更建议采用方式二,因为很方便。
但无论如何,两种方式都采用了闭包。
闭包的缺点及解决
缺点:函数执行完后, 函数内的局部变量没有释放,占用内存时间会变长,容易造成内存泄露。
解决:能不用闭包就不用,及时释放。比如:
f = null; // 让内部函数成为垃圾对象 -->回收闭包
总而言之,你需要它,就是优点;你不需要它,就成了缺点。
内存泄漏内存溢出
内存泄漏
内存泄漏:占用的内存没有及时释放。内存泄露积累多了就容易导致内存溢出。
常见的内存泄露:
-
意外的全局变量
-
没有及时清理的计时器或回调函数
-
闭包
情况1举例:
// 意外的全局变量
function fn() {
a = new Array(10000000);
console.log(a);
}
fn();
情况2举例:
// 没有及时清理的计时器或回调函数
var intervalId = setInterval(function () { //启动循环定时器后不清理
console.log('----')
}, 1000)
// clearInterval(intervalId); //清理定时器
情况3举例:
<script type="text/javascript">
function fn1() {
var arr = new Array[100000]; //这个数组占用了很大的内存空间
function fn2() {
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
f = null //让内部函数成为垃圾对象-->回收闭包
</script>
内存溢出(一种程序运行出现的错误)
内存溢出:当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误。
//内存溢出
var obj = {}
for (var i = 0; i < 10000; i++) {
obj[i] = new Array(10000000); //把所有的数组内容都放到obj里保存,导致obj占用了很大的内存空间
console.log('-----')
}
网友评论