JavaScript学习笔记(八)——作用域

2022/6/16 1:21:20

本文主要是介绍JavaScript学习笔记(八)——作用域,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

作用域

1.概述

作用域:标识符(变量和函数名)在哪些地方(函数的代码块内部和外部)能够被访问,哪些地方就是这个标识符的作用。

规则:函数内部的代码可以访问函数内部的标识符,也可以访问函数外部的标识符,但是反过来不行,也就是说外部不能访问函数内部的标识符。

//案例:
    var a = 200;
    function fn(a) {
        //var a=90//210
        function fm() {
            console.log(a, 122)//210
            a = 90
            console.log(a, 123)//90
        }
        fm(a)//fm(210)
        console.log(a, 124)//90
    }       
    fn(a+10)//fn(210)
    console.log(a, 125)//200

 

 //案例
    var total=0;
    function  increment () {
     var total=0
          total=total+2
    }
    function  decrease() {
          total=total-2
    }
    increment()
    increment()
    decrease()    
    console.log(total)//0

 

//案例: 函数的调用是运行一次代码:每一次调用都会重新执行所有代码
    var total = 0;
    function increment() {
        // var total = 0
        total = total + 2
        console.log(total)
    }
    increment()//{var total=0;  total = total + 2}
    increment()//{var total=0;  total = total + 2}

 

2.同名标识符提升问题

总结:每一个作用域在运行时,js引擎会先把作用域内部的关键字隐式提前扫描 并声明 。

var a = 10
    function fn() {
        //隐式操作:把var 修饰的变量名提前声明
        console.log(a)//声明了却没有赋值的变量undefined
        a = 40//给隐式声明的变量赋初始化值
        var a = 20//更新a的值 40==>20
        console.log(a)//20
    }
    console.log(a)//10
    fn()//
    console.log(a)//10
  1. 变量函数同名时 变量然后函数

     console.log(a)
        var a=20
        function  a () {
            console.log(100)
        }       
        console.log(a)
    ​
    ​
        /*
        1.
        var a
        function  a () {
            console.log(100)
        }
         console.log(a)
         a=20
         console.log(a)
        */
  2. 变量变量同名时

    console.log(a)//unf
        var a=10;
        console.log(a)//10
        var a=20;       
        console.log(a)//20
    ​
    ​
    /*
        1.var a;var a;
        2.console.log(a)//undef
        3.a=10;
        4.console.log(a)//10
        a=20;
        console.log(a)//20
        */
  3. 函数和函数同名时

      function fn () {
            console.log(111)
        }
        function fn () {
            console.log(2222)
        }           
        fn()
  4. 同名标识符提升顺序问题:记住四个字

    法则:形(形参和变量)实函运

    标识符有三种写法: var function function(a){}

        var a=20
        function fn(a){         
            console.log(a,1)//函数
            a=90
            console.log(a,2)
            var a=100
            console.log(a,3)
            function  a () {
                console.log(6666)
            }
            console.log(a,4)            
        }
        fn(a)
        /*
        var a;
        a=20
         function  a () {
            console.log(6666)
         }
         
        //打印函数
        a=90
        console.log(a,2)//打印90
         a=100
        console.log(a,3)//100
        console.log(a,4)    //100
        */

    这个流程就是:

    1.先隐式提升当前作用域内部的所有形参变量和局部变量 (只是声明提升,不提升赋值)

    2.再把实参赋值给形参变量

    3.然后执行函数的隐式提前声明

    4.再按照代码顺序运行代码

3.函数运行时的作用域

函数运行时 ,是在写函数代码的地方运行代码 , 不是在调用代码的地方运行代码。

function fn () {
            var a=90
             function fm () {
                console.log(a)
             }
             return fm//a//200
        }
        var a=100
        var re=fn()
        // console.log(re)
        re()//console.log(a)
function fn(a) {
            function fm() {
                a = a + 1
                console.log(a)
            }
            return fm
        }
        var f1=fn(10)
        f1()
        f1()
​
    /*
        {
             var a=12
             return fm
             {
                a = a + 1
                console.log(a)//11 
             }
             {
                a = a + 1
                console.log(a)//12
                                 
             }
        }
         f1()==>fm()
    */
       function fn(a) {
            function fm() {
                a = a + 1
                console.log(a)
            }
            return fm
        }
        var f1=fn(10)
        f1()            
        var f2=fn(10)
        f2()
​
        /*
         {
             var a=10
            {   
            a = a + 1
            console.log(a)//11  
            }
         }
         {
              var a=10
             {
                 a = a + 1
                 console.log(a)//11 
             } 
         }                      
        */

