目录
一、递归
1.递归简介
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
2.递归三要素
(1)一个问题的解可以分解为几个更小的同一题的解
比如说下面的数列,如果要求解第 5的阶乘是多少 fac(5),可以分解为求4的阶乘fac(4)和5的乘积
我们来举个例子,我们可以用4的阶乘乘以5来定义5的阶乘,3的阶乘乘以4来定义4的阶乘,以此类推。
5!=5 *4!
4!=4 *3!
3!=3*2!
2! =2*1!
(2)分解后的子问题只存在数据不一样的问题。
比如说上面的数列,每次分解后,所形成的子问题求解方式都一样,只是说每次数据规模变了。
(3)存在递归终止条件
这个是必须存在的,把问题一层一层的分解下去,但是不能无限循环下去了。 比如说下面的数列,当 n 等于 21的时候,就会停止,此时就已经知道了第一个数和第二个数是多少了,这就是终止条件。不能像老和尚给小和尚讲故事那样,永无止境。
3.递归的应用
求5 的阶乘
//求5的阶乘
function fac(n) {
if (n == 1) {
return 1;
}
else {
return n * fac(n - 1);
}
}
console.log('5!=', fac(5));
斐波拉契数列前20项之和( 斐波拉契数列规律:该项=前两项之和)
//斐波拉契数列前20项之和 1,1,2,3,5,8……
function fac(n) {
if (n == 1||n==2) {
return 1;
}
else {
return fac(n-1)+fac(n-2);
}
}
console.log(fac(20));
var sum = 0;
for (var i = 1; i <=20; i++){
sum += fac(i);
}
console.log(sum);
二、作用域
-
作用域就是代码名字(一般是变量)在某个范围内起作用和效果。
-
作用:提高了程序逻辑的局部性, 增强了程序的可靠性,减少了名字冲突。
1、全局作用域(全局变量)
在函数外部定义的变量或在函数内部没有使用var声明的变量。在浏览器页面没有关闭之前 一直占用内存空间。比较耗费内存。在浏览器页面关闭时才释放内存
//全局变量
var stuName = "小刘";
//此处可以调用stuName变量
function fun() {
//函数内可以调用stuName 变量
}
2、局部作用域(局部变量)
在函数内部用var关键字定义的变量。只在函数内部起作用,函数调用结束后,局部变量所占的内存就会被释放。
//局部变量
//此处不可以调用stuName变量
function fun() {
var stuName = "小张";
//函数内可以调用stuName 变量
}
3、块级作用域
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
for(let i=0;i<100;i++){
}
4、作用域链
在每个执行上下文的变量环境中,都包含一个外部引用(成为outer),用来指向外部的指向上下文。当在查找一个变量的时候,如果在当前的变量环境中没有找到,js引擎会继续在outer所指向的执行上下文中查找,把这个查找的链条称为作用域链。(作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数。)
查找原则:
-
从当前的执行作用域开始查找变量;
-
如果在当前作用域中找不到该变量,则向上一级进行查找;
-
继续向上一层查找,直到最外层的全局作用域。
//作用域链
var num1 = 10;
function fun1() {
var num2 = 20;
function fun2() {
var num3 = 30;
console.log(num1 + num2 + num3);
}
fun2();
}
fun1();
【 总结】:
结合代码和上述执行上下文的流程图,当执行到console.log(num1 + num2 + num3),时会在fun2函数作用域中找到num3变量,在fun1函数作用域中找到变量num2,在全局作用域中找到变量num1,最终与该语句相关的三个变量均获取到。其中fun2函数作用域、fun1函数作用域、全局作用域就构成了一条作用域链。