js7---(函数的自调用,预编译,作用域链)

128 阅读3分钟

函数的自调用

函数自己调用自己

例子1:不要运行,浏览器会崩溃

   function fn () {
    	console.log(666)
    	fn()
    }			
    fn()
    

列子2:实现10的阶乘

 function fn (n) {
       	if(n<=1){
    		return 1
    	}
    	else{
    		return n*fn(n-1)
    	}
    }						
    var re=fn(10)
    console.log(re)
    

image.png

1.fn标识符

因为它是函数定义时的名字 函数体内部可以直接访问

   var obj={say:function fn(n){
    	console.log(666)
    	if(n<0){
    		return 
    	}
    	// say(n-1)//报错
    	fn(n-1)//自己调用自己
    }}
    

2.匿名函数的自调用

通过arguments属性调用

   (function(n){
    	if(n<=1){console.log(1)}
    	else{console.log(n);arguments.callee(n-1) }
    })(10)
    

image.png

预编译

函数调用时,运行代码的过程:

1.分析代码是否正确(符号,词法分析)

2.隐式操作 ==> 预编译:函数调用了以后 在运行代码之前

3.运行代码: 预编译过的就不在运行

1.局部预编译

(1) 创建AO对象--Activation Object(执行期上下文):AO{ };

(2) 找形参和变量声明,将变量和形参名作为AO属性名,即变量提升过程,值为undefined;

(3) 将实参的值放到形参中去

(4) 在函数体里面找函数声明,值赋予函数体

1.1 具体流程

 1.分析代码是否正确  符号  词法分析
    var var=20;
    var 01js=100
    var a;
    var a,

    2.隐式操作 ==> 预编译:函数调用了以后  在运行代码之前			
    2.1函数每次调用都会生成一个对象:执行期上下文对象
    2.2.给AO对象添加成员:函数内部的局部变量和形参变量 名 作为AO对象的属性名
    AO:{a:undefined}
    ao.a=undefined
    ao.a=undefined//形参和局部变量一样的时候 不影响
    2.3.把传入的实参赋值给AO对象的属性
     AO:{a:100}
    2.4.局部函数声明,赋值   把局部函数的名字让AO对象也有一个一样的成员名,把函数体赋值给这个属性
    AO:{a:100,fn:function fn () {}}			
    function fm(a) {
    	console.log(a)
    	var a=20
    	function  fn () {}
    	console.log(a)
    }
    var b=100
    fm(b)
    
    

    3.运行代码: 预编译过的就不在运行			
    AO:{a:100==>20,fn:function fn () {}}
    console.log(a)//100
    a=20
    console.log(a)//20
    

2.全局预编译

创建一个函数

 console.log(a)
    var a = 20
    function fn() {
    	console.log(66)
    }

过程:

   1.生成一个对象Global Object  (GO)
    GO:{}
    2.把所有的全局变量 设置为GO的属性名
    GO:{a:undefend}
    3.把所有函数名 作为GO的成员名,把函数体赋值给这个成员
    GO:{a:undefend,fn:function fn(){console.log(66)}}
    4.执行代码
    GO:{a:undefend==>20,fn:function fn(){console.log(66)}}
    console.log(a) //undef
    a = 20 //
    

2.1 不同环境下的全局预编译

1.不同的环境中运行js代码不一样

2.GO对象的成员全部浅拷贝给环境对象window

3.node.js 环境中没有这一步

作用域链

函数有属性length,name ,[[scoped]]

js对象有两种成员

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

一种是下文成员(底层语法访问的成员),[[scopes]]括起来的成员名 就是下文成员

函数在定义/声明的时候 就有了[[scopes]] 里面保存了上层的AO对象,函数调用时会生成AO对象 AO保存在scopes对象内部的

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

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

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

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

列:

 function a() {
function b(){
	function c() {
	
	}
	c();
}
b();
  }
 a();
 

分析

  a 定义:  a.[[scope]]    --> 	0:GO

   a 执行:  a.[[scope]]    --> 	0:AO(a)
	      				 1:GO
	      				 
    b 定义:  b.[[scope]]    --> 	0:AO(a)
	      				 1:GO
      注意b执行了才会产生c的定义哈!!
     b 执行:  b.[[scope]]    --> 	0:AO(b)
	      				 1:AO(a)
	      				 2:GO
	      				 
    c 定义:  c.[[scope]]    --> 	0:AO(b)
	     				 1:AO(a)
	     				 2:GO
	     				 
      c 执行: c.[[scope]]    -->   0:AO(c)
	      				 1:AO(b)
	      				 2:AO(a)
	      				 3:GO
                                             

列:

 function a() {
var aaa=123;
function b() {
	var bbb=234;
	console.log(aaa);
} 	return b;
 }
var glob=100;
var demo=a();
demo();

分析: 函数a执行时,a.[[scope]]如下:

image.png 函数b被定义时,b.[[scope]]如下:

image.png

函数b出生时是直接继承a的作用域的,所以:

整体的情况是

image.png

a函数被执行完毕之后,a函数的执行期上下文肯定是要被销毁(销毁=拆线)的,所以应该是下面的状况:

image.png