基本定义方式
//自定义函数:
function fun(args){
//do something...
}
//匿名函数:
var fun = function(args){
//do something...
}
//上述两种定义方式的调用方式:
//1、函数名();
//2、事件调用
//3、自执行
//构造函数:
function Fun(args){} //函数名通常首字母大写
//调用方式:
var f = new Fun(args);
函数参数
- 形参:出现在函数定义中
- 实参:函数调用时传递的参数
- 形参个数大于实参个数,
多余形参默认值为undefined - 实参个数大于形参个数,
多余的实参自动舍去
function fun(arg1,arg2) {
console.log(arg1); //1
console.log(arg2); //undefined
}
fun(1);
arguments
代表所有的实参
- 只能在函数体内部使用
- 可通过
arguments.length获取实参个数,函数名.length获取形参个数 - arguments代表的是一个集合,所以可以通过
arguments[下标]的方式取某个值,下标从0开始 arguments.callee代表函数名本身
function fun(arg1, arg2, arg3) {
console.log(arguments.length); //5
console.log(arguments[1]); //2
console.log(arguments.callee);
}
console.log(fun.length); //3
fun(1, 2, 3, 4, 5);
//arguments.length应用:
function fun(arg1, arg2, arg3) {
if (arguments.length < fun.length) {
throw new Error('缺少参数');
}
}
fun(1, 2); //缺少参数
//arguments.callee应用
function recursiveFunction(num) {
console.log(num); //5,6,7,8,9,10,11
if (num <= 10) {
//使用arguments.callee调用自身而非本身的函数名,防止因修改函数名导致定义的函数名与调用的函数名不一致的bug
arguments.callee(num + 1);
}
}
recursiveFunction(5);
return
用于函数体中返回某个值
function sum(a, b) {
return a + b;
}
let num = sum(1, 2);
console.log(num); //3
箭头函数(匿名函数的另一种写法)
- 内部没有arguments
- 内部的this是继承上下文中的this,所以可以当作箭头函数没有this
- 因为没有this,所以不能处理成员,自然不能作为构造函数使用
//基本写法:
let fun = (a,b) => {
console.log(a);
console.log(b);
}
//当参数为一个时可以省略括号:
let fun = a = > {}
//当函数体只有一行代码且为返回值时可以省略{}:
let fun = (a,b) => a+b;
console.log(fun(1,2)) //3
自执行函数
- 与其名称一样,自执行函数就是定义完成后会自动执行的函数
- 自执行函数内部可以访问全局变量,但是外部无法访问它
//定义方式一:
(function (name) {
console.log(`hello ${name}`); //hello aye
}('aye'))
//定义方式二:
(function (name) {
console.log(`hello ${name}`); //hello aye
})('aye')
//定义方式三:
! function (name) { //‘!’可以换成‘void’
console.log(`hello ${name}`); //hello aye
}('aye')
//例:
! function fun(a, b) {
console.log(a, b); //1 2
return a + b;
}(1, 2);
fun(); //fun is not defined(外部无法访问它)
闭包
指有权访问另一个函数作用域中变量的函数,可以理解为定义在一个函数内部的函数
//通过闭包实现一个计数器:
function fun() {
let count = 0;
function add() { //被嵌套的函数为闭包函数
++count;
return count; //在add中使用count最后将add返回,打破局部变量的限制
}
return add;
}
//通常情况下函数执行完毕其内部的变量会被垃圾回收机制清除,但在此处调用fun后,其内部的add还在保留对
//count的引用,所以无法被清除,以此达到了延长函数内局部变量生命周期的目的
let f = fun();
console.log(f()); //1
console.log(f()); //2
console.log(f()); //3
let f1 = fun();
console.log(f1()); //1 都拥有自己独立的词法作用域
//以上代码还可以通过配合自执行函数优化为以下方式:
let f = (function () {
let count = 0;
return function () {
return ++count;
}
}())
console.log(f()); //1
console.log(f()); //2
console.log(f()); //3
闭包的作用
- 避免变量被污染
- 实现私有化
- 保存变量,常驻内存
闭包应用场景
- 计数器
- 缓存:用闭包创建一个缓存函数,将计算结果缓存,避免重复计算
- 私有变量:创建私有变量,防止外部修改和访问
- 封装库时保证数据的私有性
- vue2.x中的data也是利用的闭包的特性,让每个组件拥有自己的data,互不影响
闭包的坑
//this指向问题:
var object = {
name: '对象',
getName: function () {
return function () {
console.log(this.name);
}
}
}
object.getName()(); //undefined
//引用的变量可能发生变化:
function outer() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function () {
console.log(i); //10,10,10,10,10,10,10,10,10,10
}
}
return result;
}
outer().forEach(fun => {
fun(); //这样会打印出10个10,而不是0~9,要把他变成立即执行函数才会变成0~9
});
//内存泄漏
function showId() {
var el = document.getElementById('test');
var id = el.id;
el.onclick = function () {
alert(id); //这样写会导致id去访问外部函数作用域的id,造成内存泄漏
}
el = null;
}
generator函数
又名期约函数、星号函数
- Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,next()方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield后紧跟迭代器要返回的值,也就是指针就会从函数头部或者上一次停下来的地方开始执行到下一个yield
function* fun() { //通过*定义
console.log(1);
yield //异步不同阶段的分割线
console.log(2);
console.log(3);
}
fun(); //未执行打印任何内容若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
//使用next来执行generator函数
let gen = fun(); //直接赋值不调用
gen.next(); //1
gen.next(); //2 3
高阶函数
- 若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数(Promise、setTimeout)
- 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
函数柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
// function sum(a,b,c){
// return a+b+c;
//}
//console.log(sum(1,2,3))
function sum(a) { //柯里化
return (b) => {
return (c) => {
return a + b + c
}
}
}
console.log(sum(1)(2)(3))