函数自调用+预编译

一、函数自调用

 引例:普通函数 和 自调用函数

//普通函数:
var a=100
function fn(){
 console.log(a)    //打印全局变量a=100
 console.log(fn)  //打印全局函数fn
}
fn()

//自调用函数:
var a=100
function fn(){
 console.log(111)    //打印111
 fn()   // 无限调用全局fn函数 ————函数的自调用 
}
fn()

1.利用函数自调用来执行重复的逻辑,不满足临界条件时,结束自调用

/*思想:
if语句中条件成立后就结束自调用 否则一直执行else语句里的业务代码并自调用
*/
function  fn () {							
	if(){
		return 100  //满足条件就返回某个值 不再执行else语句 结束自调用
	}
	else{
		console.log("业务")
		fn()    //自调用
	}			
}
fn()

案例:(10的阶乘)

function fn (n) {
	if(n<=1){
		return 1
	}
    else{
		return n*fn(n-1)
	}
}						
var re=fn(10)   //1*2*3...*9*10
console.log(re) //fn(10)==>10*fn(9)==>10*9*fn(8)==>10*9*8*fn(7)==>...==>10*9...2*fn(1)

案例:(递减求和)

//要求:2022+2018+2014+2010+....n(n>0)
function fn (n) {
  if(n<=0){        //当n<=0时是终止条件 结束自调用
	return 0
  }             
  else{
	return n+fn(n-4)   //一直加上下一次调用函数的返回值 直到不满足临界条件
  }
}		   
var re=fn(2022)
console.log(re)

案例:(递增求和)

//要求:128+130+132+134+136.....1000(不能超过1000)
function fn (n) {
	if(n>=1000){       //临界条件
		return 1000
	}
	else{
		return n+fn(n+2)   //自调用
	}
}
var re=fn(128)
console.log(re)

案例:(遍历数组)

//假设只知道数组内部全是(数字和数组的混套),但不知道嵌套了几层
//要求打印出所有数字	
var arr=[[1,2,3,4,[2,3,4,5],[3,4,5,6],[4,5,6,7]],[5,6,7,8,9,10],20,21]
 function fn(arr){
	// 遍历数组
	for(var i=0;i<arr.length;i++){
		if(typeof(arr[i])=="number"){  //如果数组元素是数字 则打印
			console.log(arr[i])
		}else{                         //如果数组元素是数组 则再次遍历数组 直到遍历完全部数组
			fn(arr[i])
		}
	}
}		   
fn(arr)

2.注意:fn标识符的问题

// fn标识符的问题  fn是函数定义时的名字函数体内部可以直接访问
var obj={say:function fn(n){
	console.log(666)
	if(n<0){
		return 
	}
	// say(n-1)  报错 
    //想用对象的方法访问但是没有加上对象 直接访问不合理 因为作用域内部和外部都没有say这个标识符
	fn(n-1)       //函数自调用
}}
obj.say(10)
console.log(obj.say.name)   //打印"fn"  函数的名字

3.实参arguments

实参==>调用时传入的数据(两种写法  数据直接量 变量)

  •  arguments.length 实参的个数
  •  arguments.callee 运行的函数对象(解决匿名函数没有名字无法访问不能自调用的问题)

案例:

var obj={say:function(){
	  obj.say()
}}
(function(n){
	if(n<=1){
         console.log(1)
    }
	else{
         console.log(n);
         arguments.callee(n-1) //指向正在运行的函数 实现自调用
    }
})(10)
//打印10 9 8 7 ... 1

二、预编译

 1.局部预编译

    函数调用时  是怎么运行代码的?            

  1. 分析代码:检查符号是否正确 、词法分析...
  2. 隐式操作 ==> 局部预编译
  3. 运行代码: 预编译过的就不在运行    

 局部预编译:函数调用之后  代码运行之前 js引擎会对函数进行预编译

function fm(a) {
      console.log(a)  //100  传入的实参
      var a=20
      function  fn () {}
      console.log(a)  //20   修改后的值
}
var b=100
fm(b)
//  AO:{a:100==>20,fn:function fn () {}}

步骤:

  1.函数每次调用时都会生成一个AO对象:执行期上下文对象

  2.给AO对象添加成员

  • 函数内部的局部变量和形参变量作为AO对象的成员  值为undefined(隐式声明提升)

      AO:{a:undefined}

      ao.a=undefined

      ao.a=undefined //形参和局部变量名一样的时候 不影响

  3.把传入的实参赋值给AO对象的属性 (拓展知识:arguments和形参变量相关的技术)

       AO:{a:100}  //把100传进去

  4.局部函数声明,赋值  

  • 把局部函数的名字作为AO对象的成员名,把函数体赋值给AO对象 (局部函数的隐式声明提升)

       AO:{a:100,fn:function fn () {}}                       

2.全局预编译                  

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

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

    步骤:

        1.生成一个对象Global Object (GO)  (特殊的AO)

    GO:{}

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

    GO:{a:undefined}

        3.把所有全局函数的名字作为GO对象的成员名,把函数体赋值给GO对象

     GO:{a:undefined,fn:function fn(){console.log(666)}}

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

        5.执行代码

     GO:{a:undefined,fn:function fn(){console.log(666)}}

     console.log(a)  //undefined

     a=20

     GO:{a:undefined==>20,fn:function fn(){console.log(666)}}

拓展:

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

GO对象的成员全部浅拷贝给环境对象window  (node.js环境中没有这一步)

拓展知识:关于访问成员 

console.log(a)  访问的是GO对象的成员(作用域链中没有的成员 程序会报错 防止野指针出现)

console.log(window.a) 访问的是window对象的成员  不报错 原型链也没有就返回undefined

           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈哈ha~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值