美文网首页个人收藏JavaScript
JavaScript - 闭包 - 函数回调 - 即时函数

JavaScript - 闭包 - 函数回调 - 即时函数

作者: 西巴撸 | 来源:发表于2017-03-29 18:47 被阅读310次

<h4>本小节主要讲解闭包,是特别重要的一块内容.希望大家能够用心学习!</h4>

作用域链的补充内容 ----> 搜索规则

如果有忘记的人,翻一下上一篇博客

注意点:如果在内层作用域中声明了和外层作用域中同名的变量,那么这个变量是不会把外层的同名变量覆盖的

搜索原则:

  • 在作用域中如果访问(读取|设置)某个变量,先在当前作用域中搜索,如果找到那么就直接使用
  • 如果没有找到,那么就向上一级作用域中继续搜索,找到则使用,没有找到就重复上面的过程
  • 直到0级作用域链

闭包

闭 : 关闭,封闭的意思,对外界不开方的

包 : 包装起来

闭包技术

  • 作用域规则 : 内层的作用域可以访问外层的的作用域,但反过来不行
  • 有的时候,我们确实需要访问一个封闭空间里的数据源(外层作用域 ----> 内层作用域)
  • 就是提供一种间接访问封闭空间中私有数据的方法

访问数据

  • 直接返回 return
  • 闭包 对直接返回的数据进行包装(函数)
  • 示例代码 :
<script>
    function f1(){
        var num = 10;
        return num;
    }

    //num
    var a = f1();
    var b = f1();

    console.log(a);
    console.log(b);

    //一次性获得数据
    //1 每次调用函数获得的数据并不是同一份数据
    //2 只能获取值,但是却不能修改值
</script>
  • 示例代码 : 闭包一般写法
<script>
    function f1(){
        var num = 10;
        return function(param){
            num = param;    //修改函数内部变量的值
            return num;
        };
    }
</script>
  • 示例代码 : 访问获取数据
<script>
    function f1(){
        var num = 10;
        var name = "张学友";

        return function(){
            return [num,name];
        };
    }

    var func = f1();
    console.log(func()[0]);
    console.log(func()[1]);
</script>
  • 示例代码 : 闭包基本写法
<script>
    function f1(){
        var num = 10;
        var name = "xxx";
        return {
            getNum:function(){
                return num;
            },
            getName:function(){
                return name;
            }
        }
    }

    var func = f1();
    console.log(func.getName());
    console.log(func.getNum());
</script>

闭包读取数据和设置数据

  • 示例代码 : 获取数据
<script>
    function foo(){
        var name = "卡罗";
        var age = 18;
        return {
            getName:function(nameValue){
                //判断
                if (nameValue != undefined)
                {
                    name = nameValue;
                }
                return name;
            },
            getAge:function(ageValue){
                age = ageValue;
                return age;
            }
        }
    }

    var func = foo();

    console.log(func.getName());     //
    console.log(func.getName("卡罗尔"));
    console.log(func.getName());
</script>
  • 示例代码 :设置数据
<script>
    function foo(){
        var name = "卡罗";
        var age = 18;
        return {
            getName:function(){
                return name;
            },
            getAge:function(){
                return age;
            },
            setName:function(nameValue){
                name = nameValue;
            },
            setAge:function(ageValue) {
                age = ageValue;
            }
        }
    }

    var func = foo();
    console.log(func.getName());
    func.setName("卡米尔");
    console.log(func.getName());

    func.setAge(20);
    console.log(func.getAge());
</script>

闭包的作用

提供了一种间接访问函数封闭空间中数据的方法

  • 访问函数内部的变量只能通过指定的借口
  • 对变量的修改设置操作会更加安全,可以在设置之前对数据进行校验
  • **延长变量的生命周期
    **
<script>
    function f1(){
        var age = 20;
        var author= "今何在";

        return {
            getAge:function(){
                return age;
            },
            setAge:function(ageValue){

                //校验处理
                if (ageValue <0)
                {
                    ageValue = 0;
                }

                //其他的处理(异常检测)
                age = ageValue;
            }
        }
    }

    var func = f1();
    func.setAge(22);
    console.log(func.getAge());

    //设置年龄  -3 (因为人的年龄不能是负值 所以得进行安全校验)
    func.setAge(-3);
    console.log(func.getAge());