4.js函数预编译

什么是预编译?

函数运行时,代码的运行步骤就是函数预编译。

js完成解释执行分为三个步骤:

1.语法分析;

2.预编译(全局预编译、函数预编译);记住四个字:形(形参和变量)实函运

3.执行语句。

深度理解一下上面的第二步 预编译:

函数每调用一次,会生成一个新的对象,自调用且无终止条件的话生产无数个对象,浏览器就会瘫痪。

  1. 给这个AO对象添加成员,函数内部的局部变量和形参变量 名 作为AO对象的属性名。形参名和实参名冲突时,并不影响。

  2. 把传入的实参赋值给AO对象的属性;

  3. 把局部函数的名字 让AO对象也有相同的成员名,把函数赋值给这个属性;

  4. 运行代码。

全局作用域运行代码时,也有预编译

  1. 生产一个对象 Global Object(GO);

  2. 把所有的全局变量,设置为GO的属性名;

  3. 把所有函数名作为GO的程序名,把函数名赋值给这个成员;

  4. 看是不是浏览器环境中的js脚本,如果是浏览器,还会执行异步,GO给window对象共享成员;

  5. 运行代码。

全局预编译还有一步: 不同的环境中运行js代码不一样,GO的成员全部浅拷贝给环境对象window。node.js中没有这一步。

拓展知识

关于访问成员,console.log(a)访问的时GO对象的成员。

console.log(window.a);原型链中没找到的话不报错,浏览器返回undifined;但是作用域链中GO中找不到的话就会报错。

5.作用域链

执行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被 "销毁"。

js对象有两种成员:

  • 一种是上文成员(js语法可以访问的成员);

  • 一种是下文成员(底层语言,只能看不能运用)。

  • [[]] 括起来的成员名 就是下文成员。

  • 例如:[[scope]]这个对象内部保存的就是函数的作用域。现在我们知道作用域就是一个对象,函数在定义或者申明的时候就有了作用域[[scope]]。里面保存了上文的AO对象。

function fn(n){
    var a=20;
    function fm(){
        
    }
}
fn(100);//AO{n:undifined==> 100, a:undif==>20,function fm(){}}
​
fn();//AO{n:undif,a:undif ==>20,function fm(){}}
function fn(n){
    var a=20;
    function fm(){
        
    }
}
fn(100);
fn(200);

函数生成了就会有个属性[[scope]]作用域“数组”(只能引擎使用)

函数调用时生成AO对象 , 会把AO对象放在scope

每次调用都会放在scopes前面(顶部)

每个函数scopes数组中天生就有一个AO对象 就是这个函数的上层的AO

 

function fn(a){
    var a2=100;
    function fm(b){
        var b2=200;
    }
    fm(20);
}
fn(10);

分析上面这段代码的作用域链

如下:

    /*
            Go:{fn函数创建了 放在Go对象内部}
            
            fn.[scope]=[
                Go:{fn函数创建了 放在Go对象内部}
            ]
            
            fn(10)=>
            fn.[scope]=[
                AO-fn(10):{
                    a:10,
                    a2:100,
                    fm创建了                   
                },
                Go:{fn函数创建了 放在Go对象内部}
            ]
            
            
            fm.[scope]=[
                AO-fn(10):{
                    a:10,
                    a2:100,
                    fm创建了                   
                },
                Go:{fn函数创建了 放在Go对象内部}
            ]
            fm(20)
            fm.[scope]=[
                AO-fn(10)-fm(20):{
                    b:20,
                    b2:200
                    console.log(a,a2,b,b2,c)
                },
                AO-fn(10):{
                    a:10,
                    a2:100,
                    fm创建了                   
                },
                Go:{fn函数创建了 放在Go对象内部,c}
            ]
            
            
            fm(90)
            fm.[scope]=[
                AO-fn(10)-fm(90):{
                    b:90,
                    b2:200
                    console.log(a,a2,b,b2,c)
                },
                AO-fn(10):{
                    a:10,
                    a2:100,
                    fm创建了                   
                },
                Go:{fn函数创建了 放在Go对象内部}
            ]
            */

难题分析:

        function fun(n, o) {
            console.log(o);
            return {
                fun: function (m) {
                    return fun(m, n);
                }
            };
        }
        var a = fun(0);
        a.fun(1);
        a.fun(2);
        a.fun(3);
        var b = fun(0).fun(1).fun(2).fun(3);
        var c = fun(0).fun(1);
        c.fun(2);
        c.fun(3);js

