《JavaScript设计模式与开发》笔记 4.闭包

Posted SmarTom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《JavaScript设计模式与开发》笔记 4.闭包相关的知识,希望对你有一定的参考价值。

  •  1.变量的作用域
  •  2.变量的生存周期
  •  3.闭包更多作用
    • 1.封装变量
    • 2.延续局部变量寿命
  •  4.闭包和面向对象设计
  •  5.闭包和内存管理

1.变量的作用域

var func = function(){
    var a = 1;
}
func();
console.log(a); //输出undefined
var a = 1;
fun1 = function(){
    var b = 2;
    fun2 = function(){
        var c = 3;
        console.log(b);   //输出 2
        console.log(a);   //输出 1
    }
    fun2();
    console.log(c);   //输出undefined
}
fun1();

 


2.变量的生存周期

var func = function(){
    var a = 1;
    return function(){
        a++;
        console.log(a);
    }
}
var f = func();
f();    // 输出1
f();    // 输出2
f();    // 输出3
f();    // 输出4

 

当退出函数后,局部变量a并没有消失,而是似乎一致在某个地方存活着。这是因为当执行var f = func();时,f返回了一个匿名函数的引用,它可以访问到func()被调用时产生的环境,而局部变量a一致处于这个环境里。既然局部变量所在的环境还能被外界,这个局部变量就有了不被销毁的理由。这里产生了一个闭包结构,局部变量的声明看起来被延续了。

既然f返回了一个匿名的函数引用,那么下面的也符合闭包

var func = (function(){
    var a = 1;
    return function(){
        a++;
        console.log(a);
    }
})();
f();    // 输出1
f();    // 输出2
f();    // 输出3
f();    // 输出4

 

再次变种为引用对象的方式

var func = {
    a :0,
    call:function(){
        this.a++;
        console.log(this.a);
    }
}
func.call();   //输出1
func.call();   //输出2
func.call();   //输出3

 


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
    var nodes = document.getElementsByTagName(‘div‘);
    for(var i =0;i<nodes.length;i++){
        nodes[i].onclick=function(){
            alert(i);
        }
    }
</script>
</body>
</html>

 

测试这段代码就会发现,无论点击哪个div,最后弹出的结构都是5,。这是因为div节点onclick事件是被异步出发的,当事件出发的时候,for循环早已结束,此时变量i的值已经是5,所以在div的onclick事件函数中顺着作用域链从内到外查找变量i时,查找到的值总是5。

解决方法是在闭包作用的帮助下,把每次循环的i值都封装起来。(就是把每个点击事件都独立放在内存里面。)

var nodes = document.getElementsByTagName(‘div‘);
    for(var i =0;i<nodes.length;i++){
        (function(i){
            nodes[i].onclick=function(){
                alert(i);
            }
        })(i);
    }

 


3.闭包更多作用

1.封装变量

闭包可以帮组我们封装私有变量”

var mult = function(){
    var a = 1;
    for(var i =0;i <arguments.length;i++){
        a = a*arguments[i];
    }
    return a;
}
console.log(mult(1,2,5));

 

mult函数接受一些number类型的函数,并返回这些参数的乘积。现在我们觉得对于这些那些参数来说,每次都进行计算是一种浪费,我们可以加入缓存机制来提高这个函数的性能。

var cache={};
var mult = function(){
    var args = Array.prototype.join.call(arguments,‘,‘);  //将参数对象变成一个字符串 args 1,2,3
    if(cache[args]){
        console.log(‘执行缓存‘);
        return cache[args];
    }
    var a = 1;
    for(var i=0;i<arguments.length;i++){
        a = a*arguments[i];
    }
    return cache[args] = a;
}
console.log(mult(1,2,3));   //进行计算
console.log(mult(1,2,3));   //执行缓存,不用计算

 

继续封装减少页面全局变量

var mult = (function(){
    var cache={};       //私有变量
    return function(){
        var args = Array.prototype.join.call(arguments,‘,‘);
        if(cache[args]){
            return cache[args];
        }
        var a = 1;
        for(var i=0;i<arguments.length;i++){
            a = a*arguments[i];
        }
        return cache[args] = a;
    }
})()
console.log(mult(1,2,3));   //进行计算
console.log(mult(1,2,3));   //执行缓存,不用计算

 

提炼代码,独立模块

var mult = (function(){
    var cache={};       //私有变量
    var cala = function(){  
        var a = 1;
        for(var i=0;i<arguments.length;i++){
            a = a*arguments[i];
        }
        return a;
    }
    return function(){
        var args = Array.prototype.join.call(arguments,‘,‘);
        if(cache[args]){
            return cache[args];
        }
        return cache[args] = cala.apply(null,arguments);
    }
})();
console.log(mult(1,2,3));   //进行计算
console.log(mult(1,2,3));   //执行缓存,不用计算

 

2.延续局部变量的寿命
var report = function(src){
    var img = new Image();
    img.src = src;
}

 

利用闭包来延续img的使用

var report = (function(){
    var imgs = [];   //img就留在了内存里面
    return function(src){
        var img = new Image();
        imgs.push(img);
        img.src = src;
    }
})();

 


4.闭包和面向对象

闭包:

var extent = function(){
    var value =0;
    return {
        call:function(){
            value++;
            console.log(value);
        }
    }
}
var extent = extent();
extent.call();   //输出1
extent.call();   //输出2
extent.call();   //输出3

 

如果换成面向对象的写法就是

var extent = {
    value :0,
    call:function(){
        this.value++;
        console.log(this.value);
    }
}
extent.call();   //输出1
extent.call();   //输出2
extent.call();   //输出3

 

5.闭包和内存管理

闭包是一个非常强大的特性,但人民对其他诸多误解,一种耸人听闻的说法就是闭包会造成内存泄漏。如果在将来需要回收这些变量,我们可以手动吧这些变量设置为null。

以上是关于《JavaScript设计模式与开发》笔记 4.闭包的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript设计模式与开发实践---读书笔记 策略模式

《JavaScript设计模式与开发》笔记 2.this指针

javascript设计模式与开发实践阅读笔记—— this,闭包与高阶函数

javascript设计模式与开发实践阅读笔记——策略模式

《JavaScript设计模式与开发》笔记 1.面向对象的JavaScript

JavaScript设计模式与开发实践---读书笔记