数组
1、为什么学习数组?
- 之前学习的数据类型,只能存储一个值(比如:Number/String)。如果我们想存储班级中 所有学生的成绩,此时该如何存储?
- 概念: 所谓
数组(Array),就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中, 那么这个集合我们就称之为数组。
2、数组的定义
- 数组是一组有序的数据集合。数组内部可以存放多个数据,不限制数据类型,并且数组的 长度可以动态的调整。
- 创建数组最简单的方式就是数组字面量方式。
- 数组的字面量:
[] - 一般将数组字面量赋值给一个变量,方便后期对数组进行操作。
- 如果存放多个数据,每个数据之间
用逗号分隔,最后一个后面不需要加逗号。
3、获取数组元素
数组可以通过一个 index(索引值、下标)去获取对应的某一项的数据,进行下一步操作。- index: 从0开始,按照整数排序往后顺序排序,例如0,1,2,3......
- 可以通过 index 获取某一项值之后,使用或者更改数组项的值。
- 调用数据:利用数组变量名后面直接加 [index] 方式。
// 创建一个空数组
var arr = [];
// 创建包含多个数据的数组,数据类型是不限制
var arr2 = [1,true,false,null,undefined,"haha",[7,8],9,10];
console.log(arr2);
// 获取 arr2 中下标为 0 的项
console.log(arr2[0]);
console.log(arr2[1]);
console.log(arr2[2]);
console.log(arr2[6]);
console.log(arr2[8]);
注意:
- 如果索引值超过了数组最大项,相当于这一项没有赋值,内部存储的就是 undefined。
- 更改数据:arr[index] 调用这一项数据,后面等号赋值更改数据。
//length长度为9,第4个值就是下标为5
var arr2 = [1,true,false,null,undefined,"haha",[7,8],9,10];
// 更改数组中某一项的值
arr2[4] = 5;
console.log(arr2);
所以输出结果为1,true,false,null,5,"haha",[7,8],9,10
4、数组的长度
- 数组有一个 length 的属性,记录的是数组的数据的总长度。
- 使用方法:变量名.length
console.log(arr.length); - 数组的长度与数组最后一项的下标存在关系,最后一项的下标等于数组的 length-1。
- 获取最后一项的数据时,可以这样书写:
console.log(arr[arr.length-1]); - 数组的长度不是固定不变的,可以发生更改。
var arr2 = [1,true,false,null,5,"haha",[7,8],9,10];
// 获取数组的长度为 9
console.log(arr2.length);
//可以通过长度-1,获得最后一个数字的下标 8
console.log(arr2[arr2.length - 1]);
- 增加数组长度:直接给数组 length 属性赋一个大于原来长度的值。赋值方式使用等号赋值。
// 更改数组的长度为 12,现在有11个数
arr2.length = 12;
console.log(arr2.length);
console.log(arr2[11]);
// 12值无,undefined
console.log(arr2[12]);
- 或者,可以给一个大于最大下标的项直接赋值,也可以强制拉长数组。
// 给下标为13的赋值 13,所以length为 14
arr2[13] = 13;
//输出数组arr的值
console.log(arr2);
结果为: (14) [1, true, false, null, 5, 'haha', Array(2), 9, 10, empty × 4, 13]
//同时计算arr2 的长度
console.log(arr2.length); 14
console.log(arr2[12]); 9-12的值都是undefined
- 缩短数组长度:强制给 length 属性赋值,后面数据被会直接删除,删除是不可逆的。
var arr2 = [1,true,false,null,5,"haha",[7,8],9,10];
arr2.length = 5;
console.log(arr2);
结果为:(5) [1, true, false, null, 5]
arr2.length = 9;
console.log(arr2);
结果为:(9) [1, true, false, null, undefined, empty × 4]
5、数组遍历
- 遍历:遍及所有,对数组的每一个元素都访问一次就叫遍历。利用 for 循环,将数组中的每 一项单独拿出来,进行一些操作。
- 根据下标在 0 到 arr.length-1 之间,进行 for 循环遍历。
// 定义一个数组
var arr = [45,56,76,88,89,90,100,34,56];
// 数组遍历
for (var i = 0 ; i <= arr.length - 1 ;i++) {
console.log(arr[i]);
}
// 给数组中每一项数据加 5
for (var i = 0; i < arr.length ; i++) {
// 获取每一项数组的数据,等号赋新值
arr[i] += 5;
}
console.log(arr);
结果为:(9) [50, 61, 81, 93, 94, 95, 105, 39, 61]
应用案例
求一组数中的所有数的和以及平均值。
// 定义一个数组
var arr = [45,56,76,88,89,90,100,34,56];
// 累加器。累积数组每一项的和,初始值是 0
var sum = 0;
// 数组遍历,将每一项累加到 sum 里
for (var i = 0 ; i <= arr.length - 1 ; i++) {
sum += arr[i];
}
// 输出 sum
console.log("这组成绩的总和是" + sum);
// 求取平均值 = 总和 / 班级人数
var avg = sum / arr.length ;
console.log("这个班的平均成绩是" + avg);
函数
1、为什么要用函数?函数的概念
- 如果要在多个地方求某个数的约数个数,应该怎么做?
- 函数(function),也叫作功能、方法,函数可以将一段代码一起封装起来,被封装起来的 函数具备某一项特殊的功能,内部封装的一段代码作为一个完整的结构体,要执行就都执 行,要不执行就都不执行。
- 函数的作用就是封装一段代码,将来可以重复使用。
// 定义函数
function fun() {
console.log(1);
console.log(2);
console.log(3);
console.log(4);
}
// 调用函数
fun();
fun();
fun();
fun();
2、函数声明
- 函数声明又叫函数定义,函数必须先定义然后才能使用,函数声明的时候,函数体并不会执行,只有当函数被调用的时候才会执行。
- 如果没有定义函数直接使用,会出现一个引用错误。
//函数声明语法:
function 函数名(参数){ 封装的结构体;
}
3、函数调用
调用方法:函数名();- 函数调用也叫作函数执行,调用时会将函数内部封装的所有的结构体的代码立即执行。
- 函数内部语句执行的位置,与函数定义的位置无关,
与函数调用位置有关。 - 函数可以一次定义,多次执行。
// 函数必须先定义才能使用
// 函数名命名规则:可以使用字母、数字、下划线、$,数字不能作为开头,区分大小写,不能使用关键字和保留字
// 函数声明
function fun() {
console.log(1);
console.log(2);
console.log(3);
console.log(4);
}
console.log(5);
// 函数调用
fun();
fun();
所以输出结果: 5 1 2 3 4 1 2 3 4
// 函数声明
function fun() {
console.log(1);
console.log(2);
console.log(3);
console.log(4);
}
// 函数调用
fun();
fun();
console.log(5);
所以输出结果: 1 2 3 4 1 2 3 4 5
4、函数的参数 1
- 我们希望函数执行结果不是一成不变的,可以根据自定义的内容发生一些变化。
- 函数预留了一个接口,专门用于让用户自定义内容,使函数发生一些执行效果变化。
- 接口:就是函数的参数,函数参数的本质就是变量,可以接收任意类型的数据,导致函数执行结果根据参数不同,结果也不同。
- 一个函数可以设置 0 个或者多个参数,参数之间用逗号分隔。
函数的参数 2
- 函数的参数根据书写位置不同,名称也不同:
- 形式参数:定义的 () 内部的参数,叫做形式参数,本质是变量,可以接收实际参数传递过来的数据。简称形参。
- 实际参数:调用的 () 内部的参数,叫做实际参数,本质就是传递的各种类型的数据,传递给每个形参,简称实参。
- 函数执行过程,伴随着传参的过程:
函数的参数优点
- 不论使用自己封装的函数,还是其他人封装的函数,只需要知道传递什么参数,执行什么 功能,没必要知道内部的结构什么。
- 一般自己封装的函数或者其他人封装的函数需要有一个 API 接口说明,告诉用户参数需要 传递什么类型的数据,实现什么功能。
// 定义一个求和函数,传入两个数据
// 参数:传两个参数,数据类型为数字
// 功能:得到两个数字之和
function sum(a,b) {
console.log(a + b);
}
// 调用函数中,给小括号内部添加数据
sum(3,4);
sum("3",4);
5、函数的返回值
- 函数能够通过参数接收数据,也能够将函数执行结果返回一个值。
- 利用函数内部的一个 return 的关键字设置函数的返回值。
- 作用 1: 函数内部如果结构体执行到一个
return 的关键字,会立即停止后面代码的执行。
// return 可以终止函数的执行
function fun() {
console.log(1);
console.log(2);
console.log(3);
return;
console.log(4);
console.log(5);
console.log(6);
输出结果:没有return的时候是1 2 3 4 5 6
有return的时候是1 2 3
}
- 作用 2: 可以在 return 关键字后面添加空格,空格后面任意定义一个数据字面量或者表达式,函数在执行完自身功能之后,整体会被 return 矮化成一个表达式,表达式必须求出一个值继续 可以参与程序,
表达式的值就return 后面的数据。
// 定义一个求和函数,传入两个数据
// 参数:传两个参数,数据类型为数字
// 功能:得到两个数字之和
// 使用返回值,制作函数运行结束后的结果
function sum(a,b) {
return a + b;
}
// return返回值就可以参与程序了,调用函数结果
console.log(sum(1,2));
//错误写法:sum(1,2); 因为sum没有书写在任何输出语句里,所以不能输出
函数的返回值应用
- 函数如果有返回值,执行结果可以当成普通数据参与程序。例如:上述代码块
- 函数如果有返回值,可以作为一个普通数据赋值给一个变量,甚至赋值给其他函数的实际参数。
// 使用返回值,制作函数运行结束后的结果
function sum(a,b) {
return a + b;
}
// 将返回值赋值给变量
var num = sum(3,4);
console.log(num);
输出结果:7
// 将返回值赋值给函数的实参
console.log(sum(2,sum(3,4)));
输出结果:9
注意
如果函数没有设置 return 语句 ,那么函数有默认的返回值 undefined;如果函数使用return 语句,但是 return 后面没有任何值,那么函数的返回值也是 undefined。
控制台输入alert(2),会出现一个alert警示框2,再在控制台返回一个值undefined.
6、函数表达式
- 函数表达式是函数定义的另外一种方式。
- 定义方法:就是将函数的定义、匿名函数赋值给一个变量。
- 函数定义赋值给一个变量,相当于将函数整体矮化成了一个表达式。
// 定义一个函数表达式 function...,既然他是一个表达式,那么他最后肯定是会得到一个结果,然后将结果赋值给foo
var foo = function fun() {
console.log(1);
};
- 匿名函数:函数没有函数名fun。
- 调用函数表达式,方法是给变量名加()执行,不能使用函数名加()执行。
// 定义一个函数表达式
var foo = function fun() {
console.log(1);
};
var foo2 = function () {
console.log(2);
};
// 调用函数式,只能用变量名调用,函数名调用不成功,fun是函数名
// fun();
foo(); 结果为1
foo2(); 结果为2
7、函数的数据类型
- 函数是一种单独的数据类型 Function。
// 定义一个函数
function fun() {
console.log(1);
}
// 定义一个函数表达式
var foo = function () {
console.log(2);
};
// 检测函数的数据类型
console.log(typeof(fun));
console.log(typeof(foo));
输出结果都是function
- 由于函数是一种数据类型,可以参与其他程序。
- 例如,可以把函数作为另一个函数的参数,在另一个函数中调用。
// 函数是一种数据类型,可以当成其他函数的参数.
setInterval是定时器,定时器里面有2个参数,第一个参数是函数,第2个参数是时间1000毫秒
setInterval(function(){
console.log(1);
},1000);
- 或者,可以把函数可以作为返回值从函数内部返回。
// 将函数当成另一个函数的返回值
function fn(b) {
var a = 10;
return function () {
alert(a + b);
};
}
8、arguments 对象
- JavaScript 中,arguments 对象是比较特别的一个对象,实际上是当前函数的一个内置属性 。也就是说所有函数都内置了一个arguments 对象,arguments对象中存储了传递的所有的实参。arguments 是一个伪数组,因此及可以进行遍历。
- 函数的实参个数和形参个数可以不一致,所有的实参都会存储在函数内部的 arguments 类数组对象中。
// 定义一个函数
function sum(a,b) {
return a + b;
}
// 调用函数的时候,实参的个数可以与形参不同
console.log(sum(1,2));
console.log(sum(1));
console.log(sum(1,2,3,4));
// 函数内部有一个 arguments 对象,会接收所有的实参
function fun() {
console.log(arguments);
console.log(arguments.length);
// 使用数组的遍历方法可以获取每一项实参
for (var i = 0 ; i <= arguments.length - 1 ; i++) {
console.log(arguments[i]);
}
}
// // 调用函数
fun(1,2,3,4,5,6,7);
案例
- 定义一个求和函数,如果传入 1 个参数,返回它自己,如果传入两个参数,返回他们的和 ,如果传入三个参数,先比较前两个的大小,大的与第三个参数求和返回,如果传入 4 个 及以上,输出错误提示。
// 案例:定义一个求和函数,如果传入 1 个参数,返回它自己,如果传入两个参数,返回他们的和,
如果传入三个参数,先比较前两个的大小,大的与第三个参数求和返回,如果传入 4 个及以上,输出错误提示。
function sum(a,b,c) {
// 条件分支语句,根据实参个数走不同的分支
switch (arguments.length) {
case 1:
return a;
break;
case 2:
return a + b;
break;
case 3:
return a > b ? a + c : b + c ;
break;
default:
// 提示用户,实参个数传递错误
// 模拟控制台报错
throw new Error("参数个数不能超过 3 个");
}
}
// 调用函数
console.log(sum(1)); 1
console.log(sum(1,2)); 3
console.log(sum(1,2,3)); 5
console.log(sum(1,2,3,4,5)); 报错参数不能超过3个
9、函数递归
- 函数内部可以通过函数名调用函数自身的方式,就是函数递归现象。
- 递归的次数太多容易出现错误:超出计算机的计算最大能力。
// 函数,如果 传入的参数是1,返回1,如果传入的是1以上的数字,让他返回参数 + 函数调用上一项
function fun(a) {
if (a === 1) {
return 1;
} else {
return a + fun(a - 1);
}
}
// 调用函数(传一些值给形参)
console.log(fun(1)); 1
console.log(fun(2)); 3
console.log(fun(3)); 6
console.log(fun(1000000000)); 报错,超出计算机的计算最大能力。
- 更多时候,使用递归去解决一些数学中的现象。 • 例如可以输出斐波那契数列的某一项的值。
// 菲波那切数列
// 参数:正整数
// 返回值:对应的整数位置的菲波那切数列的值
function fibo(a) {
if (a === 1 || a === 2) {
return 1;
} else {
return fibo(a - 1) + fibo(a - 2);
}
}
// 调用函数
console.log(fibo(1));
console.log(fibo(2));
console.log(fibo(3));
console.log(fibo(4));
console.log(fibo(5));
console.log(fibo(6));
10、作用域
- 作用域:变量可以起作用的范围。
- 如果变量定义在一个函数内部,只能在函数内部被访问到,在函数外部不能使用这个变量,函数就是变量定义的作用域。
//全局变量
var a = 0;
// 定义函数
function fun() {
var a = 1; //局部变量
console.log(a);
}
// 执行函数(只有调用控制台才会有结果,此时结果为1)
fun();
执行函数过程中,会在页面里面开辟出一个暂时的内存空间,有a的定义,console语句的执行,fun()在调用函数的过程之中,a是可以被访问到的,一旦函数执行完毕之后,就会瞬间销毁作用域内的变量a,因此在作用域外是调用不到的;
但是全局变量是始终存在,定义的变量不会很快被销毁,直到浏览器被关掉
在函数外部不能使用这个变量
// 定义函数
function fun() {
var a = 1;
console.log(a);
}
// 函数外部调用 a
console.log(a);
输出结果没有,并且会报错
- 任何一对花括号 {} 中的结构体都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
- 在es5之前没有块级作用域的的概念,只有函数作用域,现阶段可以认为 JavaScript 没有块级作用域.
全局变量和局部变量
- 局部变量:定义在函数内部的变量,只能在函数作用域内部被访问到,在外面没有定义的。
- 全局变量:从广义上来说,也是一种局部变量,定义在全局的变量,作用域范围是全局,在整个 js 程序任意位置都能够被访问到。
- 变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁。
参数也是局部变量
- 函数的参数本质是一个变量,也有自己的作用域,函数的参数也是属于函数自己内部的局 部变量,只能在函数内部被使用,在函数外面没有定义。
// 函数的参数也是局部变量
function fun(a) {
a = 2;
console.log(a);
}
// 调用函数,给函数参数传一个1的值
fun(1);
console.log(a); //如果在外部调用a,调用不到会报错
最后作用域中的局部变量a=2覆盖了a=1,所以fun(1)输出结果为2
函数的作用域
- 函数也有自己的作用域,定义在哪个作用域内部,只能在这个作用域范围内被访问,出了作用域不能被访问的。
- 函数定义在另一个函数内部,如果外部函数没有执行时,相当于内部代码没写。
如果函数定义在了全局,那么函数的作用域就是全局,在任何位置都可以调用函数
如果定义在了函数内部,那么作用域范围就会被限制在函数的内部,在函数内部可以正常调用,出了函数就不能被调用了
// 函数也有自己的作用域
function outer() {
var a = 1;
function inner() {
console.log(2);
}
// 函数内部调用子函数才能成功
inner();
}
// 外部调用函数inner不可行,outer()倒是可以输出结果
outer();
inner();
11、作用域链
- 只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡 是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个 作用域中就又可以诞生一个作用域。
- 将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。
// 全局作用域
var a = 1;
// 创建函数
function outer() {
var a = 2;
// 内部函数
function inner() {
var a = 3;
console.log(a);
}
//要想让inner函数执行成功,必须执行inner(),要在outer函数内的inner函数执行;
inner();
}
//调用outer:要想让outer函数执行成功,必须在自己所处的作用域执行outer
outer()
// 全局作用域
var a = 1;
// 创建函数
function outer() {
var a = 2;
// 内部函数
function inner() {
var a = 3;
console.log(a);
}
inner();
console.log(a);
}
// 调用
outer();
inner函数内部的console.log(a)的值的时候,首先是在自己的作用域链里面找,会查找到一个var a = 3,就会在本层直接执行; 不会往外找,如果找不到就会一直向上级链开始查找.
遮蔽效应
程序在遇到一个变量时,使用时作用域查找顺序,不同层次的函数内都有可能定义相同名 字的变量,一个变量在使用时,会优先从自己所在层作用域查找变量,如果当前层没有变 量定义会按照顺序从本层往外依次查找,直到找到第一个变量定义。整个过程中会发生内 层变量遮蔽外层变量的效果,叫做“遮蔽效应”。
// 全局作用域
var a = 1;
// 创建函数
function outer() {
var a = 2;
// 内部函数
function inner() {
// var a = 3; 把本层的变量等于3注释掉,那么本层作用域就查找不到a,
console.log(a); 那么就会跳出一级往上一级开始查找,遇到outer函数内 var a= 2,就会立即执行,
把上级的a遮蔽掉了。
} 如果上级作用域都没有a的值,这时候console.log(a)就是结果报错提示a没有被定义
inner();
}
// 调用
outer();
12、不写 var 关键字的影响
- 在函数内部想要定义新的变量,如果不加关键字 var,相当于定义的全局变量。如果全局也 有相同的标识符,会被函数内部的变量影响,局部变量污染全局变量。
- 注意:每次定义变量时都必须写 var 关键字,否则就会定义在全局,可能污染全局。
不写a,也可以被调用
a = 1;
console.log(a);
// 创建函数
//var a; 补全outer函数a=2
function outer() {
a = 2;
// 内部函数
function inner() {
var a = 3;
console.log(a);
}
inner();
console.log(a);
}
// 调用
outer();
console.log(a); 输出结果为2,因为a=2 ,没写var就相当于a=2 ,是全局变量,就相当于在全局把定义补全了一样,在全局作用域里var a;
// 全局作用域
var a = 1;
// console.log(a);
// 创建函数
function outer() {
a = 2;
// 内部函数
function inner() {
var a = 3;
console.log(a);
}
inner();
console.log(a);
}
// 调用
outer();
console.log(a); 如果内部函数outer 内部变量没写var ,他会自动到外部的全局变量补全,从而覆盖一开始的全局变量var a = 1;

