js等待返回值然后执行下一步_JS函数基础与递归原理

fbfc391cb958312ab0cd577305bed837.png

四种定义函数

  1. 匿名函数
  2. 具名函数
  3. 赋值函数
  4. 箭头函数

匿名函数

function (){
    console.log('hi')
}

具名函数

function sayHi(){
    console.log('hi')
}

赋值函数

let sayHi = function (){
        console.log('hi')
    }

箭头函数

箭头函数表达式的语法比函数表达式更简洁。更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。

let sayHi = ()=> console.log('hi')

变量

全局变量

  1. 顶级作用域里声明的变量
  2. http://window.xxx
let a = 1
window.a = 1

局部变量

  1. { }包括起来的代码为局部变量,除了使用window.a = 1声明变量,也不能使用var声明(历史遗留问题)
  2. 作用域可以嵌套
{ let b = 1 }
b
// Uncaught ReferenceError: b is not defined

作用域嵌套规则:默认就近原则。查找声明时,向上取最近的作用域,查找声明过程与函数执行无关,声明值才是与函数执行相关。

静态作用域也叫「词法作用域」(编译原理的知识)

闭包:一个函数用到外部变量,那么这一组组合就形成闭包


函数提升

具名函数function fn()不管在何处声明,最终JS引擎解析的时候都会提升到前面。

add(3,5);
function add(x,y){return x+y};
// 8

匿名函数let fn = funticon(){}fn只是赋值,并不是函数名称,实际上该function并没有名称,这种==匿名函数的声明并不进行函数提升。==

add(3,5);
let add = function(x,y){return x+y};
// Uncaught ReferenceError: add is not defined

形式参数

形式参数的意思就是非实际参数,在函数内部相当于==变量声明==

function add(x,y){ return x + y}

// 以上函数与 ↓ 函数近乎等价
function add(){
    var x = arguments[0]
    var y = arguments[1]
    return x + y
}

疑问:形参与对象、值有函数执行后有不同的结果

let a = 1   // 数据存储区域不同,“1”放在“stni "区
let b = {value:1}

形参只是给参数起名字,形参可多可少

function add(x){
    return x + arguments[1]
}
add(1,2);
//3

函数返回值

return返回值为undefined

console.log('hi');
hi   // 打印值
undefined  // 返回值

⚠️注意:只有函数才有返回值


JS的调用时机

JS定义一个函数后并不会自动的执行它。函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。

调用函数才会以给定的参数真正执行这些动作。例如:定义了函数add,并在之后调用函数

function add(x,y){     // 定义一个函数为”add“
    return x + y
}
add(1,2)    // 传入参数并执行函数

定义与执行不一定同时进行,其中的过程就存在着==调用时机==,不同时间调用函数会有不同的效果

function f() {console.log(a) }
let a = 0;
f()  // 0
a = 1;
function f() {console.log(a) }
let a = 0;
a = 1;
f()  // 1

循环结构

其中for...let是最简洁的循环结构

for(i = 0; i<6; i++){
    console.log(i)
}

除了for...let的循环体,还有function可以运用

function f(n,b){
    if(b === undefined ){b=n,n=0}
    if(n <= b){
        console.log(n)       
        f(n+1,b) 
    }
}
// 以下两种函数调用
f(5)
f(0,5)

setTImeout

setTimeout()是设定一个指定等候时间 (单位是千分之一秒, millisecond),时间到浏览器就会执行一个指定的代码。

setTimeout("alert('对不起, 要你久候')", 3000 )

⚠️注意:变态的情况是即便将setTimeout等待时间设置为0,JS也会在所有代码都执行完再马上执行setTimeout(console.log('hi'),0)

可以理解为JS将所有代码顺序执行,一旦设置setTimeout后,该代码的顺序会延后执行。

for(var i = 0; i<6; i++){    // 使用var,后续会解析
  setTimeout(()=>{  
    console.log(i)
  },0)
}
// 6 6 6 6 6 6

i<6; i++条件执行完i = 6跳出循环体,然后setTimeout才会一条条的执行,所以最终打印出6个6。

针对这种情况,新版ES6才会独立为for...let的循环体进行优化。

for(let i = 0; i<6; i++){ 
  setTimeout(()=>{  
    console.log(i)
  },0)
}
// 这样才会打印出 0 1 2 3 4 5

证明setTimeout的变态行为,先声明变量i一旦不使用for...let循环结构,就会出现6个6的问题

let i
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

调用栈

JS引擎在调用一个函数前,需要将函数所在的环境push到一个数组里,等函数执行完,把环境pop出来,然后return到调用前的环境里,执行后续的代码。

递归

也就是JS执行函数时会进入另外一个空间执行函数,这个空间成为调用栈。若一个函数里面嵌套自己,最终会形成多个调用栈,一层层递进运算再回退运算称为递归运算

function f(n){
    return n != 1 ? n*f(n-1) : 1 
}
f(4)   // 24

函数内部的运算过程

f(4)  
= 4 * f(4-1)
= 4 * (3 * f(3-1))
= 4 * (3 * (2 * f(2-1)))
= 4 * (3 * (2 *( 1 * f(1))))    // f(1),n = 1 不满足条件,停止调用

递进完成后,进行回归运算

f(4)  
= 4 * f( 4-1 )
= 4 * ( 3 * f( 3-1 ))
= 4 * ( 3 * ( 2 * f( 2-1 )))
= 4 * ( 3 * ( 2 *( 1 * f( 1 ))))    // f(1),n = 1 不满足条件,停止调用
----------- 
= 4 * ( 3 * (2 *( 1 )))
= 4 * ( 3 * ( 2 ))
= 4 * ( 6 )
= 24

---上面每一个=都代表将函数push到调用栈里面。---每一个=都代表将函数pop到调用栈里,直到最后一个值运算完成,才将结果return到函数调用前的环境里。

如果函数需要过多的调用栈,超出浏览器JS引擎的范围就会==爆栈==。

function f(n){
    return n != 1 ? n+f(n-1) : 1    // 相乘的值过大,运行相加来验证
}

6cf1d30f50220799b38d066420d94ed8.png

end.

语雀原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值