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

结果:在这里插入图片描述

分析:

			/*
			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			
		}
		
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值