13、预解析和声明过程
预解析
- JavaScript 代码的执行是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器执行 JavaScript 代码的时候,分为两个过程:预解析过程和代码执行过程。
预解析过程:
-
- 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
-
- 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
-
- 先提升 var,再提升 function。 JavaScript的执行过程:在预解析之后,根据新的代码顺序,从上往下按照既定规律执行js 代码。
变量声明提升
- 在预解析过程中,所有定义的变量,都会将声明的过程提升到所在的作用域最上面,在 将来的代码执行过程中,按照先后顺序会先执行被提升的声明变量过程。
- 提升过程中,只提升声明过程,不提升变量赋值,相当于变量定义未赋值,变量内存储 undefined 值。
- 因此,在 js 中会出现一种现象,在前面调用后定义的变量,不会报错,只会使用 undefined 值。
//预解析,把变量声明部分提升到了作用域最顶部
var a; //相当于存了一个undefined的值
// 调用一个变量
console.log(a);
// 后定义变量
//var a = 1;变成下面的a=1;
a = 1;
输出结果:undefined (按照之前的先定义变量后声明过程,得到结果为1;
或者只定义没有赋值的情况也会存一个undefined值)
undefined的原因是:在JS执行过程会进行一个预解析,预解析把变量声明提升到了作用域最顶部,就是把var a;放在了最顶部,相当于原来的位置就是一个a = 1;
函数声明提升
- 在预解析过程中,所有定义的函数,都会将声明的过程提升到所在的作用域最上面,在 将来的代码执行过程中,按照先后顺序会先执行被提升的函数声明过程。
- 在预解析之后的代码执行过程中,函数定义过程已经在最开始就会执行,一旦函数定义 成功,后续就可以直接调用函数。
- 因此,在 js 中会出现一种现象,在前面调用后定义的函数,不会报错,而且能正常执 行函数内部的代码。
// 预解析,把变量,函数声明部分提升到了作用域最顶部
// 模拟提升
var a; //相当于存了一个undefined的值
function fun() {
console.log(a);
}
// 先调用函数
fun();
// 定义函数
//function fun() {
//console.log(2);
}
// 调用
fun();
提升顺序
- 预解析过程中,先提升 var 变量声明,再提升 function 函数声明。
- 假设出现变量名和函数名相同,那么后提升的函数名标识符会覆盖先提升的变量名,那么在后续代码中出现调用标识符时,内部是函数的定义过程,而不是 undefined。
- 如果调用标识符的过程在源代码函数和变量定义后面,相当于函数名覆盖了一次变量名,结果在执行到变量赋值时,又被新值覆盖了函数的值,那么在后面再次调用标识符,用的就是变量存的新值。
- 建议:不要书写相同的标识符给变量名或函数名,避免出现覆盖。
上述代码,最终声明是开始的「变量声明fun」被「定义声明fun」替代,
调用结果为console.log(a)还是undefined,
如果没有var fun = "haha";但是函数fun不会报错,结果为2;
报错原因:变量的赋值又把fun函数覆盖了一次,字符串是不会调用函数执行方法的。
函数表达式的提升
- 在预解析过程中,函数表达式进行的是变量声明提升,而不是函数声明提升。提升后变 量内部存的是一个 undefined。在前面进行函数方法调用,数据类型会提示错误。
- 建议:定义函数时,最好使用 function 关键字定义方式,这样函数声明提升可以永远生效。
//2种调用结果测试函数表达式是哪种声明提升
// 第一种是变量声明提升,可以输出结果为undefined;
console.log(foo);
// 第二种是函数声明提升,输出结果报错“fun is not a function”
foo();
// 函数表达式进行的是变量声明提升
var foo = function () {
console.log(3);
};
函数声明提升的应用
- 函数声明提升可以用于调整代码的顺序,将大段的定义过程放到代码最后,但是不影响 代码执行效果。
13、IIFE 自调用函数
- IIFE:immediately-invoked function expression,叫做即时调用的函数表达式,也叫做自 调用函数,表示函数在定义时就立即调用。
- 函数调用方式:函数名或函数表达式的变量名后面加 () 运算符。
- 函数名定义的形式不能实现立即执行自调用,函数使用函数表达式形式可以实现立即执行,原因是因为函数表达式定义过程中,将一个函数矮化成了一个表达式,后面加()运算符就可以立即执行。
- 启发:如果想实现 IIFE,可以想办法将函数矮化成表达式。
// 关键字定义的方式,不能立即执行(错误写法)
function fun() {
console.log(1);
}();
// 函数表达式方式,可以在定义时被立即执行
var foo = function fun() {
console.log(2);
}();
- 函数矮化成表达式,就可以实现自调用。
- 函数矮化成表达式的方法,可以让函数参与一些运算,也就是说给函数前面加一些运算符。
- 数学运算符:+ - ()
- 逻辑运算符:!非运算
// 通过在函数前面添加操作符,可以将函数矮化成表达式
+ function fun() {
console.log(1);
}();
- function fun() {
console.log(1);
}();
(function fun() {
console.log(1);
})();
!function fun() {
console.log(1);
}();
// *function fun() {
// console.log(1);
// }();
// IIFE 关住了函数的作用域,在外面是调用不了函数的
// fun();
IIFE结构可以关住函数的作用域,可以创建一个自调用的函数,封住内部的函数,在结构外面是不能调用函数的。IIFE最常用的是()运算符,而且函数可以不写函数名,使用匿名函数。
// 常用的 IIFE 结构
(function (a) {
console.log(a);
})(4);
//再调用a,调用不了作用域被关起来了,报错
// console.log(a);