zoukankan      html  css  js  c++  java
  • 自调用匿名函数(匿名闭包)解析与调用

    转自:https://www.cnblogs.com/baiyygynui/p/9400511.html

    打开jQuery源码,首先你会看到这样的代码结构:

    (function(window,undefined ){
    //
    })();

    这是一个自调用匿名函数。什么东东呢?在第一个括号内,创建一个匿名函数;第二个括号,立即执行

    为什么要创建这样一个“自调用匿名函数”呢?
    通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,jQuery被应用在成千上万的JavaScript程序中,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。
    接下来看看在 自调用匿名函数 中都实现了什么功能,按照代码顺序排列:

    复制代码
    (function( window, undefined ) {
    // 构造jQuery对象
    var jQuery = function( selector, context ) {
    return new jQuery.fn.init( selector, context, rootjQuery );
    }
    // 工具函数 Utilities
    // 异步队列 Deferred
    // 浏览器测试 Support
    // 数据缓存 Data
    // 队列 queue
    // 属性操作 Attribute
    // 事件处理 Event
    // 选择器 Sizzle
    // DOM遍历
    // DOM操作
    // CSS操作
    // 异步请求 Ajax
    // 动画 FX
    // 坐标和大小
    window.jQuery = window.$ = jQuery;
    })(window);
    复制代码

    匿名函数从语法上叫函数直接量,JavaScript语法需要包围匿名函数的括号,事实上自调用匿名函数有两种写法:

    复制代码
    (function() {
    console.info( this );
    console.info( arguments );
    }( window ) );
    
    (function() {
    console.info( this );
    console.info( arguments );
    })( window );
    复制代码

     经常使用的方法如下格式:

    复制代码
    (function($, owner){
         //登录
        owner.login = function(info,callback){
             //code
         };
    
        //注册
        owner.reg = function(name,callback){
            //code
        };
    
    }(jQuery, window.app = {}));
    
    
    调用:
    app.login(info,function(){});
    app.reg(name,function(){})
    复制代码

    为什么要传入window呢?

    通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化,看看jquery-1.6.1.min.js: (function(a,b){})(window); // window 被优化为 a 
    通过以上的介绍,我们大概了解通过()可以使得一个函数表达式立即执行。

    匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,
    所以 ( function(){…} )() 内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

    复制代码
    (function () {
    // ... 所有的变量和function都在这里声明,并且作用域也只能在这个匿名闭包里
    // ...但是这里的代码依然可以访问外部全局的对象
    }());
    同下面
    (function () {/* 内部代码 */})();
    复制代码

    通俗的讲,()就是用来求值的,因此这个()任何时候都不能为空,因为它是要计算的。函数解析它只会解析到 {}为止,不会解析到 ()的。
    把表达式放在()中会返回表达式的值;
    把函数放在()中会返回函数本身;(function(){}());
    如果()紧跟在函数后面,就是表示在调用函数,即对函数求值:(function(){})();

    (function() {
    //自执行函数中的内部变量,外部是无法访问的
    var name = 'kevin';
    })( window );
    name //undefined,无法获取name的值

    代码在运行过程中,会优先解析 【巳声明的函数】;
    而函数表达式是当执行到它时,才会解析;
    匿名函数是不会单独写的,因此它的执行是需要其它函数的调用,通常看到的匿名函数,都是当作参数被传递的。而立即执行函数它本身就是个匿名函数,

    js代码执行的顺序:
    //巳声明的函数 function test(){}
    //匿名函数 function (){}
    //函数表达式 var test = function(){}
    //立即执行函数 (function(){})();

    立即执行函数配合闭包,在模块化中的应用,其中要明白几个点:
    1、要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明;
    2、立即执行函数可以当作是一个私有作用域,作用域内部可以访问外部的变量,而外部环境是不能访问作用域内部的变量的,因此,立即执行函数是一个封闭的作用域,不会和外部作用域起冲突。
    JQuery使用的就是这种方法,将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。
    3、Module模式,是自执行函数的高级模式,可以非常方便的在各个匿名闭包中以全局对象调用闭包函数。有兴趣可以查看:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html Module 模式为:
      a.创建一个立即调用的匿名函数表达式
      b.return一个变量,其中这个变量里包含你要暴露的东西
      c.返回的这个变量将赋值给window

    复制代码
    (function () {
    var i = 0;
    return {
    get: function () {
    return i;
    },
    set: function (val) {
    i = val;
    },
    increment: function () {
    return ++i;
    }
    };
    } (window));
    
    // window作为一个带有多个属性的全局对象,上面的代码对于属性的体现其实是方法,它可以这样调用:
    window.get(); // 0
    window.set(3);
    window.increment(); // 4
    window.increment(); // 5
    
    window.i; // undefined 因为i不是返回对象的属性
    i; // 引用错误: i 没有定义(因为i只存在于闭包)
    复制代码

    /******上面就是关于自调用匿名函数的解析,那么这样的函数它是怎么被调用的呢?*******/

    /******下面是关于全局变量的调用,也就是匿名闭包函数的调用*******/

    再次搬出Module模式,有兴趣可以查看:http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html
    Module 模式,也就是匿名闭包的创建与调用:
      a.创建一个立即调用的匿名函数表达式
      b.return一个变量,其中这个变量里包含你要暴露的东西
      c.返回的这个变量将赋值给window

    window(或者是任意一个全局对象)作为一个带有多个属性的全局对象,也可以把window当成一个参数,以对象的方式,在其它函数中实现调用。用下面的例子说明:

    复制代码
    (function ($, YAHOO) {
    // 这里,我们的代码就可以使用全局的jQuery对象了,YAHOO也是一样
    $.aa = function(){
    //code
    }
    } (jQuery, YAHOO));
    //调用 jQuery.aa();
    复制代码

    下面是一个标准的Module模式,通过匿名函数的返回值来返回这个全局变量:

    复制代码
    var blogModule = (function () {
    var my = {}, privateName = "博客园";
    
    function privateAddTopic(data) {
    // 这里是内部处理代码
    }
    
    my.Name = privateName;
    my.AddTopic = function (data) {
    privateAddTopic(data);
    };
    
    return my;
    } ());
    //调用 blogModule.my();
    复制代码

    在一些大型项目里,将一个功能分离成多个文件是非常重要的,因为可以多人合作易于开发。再回头看看上面的全局参数导入例子,我们能否把blogModule自身传进去呢?答案是肯定的,我们先将blogModule传进去,添加一个函数属性,然后再返回就达到了我们所说的目的:

    复制代码
    var blogModule = (function (my) {
    my.AddPhoto = function () {
    //添加内部代码 
    };
    return my;
    } (blogModule || {})); 
    或
    (function (my){
    my.AddPhoto = function () {
    //添加内部代码 
    };
    return my;
    })(blogModule || {}));
    //调用 blogModule.AddPhoto();
    复制代码


    那么,多个自执行函数间是怎么调用的呢?

    复制代码
    (function(owner) {
    //第一个匿名闭包
    owner.debug = true;
    //Ajax相关参数配置
    owner.ajax = {
    timeout: 10000,
    type: 'post',
    dataType: 'json',
    };
    
    })(window.C = {}));
    复制代码

    如果第二个函数想调用 全局变量为C中的 对象呢?要怎么写?

    复制代码
    (function($, owner) {
    //这里调用上面全局变量为C 中的对象呢
    if(!C.debug) return false;
    var url = 'aaa.html';
    mui.ajax({
    url: url,
    dataType: C.ajax.dataType,
    type: C.ajax.type,
    });
    })(mui, window.app = {});
    复制代码

    再举个例子,同样的,不同自执行闭包函数间的调用方法:

    复制代码
    (function($, owner) {
    //获取语言闭包
    owner.getLanguage = function() {
    var language = localStorage.getItem(C.state.field.language);
    if(typeof language == "undefined" || language === null || language == '') {
    var currentLang = navigator.language;
    if(!currentLang)
    currentLang = navigator.browserLanguage;
    language = currentLang.toLowerCase();
    language = language.replace(/-/g, '_');
    
    if(language != 'en_us' && language != 'zh_cn')
    language = 'en_us';
    
    localStorage.setItem(C.state.field.language, language);
    }
    复制代码
    复制代码
    //在上面的解析中有说过,Module模式,return 一个变量,这个变量就是要爆露的东西。通过这个函数的全局变量,这个 language 可以在任何地方调用 
    //return一个变量,其中这个变量里包含你要暴露的东西 
    //全局调用 storage.language 
    return language;
    };
    })(mui, window.storage = {}));
    
    (function($, owner) {
    owner.language = {};
    owner.preload = function(settings){
    var defaults = {
    name: 'i18n',
    language: '',
    path: '/',
    cache: true,
    encoding: 'UTF-8',
    autoReplace: true,
    success: null,
    error: null,
    };
    
    settings = $.extend(defaults, settings);
    if(settings.language === null || settings.language == '') {
    //全局调用 storage.language 
    settings.language = storage.getLanguage();
    }
    } 
    })(mui, window.i18n = {});
    复制代码

    所以 匿名闭包的调用规则是这样的,立即执行(最后一个括号) (window),如果把window作为一个参数进行传递,那么就把它以对象的方式,在其它函数中实现全局调用。

    如果有错误,请大家指正,谢谢!

  • 相关阅读:
    好用的开源产品搜集;开源软件,开源系统,开源项目;
    windows10 双系统安装后,grub2 引导修复(亲自实验);grub2 命令行 手动加载内核;fedora 29 系统grub2引导修复;
    C 实战练习题目40
    C 实战练习题目39
    C 实战练习题目38
    C 实战练习题目37 – 排序
    C 实战练习题目36 – 求100之内的素数
    C 实战练习题目35 -字符串反转
    C 实战练习题目34
    C 实战练习题目33 – 质数(素数)判断
  • 原文地址:https://www.cnblogs.com/sharpest/p/11439753.html
Copyright © 2011-2022 走看看