简述作用域还有闭包延伸至模块化

发布时间:2019-08-20 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了简述作用域还有闭包延伸至模块化脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

首先,变量对于一个程序来说是一个很重要的角色, 那么问题来了 这些变量存在哪里,程序用到的时候如何找到变量呢?
所以需要一套规则来存储变量方便之后再找到 这套规则就成为作用域.


js是一门编译语言,对于js来说 大部分情况下编译发生在代码执行前的几微妙的时间内,
对于参与到一个程序 : var a = 2; 处理的时候,

  • 引擎 : 从头到尾负责整个js程序的编译和执行过程
  • 编译器: 负责的是语法分析和代码生成
  • 作用域: 负责收集并维护所有声明的变量组成的一系列查询,并实施一套非常严格的规则,最后确定当前执行的代码对于这些变量的访问权限

对于 var a = 2; 来说

  • 首先编译器会先询问作用域是否已经有一个该名称a的变量存在同一个作用域的集合中 ,如果有了 编译器就会忽略否则 则会要求作用域在当前作用域集合中声明一个新的变量 名称为a;
  • 然后编译器生成代码来进行a=2 的复赋值操作, 到了引擎工作的时候了 首先会先在作用域中查找a这个变量,如果有,引擎就会对变量a进行LHS查询(LHS查询是试图找到变量本身然后进行赋值操作(等号左边left) | RHS查询是指非等号左边的查询 即要取到某个变量的指),对a进行赋值操作.如果找不到这个变量就会抛出异常了.

(

  • 这里补充一下引擎的LHS查询和RHS查询 : 例如 VAR b=a; return a+b 这里的 LHS查询有: b=...
    RHS查询有 =a; a...; b...; 对于变量没有声明的情况下,两个查询的行为是不一样的, 对于 b=a
    时,对b=进行LHS查询 会一直向上作用域查找该变量 如果在最顶层还没有找到 那么就会在最顶层创建一个b变量(非严格模式下) ,
    而对于=a进行RHS查询时,如果在所有作用域中找不到该变量就会抛出ReferenceError异常.

)

了解了js工作机制之后,那么对于 "声明提升" 就会恍然大悟了,
例如:

    foo();
    function foo(){
        console.LOG(a)  //undefined
        var a= 2; 
    }
    //首先编译器先定义声明foo 和 作用域中的a  然后才会到引擎执行代码进行赋值操作,所以才有了以上的输出, 这种效果即是"声明提升"
    

意味着无论作用域中的声明出现在什么地方,都会在代码执行前(编译阶段)进行首先处理,想象成所有的声明都会被移动到各自作用域的顶端,===>即成为 我们所说的提升.

对于作用域有了深入理解了之后 接下来说一说作用域闭包

  • 网上对于闭包的定义太多了 总的来说 闭包实际上就是:
    无论通过任何手段内部函数传递到所在的此法作用域以外,它都会持有对原始定义作用域的引用,无论在任何处执行这个函数都会使用闭包,
    所以说平时写的代码中
    很多就是闭包只是自己没有发现而已,本质上无论何时何地,如果将(访问他们各自此法作用域的)函数当做第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用了.
    在定时器,事件监听器,ajax请求,web workers..等任务中只要用了回调函数 其实就是在使用闭包!

    几个闭包的例子:
    1. function foo(){
          var a =2;
          function baz(){
              consolo.log( a ) // 2
          }  
          bar(baz)
      }
      function bar(fn){
          fn();
      }
      foo();         //把内部函数baz 传递给bar 当调用这个内部函数时,它涵盖的foo()内部作用域的闭包就可以观察到了,因为能访问到a.
      
    2. function wait(message){
        setTimeout(function timer(){
            console.log(message)
        },1000)
     }
       waIT('hello,closure')
       
    3. function setBot(name,selector){
        $(selector).click(function activator(){
            console.log('activating:' + name)
        })
    }
       setBot('test1','#bot1');
       setBot('test2','#bot2');
       

ok,还有其他的代码模式利用了闭包的作用,表面上看似乎和回调函数无关,这就是 模块