</script>

setTimeout和闭包的执行

setTimeout(); 只调用一次函数(延迟函数) 一次是隔两秒(至少)

每隔一定的时候就调用一次

  • **参数设置 : **
    第一个参数:函数回调(时间到了之后执行的代码)
    第二个参数:间隔时间(毫秒) 1秒 = 1000毫秒

setTimeout的问题

  • 示例代码 :
<script>
    for (var i = 0; i < 100; i++) {

        setTimeout(function () {
            console.log(i);
        },0);

        console.log("----");
    }
</script>

解决setTimeout的问题

  • 示例代码 :
<script>
    for (var i = 0; i < 10; i++) {

        setTimeout((function (j) {

            return function () {
                console.log(j);
            }
        })(i),10);

        console.log("+++++++++");
    }
</script>

div事件和闭包

  • **示例代码 : **
<div>我是第1个div标签</div>
<div>我是第2个div标签</div>
<div>我是第3个div标签</div>
<div>我是第4个div标签</div>
<div>我是第5个div标签</div>
<div>我是第6个div标签</div>
<script>
var divs  = document.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++) {
        divs[i].onclick = function(){
            alert("我是第" + i + "标签");
        }
    }
    // 每次点击得到的都是第六个标签
</script>
  • **用闭包解决问题 : **
<script>
    var divs  = document.getElementsByTagName("div");
    for (var i = 0; i < divs.length; i++) {
       (function(a){
           divs[a].onclick = function(){
               alert("我是第" + a + "标签");
           }
       })(i)
   }
</script>

函数的特性

  • 函数本身也是对象(第一型的对象) 凡是可以使用对象的地方都可以使用函数
  • 函数可以创建作用域
  • 函数是可以被调用的

函数的约定和name值

  • 函数声明

  • 函数表达式

  • new 构造函数

  • 示例代码 :

<script>

    //function funName(参数1,参数2){函数体}   //函数声明

    //函数表达式(匿名)
    var add = function(a,b){
        console.log(a + b);
    }

    //函数表达式(命名)
    var sum = function name(a,b){
        console.log(a + b);
    }

    //sum.length // 形参的个数
    console.log(add.name);   //add 跟浏览器的实现有关系(在火狐中name的值为空)
    console.log(sum.name);   //name

    //03构造函数创建对象
    new Function(参数1,参数2,参数3)
</script>

函数的回调(作为参数传递)

函数是第一型对象(因此可以使用对象的地方都可以使用函数)

  • 函数可以作为函数的参数
  • 函数可以作为函数的返回值

回调函数(回调),当我们把某个函数作为参数传递给另一个函数的时候,这个函数就称为回调函数。

回调函数的基本模式

  • 示例代码 :
<script>
    function func(callBack) {

        //处理其他的操作
        callBack();     //调用回调函数
    }

    function demo() {
        console.log("这是一个回调函数");
    }

    func(demo); //注意调用函数的时候,参数是回调函数的引用(不要加括号);
</script>

回调函数(关于this丢失的问题)

  • 示例代码 :
<script>
    //如果回调函数是某个对象的方法,而该对象方法中使用了this指针
    //那么该方法作为回调函数来使用的时候,需要注意this丢失的问题

    //01 提供一个对象,该对象中永远有showName方法
    var obj = {
        name:"xxx名字",
        age:30,
        showName:function () {
            console.log(this.name);
        },
        showAge:function () {
            console.log(this.age);
        }
    }


    //02 提供一个函数,该函数接受一个参数(函数引用)
    function demo(callBack) {
        callBack();
    }

    demo(obj.showName); //注意:不要带上括号 打印结果为空(window.name)
    demo(obj.showAge);  //undefined
</script>

回调函数(解决this丢失的问题)

  • 示例代码 :
