深刻理解javascript系列(九):应用闭包

理论是自信的基础,结合理论的实践才能让咱们走的更远。javascript

前两个系列,我记录了闭包的学习,如何利用闭包解决实际问题了?其实,不少东西你我都知道,不是一蹴而就的,不是你今天学了就会了,还须要屡次练习,反复练习。相信终究一天你我会运用自如。java

下面就经过3个小例子,来运用闭包解决实际问题吧。面试

9.1  循环、setTimeout与闭包

在面试题中经常会遇到一个与循环、闭包有关的问题,以下:设计模式

//利用闭包的知识修改这段代码,让代码的执行结果为隔秒输出1,2,3,4,5
for (var i = 1; i<=5; i++) {
    setTimeout(timer=()=>{
        console.log(i)
    },i*1000)
}复制代码

首先来分析一下若是直接运行这个例子会输出什么结果。(涉及到定时器线程的相关知识还须要自行学习)bash

前面咱们已经知道,for循环的大括号并不会造成本身的做用域,所以这个时候确定是没有闭包产生的,而i值做为全局的一个变量,会随着循环的过程递增。所以循环结束以后,i变成了6。微信

而每个循环中,setTimerout的第二个参数访问的都是当前的i值,所以第二个i值分别是1,2,3,4,5。而第一个参数timer函数中虽然访问的是同一个i值,可是因为延迟的缘由,当timer函数被setTimerout运行时,循环已经结束,即i已经变为6了。闭包

所以这段代码执行结果是隔秒输出6.框架

而咱们想要的是隔秒输出1,2,3,4,5,所以须要借助闭包的特性,将每个i值都用一个闭包保护起来。每一轮循环,都把当前的i值保存在一个闭包中,当setTimerout中定义的操做执行时,访问对应闭包便可。模块化

这个时候咱们回想一下闭包造成的条件,简单来讲,就是一个函数中定义了一个子函数,子函数内部访问了函数的变量对象。所以咱们只须要建立一个这样的环境便可。函数

for (var i = 1; i<=5; i++) {
    (function(i) {
        setTimeout(timer=()=>{        console.log(i)
    },i*1000)    })(i)
}复制代码

定义一个匿名函数,称做A,并将其看成闭包环境。而timer函数则做为A的内部函数,当A执行时,只需访问A的变量对象便可。所以将i值做为参数传入,这样也就知足了闭包的条件,并将i值保存在了A中。

一样的道理也能够在timer函数里作文章。还有更多方法,这里就不一一赘述。

9.2  单例模式与闭包

好,我继续个人笔记。

在javascript中有许多解决特定问题的编码思惟(设计模式,Alloy Team出的那本我的以为很棒),例如工厂模式、发布订阅模式、装饰者模式、单例模式等。其中,单例模式是早期开发最经常使用的模式之一,而它的实现,与闭包戚戚相关。

所谓单例模式,就是只有一个实例。

1.  最简单的单例模式

对象字面量的方法就是最简单的单例模式,咱们能够将属性与方法依次放在字面量里。

var person = {
    name: 'pan',
    age: '18',
    getName: function() {
        return this.name
    },
    getAge: function() {
        return this.age
    }
}复制代码

可是这样的单例模式有一个严重的问题,即它的属性能够被外不修改。所以在许多场景中,这样的写法并不符合咱们的需求,咱们更指望对象可以有本身的私有方法与属性。

PS:我的以为不论学习什么,都应该回顾它的历史,以便更利于当下的学习。理论性知识发展到目前,不是偶然,总有那么一个阶段性的过渡。司徒正美在他的做品《javascript框架设计》前言中就说到了这么一个事“当初,我阅读jQuery源码,最初看的是1.4.3版本,看得一头雾水,一气之下,从最初的1.0版本开始看。看完全部版本,了解其迭代过程,才明白”。

2.  有私有方法/属性的单例模式

经过前面所学的知识咱们很容易就能想到,想要一个对象拥有本身私有的方法属性,那么只须要建立一个单独的做用域将对象与外界隔离起来就好了。这里咱们借助匿名函数自执行的方式便可。

var person = (function() {
     var name = 'pan';
     var age = 18;
 
     return{
        function getName() {        return name
     };
     function getAge() {
         return age
     }    }   
})();
person.getName();复制代码

私有变量的好处在于,外界对于私有变量可以进行什么样的操做是能够控制的。咱们能够提供一个getName方法让外界能够访问名字,也能够额外提供一个setName方法,来修改它的名字。对外提供什么样的能力,彻底由咱们本身决定。

如今咱们正走在模块化的道路上....

3.  调用时才初始化的单例模式

有的时候(使用频次较少)咱们但愿本身的实例仅仅只是在调用的时候才被初始化,而不像上面两个例子那样,即便没有调用person,person的实例在函数自执行的时候就返回了。

那么咱们就须要在上面例子的基础上作一点小小的改动了。

var person = (function(){
    //定义一个变量,用来保存实例
    var instance = null;
    var name = 'pan';
    var age = 18;

    //初始化方法
    function init() {
        return{
            getName: function() { return name;},
            getAge: function() { return age;}
        }
    }

    return {
        getInstance: function() {
                if(!instance){
                    instance = init()
                }
                return instance
            }
    }
})();

//只在使用时获取实例
var p1 = person.getInstance();复制代码

在这个例子中,咱们对匿名函数中定义了一个instance变量用来保存实例。在getInstance方法中判断了是否对他进行从新赋值。因为这个判断的存在,所以变量instance仅仅只在第一次调用getInstance方法时赋值了。

在写一个系列,我将持续更新我闭包相关应用。感谢阳波大神。

这些都是我以往的学习笔记。若是您看到此笔记,但愿您能指出个人错误。有这么一个群,里面的小伙伴互相监督,坚持天天输出本身的学习心得,不输出就出局。但愿您能加入,咱们一块儿终身学习。欢迎添加个人我的微信号:Pan1005919589

相关文章
相关标签/搜索