函数
1.函数介绍
将一系列代码进行封装,并实现一定的功能,实现代码复用。
JavaScript定义函数的格式:
function 函数名称(形参列表)(){}; //直接声明,若没有函数名称,则为匿名函数
var foo=function(形参列表){}; //函数表达式
函数的作用:
功能封装,直接调用,提高代码复用率
2.函数声明提升
函数的声明和var变量声明类似,都会进行提升,提升到代码的最前边,即可以在声明函数前调用函数,函数声明提升优先于变量声明提升
var sum=add(2,3);//函数声明提升,sum=5
function add(a,b){
var result = a + b;
return result;//返回值
}
var total = add(1,2) //total=3
3.函数内部属性
在函数内部才能访问的属性,通过this也可在函数外部使用
arguments
是一个接收传入函数的所有参数的类数组对象,拥有length属性,但没有数组特有的属性
function add(a,b){
var result = a + b;
return result;//返回值
}
var total = add(1,2,3,4,5)// total=3
/*
arguments={
“0”:1,
“1”:2,
“2”:3,
"3":4,
"4":5
};
*/
其中callee属性是arguments对象的成员,callee指向正在被执行的function对象
//递归实现1~n的和
function sum(n){
if(n==1)
return 1;
else{
//return n+arguments.callee(n-1)
//即arguments.callee(n-1) == sum(n-1)
return n+sum(n-1);
}
}
this
this表示对当前对象的引用,会随着执行环境的不同而变化
-
在方法中,this指向该方法所属的对象
-
在全局使用时,this指向全局对象
-
在函数中使用,this指向全局对象
-
在事件中使用,this指向接收事件的元素
<button onclick="this.style.display='none'"> 点我后我就消失了 </button>
-
可以通过call、apply、bind改变this的指向
4.立即执行函数(IIFE)
表示立即调用的函数表达式,即声明函数的同时调用该函数
作用:
- 只执行一次的函数
- 函数中的变量只作用于函数作用域中,不会泄漏成全局变量
IIFE的格式:
//1.对返回结果不进行处理
(function(形参){
//代码
})(实参);
//2.返回的是一个布尔值,然后进行取反
!function(形参){
//代码
return (true/false);
}(实参)
//3.返回值若为数字,返回原来的结果,非数字返回NaN
+function(形参){
//代码
}(实参)
//4.返回值若为数字,返回原来结果的相反值,非数字返回NaN
-function(形参){
//代码
}(实参)
//5.返回值若为数字,返回原来结果的相反值-1,非数字返回-1
~function(形参){
//代码
}(实参)
//6.返回的结果是undefined
void function(形参){
//代码
}(实参)
为什么需要IIFE?
为了弥补JS(ES5)在作用域的缺陷,在ES6前JS只有全局作用域、函数作用域(在ES6后有了块级作用域),因此IIFE主要是实现作用域的隔离。
5.作用域
全局作用域:函数之外声明的变量,会成为全局变量,函数内部可以访问
函数作用域:函数中声明的变量,会成为函数的局部变量,函数外部不能访问
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突
经典案例
var a = 10;
function foo() {
// 当函数内部有变量a时,会产生局部作用域,外界全局作用域中的a不会对函数内部造成影响
// 如果把函数内部的变量a注释掉,函数内部的a输出的就是全局作用域中的a
//此时变量a声明提升,因此输出undefined
console.log(a); //undefined
var a = 100;
console.log(a); // 100
function fn() {
//同上
console.log(a); //undefined
var a = 200;
console.log(a); // 200
}
fn()
}
foo()
作用域链
自由变量
自由变量就是在全局中声明了该变量,并且在函数内部调用,但是在函数内部没有声明该变量
作用域链
层级关系,在函数内没有该变量就会向上一层寻找。
综合案例
作用域链-闭包-经典面试题
var a = 10
function fn() {
var b = 20
function bar() {
console.log(a + b) //30
}
return bar
}
var x = fn(),b = 200; // 执行fn() 返回的是bar
x() //执行x,就是执行bar函数
6.函数调用
调用方式
-
函数名(实参列表);
-
函数名.call(执行环境对象,实参列表);
-
函数名.apply(执行环境对象,实参列表数组);
-
函数名.bind(执行环境对象)(实参列表);
总结:call和apply都是改变上下文中的this指向并立即执行这个函数,bind方法需要等函数进行调用时才执行,并且参数可以在执行是添加,也可以在调用bind方法时添加。
7.函数的应用
1.作为回调函数
即先执行完主函数,再执行回调函数
作用:
回调函数主要用作耗时操作中,如ajax请求、处理文件
调用回调函数可以让主函数不用等待这些耗时操作
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B() {
// 模仿延时操作
setTimeout(() => {
console.log('我是回调函数');
}, 3000);
}
//调用主函数,将函数B传进去
A(B);
/*
输出:
我是主函数
我是回调函数
*/
2.作为返回值
将一个函数作为另一个函数的返回值
var a = 10
function fn() {
var b = 20
function bar() {
console.log(a + b) //30
}
return bar; //此时返回的就是bar函数
}
var x = fn(), // 执行fn() 返回的是bar
b = 200
x() //执行x,就是执行bar函数
8.闭包
闭包就是有权访问另一个函数作用域中的变量的函数
MDN 上面这么说:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成
生成闭包的条件:
-
函数嵌套函数
-
内部函数引用了外部函数中的数据(属性、函数)
-
参数和变量不会被回收
闭包的作用:
- 读取函数内部的变量
- 变量的值始终保持在内存中,不会被垃圾回收机制回收
闭包的缺点:
- 变量保存在内存中,内存消耗很大
- 不恰当的使用闭包可能会造成内存泄漏,所以不能滥用闭包
解决办法:
在退出函数之前,将不使用的局部变量全部删除,置空