<script>
    //01 提供一个对象,该对象中永远showName方法
    var obj = {
        name:"默认的名字",
        age:30,
        showName:function () {
            console.log(this.name);
        },
        showAge:function () {
            console.log(this.age);
        }
    };

    //02 提供一个函数,该函数接受一个参数(函数引用)
    function demo(callBack,callBack_obj) {
        if (typeof callBack == 'function')
        {
            callBack.call(callBack_obj);
            console.log("++++");
        }
    }

    demo(obj.showName,obj); //注意:不要带上括号
    demo(obj.showAge,obj);  //undefined

</script>

回调函数(兼容字符串方式)

  • 示例代码 :
<script>
    //01 提供一个对象,该对象中永远showName方法
    var obj = {
        name:"默认的名字",
        age:30,
        showName:function () {
            console.log(this.name);
        },
        showAge:function () {
            console.log(this.age);
        }
    };

    //02 提供一个函数,该函数接受一个参数(函数引用)
    function demo(callBack,callBack_obj) {

        //处理第一个参数传递对象方法字符串的形式
        if(typeof callBack == 'string')
        {
            callBack = callBack_obj[callBack];
        }

        if (typeof callBack == 'function')
        {
            callBack.call(callBack_obj);
        }


    }

    //demo(obj.showName,obj);
    //demo(obj.showAge,obj);

    //传递字符串和对象来进行调用
    demo("showName",obj);
</script>

函数作为返回值( 计算器)

  • 示例代码 :
<script>

    //使用闭包实现一个计数器(在该示例中setup函数的返回值为一个函数)
    //通过调用返回值(一个函数),可以操作setup函数中的变量

    var setup = function () {
        var count = 0;
        return function () {
            return count ++;
        }
    }

    var next = setup();
    console.log(next());    //0
    console.log(next());    //1
    console.log(next());    //2
</script>

自定义函数( 惰性函数 )

惰性函数

  • 某个函数直到第一次使用的时候才被正确的定义,并且其具有向后惰性,执行更少的工作。

应用场景

  • 函数有一些初始化的准备工作要做,且只需要执行一次的情况。

特点

  • 能够更新自己(函数)的实现。

缺点:

  • 当重新定义自身的时候,已经添加到原始函数的任何属性都会丢失。
  • 如果函数被赋值给了其他的变量或者是对象方法,那么在使用变量或者是对象方法调用时仍然会执行旧的函数体。
  • 示例代码 :
<script>
    var demo = function foo() {
        console.log("foo!");
        foo = function () {
            console.log("new foo!");
        }
    }

    //函数的调用
    //foo();  //foo!
    //foo();  //new foo!

    //总结:该函数的实现特点在于能够更新自身的实现。
    demo(); //foo!
    demo(); //foo!
</script>

惰性定义函数存在的问题

  • 添加属性
  • 把函数赋值给新的变量
  • 以对象的方法调用函数
  • 示例代码 :
<script>
    //01 声明函数foo
    function foo() {
        console.log("foo!");
        foo = function () {
            console.log("foo! foo!");
        }
    }

    //02 为foo函数对象添加属性
    foo.description = "foo函数的描述信息";

    //03 把foo函数赋值给其他的变量
    var func = foo;

    //04 把foo函数赋值给对象中的方法
    var obj = {
        showFoo:foo
    }

    //05 验证并演示输出
    func(); //foo!
    func(); //foo!
    console.log(func.description);  //foo函数的描述信息
    //总结:01 如果把函数赋值给其他的变量,那么在以其他变量的方式调用时不会更新自身,还是执行旧的函数体

    obj.showFoo();  //foo!
    obj.showFoo();  //foo!
    console.log(obj.showFoo.description);   //foo函数的描述信息
    //总结:02 如果把函数赋值给对象的方法,那么在以对象方法形式调用时不会更新自身,还是会执行旧的函数体。

    foo();  //已经更新过foo函数 foo! foo!
    foo();  //已经更新过foo函数 foo! foo!
    console.log(foo.description);   //undefined
</script>

即时函数

在函数定义之后立即执行该函数。

即时函数模式的组成:

  • 使用函数表达式来定义函数(匿名函数,注意不能使用函数声明方式)
  • 在函数表达式末尾添加一组(),表示立即执行当前函数。
  • 将整个函数包装在()中,有两种方式

