高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。 最典型的就是作为回调函数。
同理函数也可以作为返回值传递回来
一、回调函数
回调函数:指的就是一个函数A作为参数传递给一个函数B,然后在B的函数体内调用函数A。此时,称函数A为回调函数。
例:
function cal(num1, num2, fn) {
return fn(num1, num2);
}
console.log(cal(45, 55, function (a, b) {
return a + b;
}));
//function(a,b)作为参数作为参数传递给一个cal() ,然后在cal()的函数体内调用function(a,b)。
//此时,称function(a,b)为回调函数。
//调用cal()函数,首先将num1 = 45,num2 = 55,function(a,b)的返回值是a+b,,
//cal的返回值是fn(44,45),即就是将fn的num1和num2调用到function(a,b)的a和b中,最后返回a+b=100
console.log(cal(10, 20, function (a, b) {
return a * b;
}));
//打印200
二、递归函数
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数。
递归函数的作用和循环效果一样。
由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return。
<script>
var num = 1;
function fn() {
console.log('我要打印6句话');
if (num == 6) {
return; // 递归里面必须加退出条件
}
num++;
fn();
}
fn();
</script>
根据用户的输入计算指定数据的阶乘:
function factorial(n) { // 定义回调函数
if (n == 1) {
return 1; // 递归出
}
return n * factorial(n - 1);
}
var n = prompt('求n的阶乘\n n是大于等于1的正整数,如2表示求2!。');
n = parseInt(n);
if (isNaN(n)) {
console.log('输入的n值不合法')
} else {
console.log(n + '的阶乘为:' + factorial(n));
}
递归输出斐波那契数列前20项:
function fib(n){
if (n==1||n==2) {
return 1
}else{
return fib(n-2)+fib(n-1)
}
}
for(var i=1;i<=20;i++){
console.log("第"+i+"项为"+fib(i))
}
三、作用域
变量需要在它的作用范围内才可以被使用,这个作用范围称为变量的作用域, JavaScript根据作用域使用范围的不同,划分如下:
- 全局变量:不在任何函数内声明的变量(显式定义)或在函数内省略var声明的变量(隐式定义)都称为全局变量
- 局部变量:在函数体内利用var关键字定义的变量称为局部变量
- 块级变量:ES6提供的let关键字声明的变量称为块级变量
全局变量
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
- 全局变量在代码的任何位置都可以使用
- 在全局作用域下 var 声明的变量 是全局变量
- 特殊情况下,在函数内不使用 var 声明的变量也是全局变量(不建议使用)
局部变量
在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
- 局部变量只能在该函数内部使用
- 在函数内部 var 声明的变量是局部变量
- 函数的形参实际上就是局部变量
两者的区别:
在全局作用域下,添加或省略var关键字都可以声明全局变量,全局变量在浏览器关闭页面的时候才会销毁,比较占用内存资源
在函数中,添加var关键字声明的变量是局部变量,省略var关键字时,如果变量在当前作用域下不存在,会自动向上级作用域查找变量。局部变量在函数执行完成后就会销毁,比较节约内存资源
全局作用域
- JavaScript作用域 : 就是代码名字(变量)在某个范围内起作用和效果 目的是为了提高程序的可靠性更重要的是减少命名冲突
- js的作用域 : 全局作用域 局部作用域 块级作用域
- 全局作用域: 整个script标签 或者是一个单独的js文件
var num = 10; var num = 30; console.log(num);
局部作用域
局部作用域(函数作用域) 在函数内部就是局部作用域 这个代码的名字只在函数内部起效果和作用
function fn() {
// 局部作用域
var num = 20;
console.log(num);
}
fn();
全局作用域和全局变量的区别
此处转自:全局作用域和全局变量的区别
var abc = '123'; // 全局变量,可以再全局使用,不能通过 delete 方法删除
abcd = '1234'; // 相当于挂载在全局变量 window 上的属性,具有全局作用域,但不是全局变量,可能被delete方法删除
function test() {
abce = '12345' // 相当于挂载在全局变量 window 上的属性,具有全局作用域,但不是全局变量,可能被delete方法删除
}
test()
console.log(abc, abcd, abce); // 123, 1234, 12345
delete abce; // 返回true
delete abcd; // 返回true
delete abc; // 返回false
console.log(abc); // 123
console.log(abcd); // undefind
console.log(abce); // undefind
作用域链
当在一个函数内部声明另一个函数时,内层函数只能在外层函数作用域内执行,在内层函数执行的过程中,若需要引入某个变量,首先会在当前作用域中寻找,若未找到,则继续向上一层级的作用域中寻找,直到全局作用域,称这种链式的查询关系为作用域链。
// 就近原则
var num = 10;
function fn() { // 外部函数
var num = 20;
function fun() { // 内部函数
console.log(num);//结果是20
}
fun();
}
fn();
var a = 1;
function fn1() {
var a = 2;
var b = '22';
fn2();
function fn2() {
var a = 3;
fn3();
function fn3() {
var a = 4;
console.log(a); //打印4
console.log(b); //打印22
}
}
}
fn1();
三、闭包函数
闭包(closure)指有权访问另一个函数作用域中变量的函数。 简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。
主要的两点用途如下:
- 可以在函数外部读取函数内部的变量
- 可以让变量的值始终保持在内存中
注意:由于闭包会使得函数中的变量一直被保存在内存中,内存消耗很大,所以闭包的滥用可能会降低程序的处理速度,造成内存消耗等问题。
function fn1(){ // fn1 就是闭包函数
var num = 10;
function fn2(){
console.log(num); // 10
}
fn2()
}
fn1();
闭包函数的实现
闭包的常见创建方式就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量可以在函数外部读取函数内部的变量。
function fn() {
var times = 0;
var c = function () { return ++times; };
return c;
}
var count = fn(); // 保存fn()返回的函数,count就是一个闭包
console.log(count()); // 输出结果:1
console.log(count()); // 输出结果:2
console.log(count()); // 输出结果:3
在 chrome 中调试闭包:
- 打开浏览器,按 F12 键启动 chrome 调试工具。
- 设置断点。
- 找到 Scope 选项(Scope 作用域的意思)。
- 当我们重新刷新页面,会进入断点调试,Scope 里面会有两个参数(global 全局作用域、local 局部作用域)。
- 当执行到 fn2() 时,Scope 里面会多一个 Closure 参数 ,这就表明产生了闭包。
四、预解析
JavaScript解析器在运行JavaScript代码的时候会进行预解析,也就是提前对代码中的var变量声明和function函数声明进行解析,然后再去执行其他的代码。
- 我们js引擎运行js 分为两步:预解析和代码执行
(1)预解析js引擎会把js 里面所有的 var 还有 function 提升到当前作用域的最前面
(2)代码执行按照代码书写的顺序从上往下执行 - 预解析分为:变量预解析(变量提升) 和 函数预解析(函数提升)
(1) 变量提升:就是把所有的变量声明提升到当前的作用域最前面,不提升赋值操作
(2) 函数提升:就是把所有的函数声明提升到当前作用域的最前面,不调用函数
console.log(num); // undefined 坑 1
var num = 10;
// 相当于执行了以下代码
var num;
console.log(num);
num = 10;
fun(); // 报错 坑2
var fun = function() {
console.log(22);
}
// 函数表达式 调用必须写在函数表达式的下面
// 相当于执行了以下代码
var fun;
fun();
fun = function() {
console.log(22);
}
var a = 18;
f1();
function f1() {
var b = 9;
console.log(a);//undefined 坑3,老是忘记
console.log(b);//9
var a = '123';
}
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;//相当于var a ; a = b = c = 9;
// b和c直接赋值,没有var声明,相当于 全局变量
//如果要集体声明,则应该var a = 9, b = 9, c = 9;
console.log(a);
console.log(b);
console.log(c);
}