结果:image-20220522204215643

分析:

            /*
            Go:{fun函数创建了,a:und,b:und,c:und}         
            fun.[scope]=[               
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
            
            //a = fun(0);
            fun.[scope]=[
                AO-fun(0):{
                    n:0,
                    o:und
                    return {fun:function(m) {return fun(m, n)}}
                }, 
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
            
            a.fun.[scope]=[
                AO-fun(0):{
                    n:0,   
                    o:und   //第一次打印undefined
                    return {fun:function(m) {return fun(m, n)}}
                }, 
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
            
            //a.fun(1);
            a.fun.[scope]=[
                AO-fun(0)-a.fun(1):{
                    m:1
                    return fun(1, 0)
                },
                AO-fun(0):{
                    n:0,   
                    o:und,
                    return {fun:function(m) {return fun(m, n)}}
                }, 
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
            fun.[scope]=[
                AO-fun(1, 0):{
                    n:1,
                    o:0,   //第二次打印0
                    return {fun: function(m) {return fun(m, n)}}
                },
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
                
                
            //a.fun(2)  
            a.fun.[scope]=[
                AO-fun(0)-a.fun(2):{
                    m:2,
                    return fun(2, 0);
                },
                AO-fun(0):{
                    n:0,    
                    o:und
                    return {fun:function(m) {return fun(m, n)}}
                }, 
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]   
                
            fun(2, 0);
            fun.[scope]=[
                AO-fun(2,0):{
                    n:2,
                    o:0   //第三次打印0
                    return {fun: function(m) {return fun(m, n)}};
                },
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]   
            a.fun(3)同理==>第四次打印0
                
            
            */
            
            
            
           /*
            Go:{fun函数创建了,a:und,b:und,c:und}
            fun.[scope]=[               
                Go:{fun函数创建了,a:und,b:und,c:und}
            ] 
            
            //var b = fun(0).fun(1).fun(2).fun(3);
            ==>
            fun(0)
            fun.[scope]=[
                AO-fun(0):{
                    n:0,
                    o:und
                    return {fun: function(m) {return fun(m, n)}}
                },
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
              
            fun(0).fun.[scope]=[
                AO-fun(0):{
                    n:0,
                    o:und   //第五次打印undefined
                    return {fun: function(m) {return fun(m, n)}}
                },
                Go:{fun函数创建了,a:und,b:und,c:und}
            ]
          fun(0).fun(1)
       fun(0).fun.[scope]=[
    AO-fun(0)-fun(0).fun(1):{
    m:1
    return fun(1, 0);
    },   
        AO-fun(0):{
           n:0,
           o:und
           return {fun: function(m) {return fun(m, n)}}
        },
        Go:{fun函数创建了,a:und,b:und,c:und}
       ]
       

       fun(1, 0)
       fun.[scope]=[
    AO-fun(1, 0):{
    n:1,
    o:0,  //第五次打印0
    return {fun: function(m) {return fun(m, n)}}==>fun(0).fun(1)
    },
        Go:{fun函数创建了,a:und,b:und,c:und}
       ]


    fun(0).fun(1).fun.[scope]=[
    AO-fun(0)-fun(0).fun(1):{
    m:1
    return fun(1, 0);
    },   
    AO-fun(0):{
    n:0,
    o:und
    return {fun: function(m) {return fun(m, n)}}
    },
    Go:{fun函数创建了,a:und,b:und,c:und}
    ]

    fun(0).fun(1).fun.[scope]=[
    AO-fun(0).fun(1):{
    m:1
    return fun(1, 0);
    },
    AO-fun(0):{
    n:0,  
    o:und
    return {fun: function(m) {return fun(m, n)}}
    },
    Go:{fun函数创建了,a:und,b:und,c:und}
    ]

    fun.[scope]=[
    AO-fun(1,0):{
    n:1
    o:0
    return {fun: function(m) {return fun(m, n)}}
    },
    Go:{fun函数创建了,a:und,b:und,c:und}
    ]
    fun(0).fun(1).fun(2)
    [scope]=[
    AO-fun(0).fun(1).fun(2):{
                m:2,
                    return fun(2,1)
    },
    AO-fun(1,0):{
    n:1
    o:0
    return {fun: function(m) {return fun(m, n)}}
    },
    Go:{fun函数创建了,a:und,b:und,c:und}
    ]
    fun(2,1)
    AO:{
    m:2
    o:1
    }


这篇关于JavaScript学习笔记(八)——作用域的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程