递归函数
递归函数:在函数内部调用自身,一般解决数学问题。
菲波那切数列:1,1,2,3,,5,8,13,21,34……
function feibo(n) {
// 用户输入第n项,返回对应的值
// 从第3项开始值等于n-1对应的值+n-2对应的值
if(n === 1 || n == 2) {
return 1;
}else {
return feibo(n-1) + feibo(n-2);
}
}
console.log(feibo(1));
console.log(feibo(2));
console.log(feibo(3));
console.log(feibo(4));
console.log(feibo(5));
console.log(feibo(50));
变量的作用域
函数的内部声明的变量,只能在函数内部使用,在函数外部任何地方都不能访问。
// 在函数内部声明的变量只能在函数内部使用
function fun() {
// a在函数内部声明的,只能在fun函数内部使用
var a = 10;
console.log(a);
}
// a在函数外部不能使用
console.log(a);
对于js。只有函数能够关住变量的作用域。
局部变量和全局变量
局部变量:在一个作用域(定义域)内定义的变量就是这个作用域内的局部变量。只能在作用域内被访问到。
全局变量:从广义上来看,全局变量也是一种局部变量。全局变量定义在全局,所以也叫全局变量。可以在任何地方都被访问到。
变量申明的原理:全局变量,在全局定义之后,会永久存在,任何时候,任何位置访问,都能够找到它。局部变量定义在函数内部的,函数定义的过程,并没有真正的去定义这个局部变量,只有在执行函数的时候,才会立即定义这个局部变量,执行完之后,变量就被立即销毁了,在其他的地方访问变量的时候,找不到这个变量,所以会有一个引用错误,变量未定义。
// 在全局范围内定义的变量
var a = 10;
// 函数
function fun() {
// 函数内部可以使用全局a
console.log(a);
// b在函数内部声明,只能在函数内部使用,b局部变量
var b = 20;
console.log(++ b);
}
// 在函数内部声明b,在函数外部不能使用
// console.log(b);
fun();
形式参数是局部变量
形参也是局部变量,只能在函数内部使用,在外部任何地方都不能访问。
// 形参是局部变量
function fun(a,b,c) {
console.log(a);
console.log(b);
console.log(c);
}
// 调用
fun(10,20,30,40,50);
全局变量作用
1传递作用。在不同函数间使用全局变量可以作用信号量,这些函数都可以改变信号量,使用新值参与计算。
// a 是全局变量,任何地方都可以使用
// a称为信号量,不管任何函数改变a的值,都可以记住并且再次使用都是使用a改变之后的新值参与计算
var a = 10;
// 函数使用a
function fun() {
a ++;
console.log(a);
}
// 函数也可以使用a
function fun2() {
a --;
console.log(a);
}
2 通信作用,在同一个函数使用全局变量,不会每次都清空,也是使用当前的新值参与计算。
// 使用全局变量
var a = 1;
// 声明函数
function fun() {
console.log(++ a);
}
fun();
fun();
fun();
fun();
fun();
fun();
fun();
fun();
// 输出新值
console.log(a);
作用域链
指的是我们变量查找的一个规律:我们可以在不同的作用域内使用相同的标识符去命名变量。我们在使用一个变量的时候,需要找到匹配的标识符,我们有重复的,用哪一个?如果在当前作用域有这个变量,就直接使用,如果当前作用域没有这个变量定义,会一层一层的从本层往外依次查找,遇到第一个就直接使用。类似于就近原则。
当遇见一个变量时,JS引擎会从其所在的作用域依次向外层查找,查找会在找到第一个匹配的标识符的时候停止。在多层嵌套的作用域中可以定义同名的标识符,发生“遮蔽效应”。
// 全局a
var a = 10;
function fun1() {
var a = 20;
// 本层作用域有变量直接使用20
console.log(a);
function fun2() {
// 本层作用域没有a的定义,从本层出发,向外进行查找20
console.log(a);
function fun3() {
// 本层有a定义直接使用
var a = 30;
console.log(a);
}
fun3();
}
fun2();
}
// 调用
fun1();
console.log(a);
如果变量声明时,不写var关键字,计算机会自动在全局作用域内给它进行一个声明,局部变量就强制性的变成了全局变量。这种情况是不合理,会造成一个全局变量的污染。所以,定义变量必须写var关键字。
// 全局a
var a = 10;
// 等价于在全局声明var a = 20;
function fun1() {
// 函数内部的var 省略,强制转为全局变量
a = 20;
console.log(a);
function fun2() {
console.log(a);
}
fun2();
}
fun1();
console.log(a);
函数的作用域
在函数内部声明的函数,只能在函数内部声明,在函数外部任何地方都不能访问。
// 声明函数
function outer() {
// 函数内部声明一个变量
var a = 10;
// 函数内部声明一个函数
function inner() {
console.log(a);
}
// 调用函数
inner();
}
// 调用函数
outer();
// inner在函数外部不能使用
inner();
闭包
函数本身就是闭包,函数可以记住自己定义时所处的外部环境,和内部语句,这就是闭包。
function outer() {
var a = 10;
function inner() {
console.log(a);
}
// console.log(inner);
// inner没有小括号。表示定义语句
return inner;
}
// 调用outer返回的是Inner函数的定义
// console.log(outer());
// 用i变量接收outer(),相当于把inner函数赋值i
var i = outer();
i();
对于inner函数,外部环境var a = 10;内部语句console.log(a),不管我们把inner函数拿到哪里它都可以记住自己的所处的外部环境(变量的作用域)和自己的内部语句。
闭包是函数天生存在的性质,不需要任何结构,只不过我们需要借助这种形式体会到闭包。
// 形参是局部变量
function outer(x) {
function inner(y) {
// 当前作用域没有x的定义,使用outer中x
console.log(x + y);
}
// 返回inner定义
return inner;
}
// 将outer调用赋值给i,相当于将inner定义赋值的i
//类似于把inner拿到外部使用,能够记住自己定义时所处的外部环境x=10
//类似于把inner拿到外部使用,能够记住自己定义时内部语句console.log(10+ y)
//i = function inner(y) {console.log(10 + y)}
var i = outer(10);
// 调用i函数
i(20,30,40);
// 声明一次函数i可以多次调用
i(100);
// 闭包:函数能够记住定义时的外部环境和内部语句
function outer(x,y) {
function inner(y) {
// 作用域链根据就近原则
// console.log(y);
console.log(x + y);
}
// 调用outer(x,y)返回的是inner函数
return inner;
}
// i赋值的是inner函数
// outer(1,2) x = 1, y =2
var i = outer(1,2);
// i是inner函数名字,调用i函数,i的实际参数是赋值给inner函数y
// i(10)调用,能够记住x = 1,还可以内部语句console.log(1 + y)
i(10);
i(20);
闭包并不是一成不变的,函数的每一次调用都会产生一个全新的闭包。
举例:
function fun1() {
var a = 10;
function fun2() {
a ++;
console.log(a);
}
// 调用fun1返回的是fun2的定义
return fun2;
}
// 每一次fun1调用都产生一个全新的闭包,之间互不影响
var inn1 = fun1();
var inn2 = fun1();
// 闭包的外部环境不是一成不变的,能够记住当前的新值,并且参与计算
inn1();
inn1();
inn1();
inn1();
// 每一次fun1调用都产生一个全新的闭包,inn1和inn2闭包互不影响
// 每一个闭包,外部环境是全新的内部语句也是全新的
inn2();
inn2();
inn1();