资讯专栏INFORMATION COLUMN

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

imingyu / 2021人阅读

摘要:首先变量对于一个程序来说是一个很重要的角色那么问题来了这些变量存在哪里程序用到的时候如何找到变量呢所以需要一套规则来存储变量方便之后再找到这套规则就成为作用域是一门编译语言对于来说大部分情况下编译发生在代码执行前的几微妙的时间内对于参与到一

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

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,还有其他的代码模式利用了闭包的作用,表面上看似乎和回调函数无关,这就是 模块

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

必须有外部的封闭函数,该函数必须被至少调用一次,(每次调用都会创建一个新的模块实例)

封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态!

    举个例子(单例模式)
    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

未来的模块机制
即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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97576.html

相关文章

  • JS三部曲,变量提升,this与作用闭包

    摘要:这篇文章总结下之前看的文章和自己在工作中遇到的坑,纯手写的,有什么写的不对的请多多提出修正哈变量提升何为变量提升里面的变量在声明之前就可以使用,因为该声明已经被提升至该作用域函数或全局的顶部直接上代码和都会变量提升优先级上面可理解为函数这时 这篇文章总结下之前看的文章和自己在工作中遇到的坑,纯手写的,有什么写的不对的请多多提出修正哈 变量提升 何为变量提升?js里面的变量在声明之前就可...

    dongxiawu 评论0 收藏0
  • 函数作用闭包

    摘要:函数作用域要理解闭包,必须从理解函数被调用时都会发生什么入手。可以说,闭包是函数作用域的副产品。无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。 函数作用域 要理解闭包,必须从理解函数被调用时都会发生什么入手。 我们知道,每个javascript函数都是一个对象,其中有一些属性我们可以访问到,有一些不可以访问,...

    Code4App 评论0 收藏0
  • JS 中的闭包是什么?

    摘要:大名鼎鼎的闭包面试必问。闭包的作用是什么。看到闭包在哪了吗闭包到底是什么五年前,我也被这个问题困扰,于是去搜了并总结下来。关于闭包的谣言闭包会造成内存泄露错。闭包里面的变量明明就是我们需要的变量,凭什么说是内存泄露这个谣言是如何来的因为。 本文为饥人谷讲师方方原创文章,首发于 前端学习指南。 大名鼎鼎的闭包!面试必问。请用自己的话简述 什么是「闭包」。 「闭包」的作用是什么。 首先...

    Enlightenment 评论0 收藏0
  • 简述Function类型

    摘要:类型是提供的引用类型之一,通过可需变更创建对象。调用自身的函数被称之为递归函数。想要解决上述递归函数的问题,可以使用对象属性替换具体的函数名。保护贡献的局部变量。 Function类型 概述 Function与函数 函数是这样的一段JavaScript代码,她只定义一次,但是可能被执行或调用多次。Function类型是JavaScript提供的引用类型之一,通过Function可u需变...

    ityouknow 评论0 收藏0
  • python奇遇记:深入理解装饰器

    摘要:可见装饰器改变了函数的功能。装饰器除了改变函数功能之外还有一个特性是,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。 什么是装饰器 装饰器是什么,简单来说,装饰器可以改变一个函数的行为,比如原本有一个函数用来计算菲波那切数列,我们给这个函数加个计算执行时间的装饰器,这样原来的函数不仅能够计算菲波那切数列,而且还可以输出计算花费了多少时间。 在Python中,有几个很...

    lemon 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<