对于模块模式,需要的两个必要条件就是

  1. 必须有外部的封闭函数,该函数必须被至少调用一次,(每次调用都会创建一个新的模块实例)
  2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态!

    举个例子(单例模式)
    var foo = (function Moudle(){
           var something = 'oye';
           var other = [1,2,3];
           function DOSomething(){
               console.log(something);
           }
           function doother(other){
               console.log(other.join('!'));
           }
           return {
               doSomething : dosomething,
               doother:doother
           }
    })();
    foo.dosomething(); //oye
    foo.doother(); // 1!2!3
    
    //模块也是普通的函数 所以也可以接受参数.

模块模式的另一个简单强大的用法是命名将要作为公共API返回的对象:


    var foo = (function moudel(id){
        function change(){
            //修改公共API
            public.one = two;
        }
        function one(){
            console.log(id);
        }
        function two(){
            console.log(id.toUpPErCase());
        }
        var API = {
            change:change,
            identify:one
        }
        return API;
    })('foo moudel')

    foo.identify(); //foo moudel;
    foo.change();
    foo.identify(); // FOO MOUDEL;
    //通过在模块实例的内部保留对公共API对象的内部引用,可以从内部对实例进行修改,包括添加或者删除方法和属性,以及修改值.

对于现代的模块机制
本质上都是将这种模块定义封装进一个友好的API


    //模块管理工具,MyModules
                var MyModules = (function Manager() {
                    var modules = {};
                    function define(name, deps, impl) {
                        for (var i=0; i<deps.length; i++){
                            //将依赖的名字替换成已经注册了的模块 即找到依赖的那个模块
                            deps[i] = modules[deps[i]];
                        }
                        //将依赖数组展开成参数传入该模块的构建函数,生成新模块
                        modules[name] = impl.apply(impl, deps);
                    }
                    function get(name){
                        return modules[name];
                    }
                    return {
                        define: define,
                        get: get
                    }
                })();
                //定义一个模块,data
                MyModules.define("data",[],function(){
                 
                    function getName(name){
                        return '我的名字是'+name;
                    }
                    return {
                        getName:getName
                    }
                });
                //定义一个模块,app
                //该模块依赖data模块
                MyModules.define("app", ["data"], function(data){
                    var newName = 'zhangyu'
                    function run(){
                        console.log(data.getName(newName).toUpperCase());
                    }
                    return {
                        run:run
                    }
                });
                //取出模块
                var data = MyModules.get("data");
                var app = MyModules.get("app");
                console.log(data.getName('zy'))  //我的名字是zy
                app.run();   //我的名字是ZY
            
app和data 模块都是通过一个返回公共API的函数来定义的  app接受data的实例作为依赖参数,并使用.

未来的模块机制
即ES6为模块增加的以及语法支持, 在通过莫魁岸系统进行加载时,ES6会将文件当做独立模块来处理.每个模块都可以导入其他模块或者特定的API成员,同样也可以导出自己的API成员.
(
因为函数的模块并不是一个能被静态识别的模式(编译器无法识别,)他们的API语义只有在运行时才被考虑进来.
相比之下 ES6模块API是静态的,编译器知道一点 然后在编译器会对模块导出的API或者成员的引用做一次检查是否存在,不存在就会抛出早期的错误 不会等到运行期在动态解析
)


    bar.js
    
        function hello(who){
            return '我是'+who
        }
        export hello;
        
        
    foo.js
    
        import hello From "bar"
        var new = 'marry';
        function newHello(){
            console.log(
                hello(new).toUpperCase(); 
            )
        }
        export awesome;
        
    baz.js
    
        module foo from "foo";
        module bar from "bar";
        console.log(bar.hello('lil')) //我是lil
        foo.newHello();    //我是MARRY;
        
    // import 可以将一个模块的一个或者多个API导入到当前作用域中,并分别绑定在一个变量上. module会将整个模块的API导入并绑定.  export会将当前模块的一个标识符(变量 函数) 导出为公共APII

总结:
当函数可以记住并且能访问所在语法作用域,即使函数实在当前作用域之外执行,这时候就产生了闭包.
对于模块首先必须有外部的封闭函数,该函数必须被至少调用一次,(每次调用都会创建一个新的模块实例)
封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态

脚本宝典总结

以上是脚本宝典为你收集整理的简述作用域还有闭包延伸至模块化全部内容,希望文章能够帮你解决简述作用域还有闭包延伸至模块化所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。