即时函数的作用

  • 用来将所有的代码包装到当前的作用域中,并且不会将任何的变量泄露到全局作用域中。
  • js中没有代码块作用域,而函数是js中唯一可以创建作用域的。
  • 即时函数就是利用了函数创建作用域这一点,来实现对一些需要封装且不允许外部访问的操作。

即时函数的优点

  • 封装在一起执行的代码,且不会产生全局变量,在即时函数内部定义的所有变量都仅仅只是该函数的局部变量,不会造成全局变量污染问题。
  • 具有更好的封装性,外部无法访问到该函数内部的数据。
  • 示例代码 : 即时函数两种写法
<script>
    //第一种写法
    (function () {
       console.log("即时函数的第一种写法");
    }());

    //第二种写法
    (function () {
        console.log("即时函数的第二种写法");
    })();
</script>
  • 示例代码 : 即时函数可以接受参数并提供返回值
<script>
    //01 接受参数
    (function (str) {
        console.log(str);           //hello
    })("hello");

    //02 提供返回值并赋值给新的变量
    var foo = (function () {
        return 2 + 1;
    })();

    console.log(foo);           //3
</script>

即时函数和闭包

  • 相同点:他们都是函数的一种特殊形态,并且可以共存。而且闭包配合即时函数“效果更佳”。

  • 不同点:即时函数是定义一个函数,并立即执行。它只能被使用一次,相当于“阅后即焚”。闭包是指一个函数与它捕获的外部变量的合体,按照MDN的说法,闭包就像一个对象---一个具有一个方法(行为)和一个或多个私有字段(状态)的对象。从这个角度看,闭包是符合面向对象的封装思想的。

即时对象初始化

结构特征

  • 提供一个对象,在该对象内部提供一个init初始化方法,使用()把对象包装起来(让字面量变成表达式)
  • 然后随即调用init方法,完成初始化操作。

基本结构

({}).init();

模式优点

  • 在执行一次性的初始化任务时保护全局的命名空间。
  • 示例代码 :
<script>
    ({
        name:"xxx",
        age:23,
        getDescript:function () {
            console.log("名字:" + this.name + "年龄:" + this.age);
        },

        //注意:在对象中访问对象的属性和方法都需要使用this.前缀
        init:function () {
            this.getDescript();
            //其他的初始化处理
        }
    }).init();

相关文章

  • JavaScript - 闭包 - 函数回调 - 即时函数

    本小节主要讲解闭包,是特别重要的一块内容.希望大家能够用心学习!

    作用域链的补充内容 ---->...

  • JS第三天

    一、函数高级 1、函数回调 2、闭包 二、循环绑定 1、使用闭包解决局部变量生命周期 2、使用闭包解决变量污染问题...

  • 闭包、定时器

    问题 一、什么是闭包? 有什么作用? 闭包闭包就是能够读取其他函数内部变量的函数。在javascript中,只有函...

  • Kotlin-简约之美-进阶篇(十二):闭包

    @[toc] 什么是闭包 闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函...

  • 简单聊一下js闭包

    js闭包,阮老师说:‘闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函...

  • JavaScript中闭包的本质

    闭包是JavaScript开发人员常常谈论的问题,大家普遍对闭包的认知如下: 模糊的认知:闭包是定义在函数内部的函...

  • 023 JS高级

    JS高级 一、函数高级 1、函数回调 2、闭包 二、循环绑定 三、面向对象JS 1、属性与方法 2、类字典结构使用...

  • 高级函数(闭包,递归,深浅拷贝)

    闭包函数(closures) 闭包函数也叫 匿名函数闭包函数( closures ) 允许创建一个没有指定名称的函...

  • 闭包的缺点

    我现在的理解就是 闭包就是一个函数,这个函数能访问其他函数的作用域。 因为JavaScript的函数比较特殊, 函...

  • Swift 逃逸闭包@escaping

    非逃逸闭包 非逃逸闭包的生命周期: 把闭包作为参数传递给函数。 函数中运行该闭包。 退出函数。非逃逸闭包被限制在函...

网友评论

    本文标题:JavaScript - 闭包 - 函数回调 - 即时函数

    本文链接:https://www.haomeiwen.com/subject/bfvrgttx.html