zoukankan      html  css  js  c++  java
  • javascript作用域与闭包

    Javasript作用域概要 

    在javascript中,作用域是执行代码的上下文,作用域有三种类型:

    1)  全局作用域

    2)  局部作用域(函数作用域)

    3)  eval作用域

            var foo = 0; //全局作用域 
            console.log(foo);//输出0
    
            var myFunction = function() {
                var foo = 1; //局部作用域 
                console.log(foo); //输出1
                var myNestedFunction = function() {
                    var foo = 2;
                    console.log(foo); //输出2
                }();
            } ();
            eval("var foo=3;console.log(foo);");//eval()作用域

    javascript中,函数内部的可以直接读取全局的变量:

    取内部的局部变量,例如:

    还有需要注意的,在函数内部声明变量的时候,一定要用var命令,如果不用的话,实际声明的是一个全局变量哈

    javascript没有块作用域

    由于逻辑语句无法创建作用域,因此变量可以互相覆盖,例如:代码执行时,foo是变化的,因为javascript没有块作用域,只有函数,全局和eval作用域

    javascript块级作用域中的变量是由其执行环境确定的,例如:

    var a=111;
    if(true){
      var a=222;
    }
    console.log(a);

    输出的是: 222,是因为执行环境是window

    例如:

    var a=111;
    function num(){
        var a=333;
         if(true){
            var a=666;
         }
        console.log(a);
    }
    
    num();
    console.log(a);

    调用 666,111 是因为if块级作用域的执行环境是num函数 ,所以输出666,之后console.log(a),访问的是全局a,所以输出111

    作用域链(词法作用域)

    当javascript查找与变量关联的值时,会遵循一个查找链。这个链是基于作用域的层次结构的。

    例如:查找 sayHiText变量的顺序为;Func2中--àfunc1中 ---》全局变量中

    函数的作用域会一层层的查找,当变量没有用var声明变量时,它会一层层的往上找,找到就替换,如果回溯到顶层window时,发现还是没有,

    就会把这个变量定义在window下(即全局变量),例如以下代码:

    var name="first";
    function test(){
       var name="second";
       function num(){
           name="third";
          console.log(name);
       }
       num();
       console.log(name);
    }
    test();
    console.log(name);

    此时会输出:third   third   first

    这是因为name=”third”并不是局部变量,然后它恰好在test这个大函数中,这时候解析器就在test函数中寻找name,发现有name,就把ctrip的name修改为third。

    为了进一步说明,再看如下代码:

    var name="first";
    function test(){
       var name="second";
       function num(){
           age=28;
          name="third";
          console.log(name);
       }
       num();
       console.log(name);
    }
    test();
    console.log(name);
    console.log(age)

    输出为:third   third  first  28

    在test环境中找到了name,但是没有找到age,所以age会一直回溯到window,所以这时候age是全局变量。

    如何从外部读取局部变量?

    有时候我们需要得到函数内的局部变量,但是,在正常情况下是办不到的,只有通过变通方法实现,那就是在函数的内部,再定义一个函数。

    既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在 f1外部读取内部变量了,即f2函数就是闭包。

    闭包的概念

    闭包就是能够读取其他函数内部变量的函数。由于在javascript中,只有函数内部的子函数才能够读取局部变量,

    因此可以把闭包简单理解成“定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

    既然闭包是函数嵌套函数,例如:

    function A(){
          var b=1;
         function B(){
            alert(b++)
         }
    }
    
    A();

    这个是闭包吗?

    不管执行多少次,都会返回undefined,因为A函数没有return语句,所以默认会返回undefined;

    显然上面这个不是闭包,只有当你return内部function时,就是一个闭包;

    例如:

    function A(x){
         var temp=3;
         return function(y){
            return x+y+(++temp);
         }
    }
    
    var bar=A(2);//bar现在是一个闭包
    bar(10);//返回16
    bar(10);//返回17,因为每运行一次,temp加1,temp内存并没有释放

    闭包的用途

    闭包最大的两个用途是: 1) 可以读取函数内部的变量

                                    2)让这些变量的值始终保持在内存中

    例如以下代码:

    在这段代码中,result实际上就是闭包f2函数, 它一共运行两次,第一次是99,第二次是100。

    这就证明了函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

    因为f1是f2的父函数,而f2被赋给了一个全局变量,这就导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会再调用结束后,被垃圾回收机制回收。

    使用闭包的注意点

    1)  由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包。

    2)  闭包会在父函数外部,改变父函数内部变量的值。

     

    如上,为什么输出的结果是the window 了?

    主要有两个知识点:

    1)  内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments之外)

    2)  如果需要访问对象的name属性,就需要显示的定义一个变量that来引用this,而这个变量此时就指向了object对象了

    getNameFunc的第一个()是属于方法的调用,所以this绑定到了object对象上,自然this.name为the object,但是闭包函数无法访问这个this,它只能访问到全局的this,因此输出的是”the window”

    例如:

    闭包中的this指向的是全局对象,而getNameFunc中的this指向的是调用者,所以输出的结果如上。例如:

    为对象显示定义了that来引用this,因此输出的是 “the object”

    闭包经常用于创建含有隐藏数据的函数(但并不总是这样)

    例如:

    var db = (function() {
    // 创建一个隐藏的object, 这个object持有一些数据
    // 从外部是不能访问这个object的
    var data = {};
    // 创建一个函数, 这个函数提供一些访问data的数据的方法
    return function(key, val) {
        if (val === undefined) { return data[key] } // get
        else { return data[key] = val } // set
        }
    // 我们可以调用这个匿名方法
    // 返回这个内部函数,它是一个闭包
    })();
    
    db('x'); // 返回 undefined
    db('x', 1); // 设置data['x']为1
    db('x'); // 返回 1
    // 我们不可能访问data这个object本身
    // 但是我们可以设置它的成员
  • 相关阅读:
    51Nod--1247 可能的路径(gcd)
    51Nod--1117 聪明的木匠(排序)
    51Nod--1076 2条不相交的路径(强连通分量)
    HDU--4486 Task(贪心)
    BZOJ--1045-- 糖果传递(中位数,排序)
    [Windows Server 2012] Discuz X3安全设置
    [Windows Server 2012] PHPWind安全设置
    [Windows Server 2012] MySQL安全加固
    [Windows Server 2012] Filezilla安全加固方法
    [Windows Server 2012] WordPress安全设置方法
  • 原文地址:https://www.cnblogs.com/alice626/p/4956239.html
Copyright © 2011-2022 走看看