- A+
函数
基本概念
-
在JS中,函数实际上是对象,是
Function
类型的实例。 -
函数名是指向函数对象的指针。
function sum(num1, num2){ return num1 + num2; } // 检查sum是否是Function类型的实例 console.log(sum instanceof Function); // true let anotherSum = sum; // 指向同一个函数 console.log(anotherSum(1,2)); // 3
- 函数将若干条语句封装起来,可以提高代码的可复用性。
创建函数
函数声明
语法:
function funcName(params){ // do something... return something; }
函数表达式
语法:
let funcName = function(params){ // do something... return something; }
因为函数本质上是对象,所以可以直接赋值给一个变量。这个变量将指向这个函数,调用函数时直接使用变量名即可。
箭头函数
语法:
let funcName = (params)=>{ // do something... return something; }
注:箭头函数是ES6新加入的语法。
构造函数
语法:
let funcName = new Function("param1","param2",...,"body");
构造函数接收任意多个字符串参数,最后一个是函数体,前面的是函数参数。
注:这种写法不推荐,因为作为JS语句,它首先会被当做常规代码解析,然后再通过Function
构造函数解析形成函数对象,性能较差。
箭头函数
箭头函数是ES6新增的语法。任何可以使用函数表达式的地方,都可以使用箭头函数。
语法格式
- 如果没有参数,要写括号:
let getRandom = () => {return Math.random();};
- 如果参数只有一个,可以不写括号:
let double = x => {return x*2; };
- 如果有多个参数,要写括号:
let sum = (x, y) => {return x + y; };
- 如果函数体有多条语句,则需要大括号:
let division = (a, b)=>{ if(b === 0){ console.error("除数不能为零"); }else{ return a/b; } }
- 如果函数体只有一条语句,可以不写大括号,此时会隐式返回这行代码的值:
let double = x => x*2;
特点
-
箭头函数语法简洁。
-
箭头函数不能使用
arguments
、super
,也不能用作构造函数,且箭头函数没有prototype
属性。
参数列表
JS的函数参数和大多数其他语言不同,JS中的函数参数定义是无关紧要的。
假如定义函数时预设有两个参数,而调用时传入了0个、1个或者3个参数,解释器都不会报错。
原因是使用function
关键字定义的函数,可以在函数内部访问arguments
对象,从中获取传入的参数。
function testArgs(){ console.log(arguments); } testArgs(1,2,3,4,5,6); testArgs(1,"Hello",true);
arguments
是一个类数组对象,它并不是Array
的实例对象,并不是真正的数组,但是它具有类似数组的一些操作,比如可以像数组一样通过[]
访问元素的值,可以通过length获取元素个数。
可以通过arguments[0]
访问到传入的第一个参数,通过arguments.length
获取传入多少个参数。
箭头函数的参数
注:箭头函数不能使用arguments
,所以箭头函数需要多少参数需要在定义函数时就写明。
不存在函数重载
在其他语言比如C++
、Java
中,存在函数重载,即可以存在多个函数名相同的函数,只要它们的参数个数、参数类型不一样。在调用函数的时候,会根据传入的参数数据类型,匹配上对应的函数。
显然,在JS中,函数的参数列表是随意的(参数个数与参数数据类型都是随意的),也就不存在函数重载了。
注:在JS中,如果有多个函数名相同的函数定义,只有最后一个是有效的,前面的定义都将被覆盖。
默认参数值
// 返回一个由随机数组成的数组,默认长度为5 function getRandomArray(length = 5){ const arr = new Array(); for(let i=0;i<length;i++){ arr.push(Math.floor(Math.random()*100)); } return arr; } const arr1 = getRandomArray(); console.log(arr1); //[76, 86, 60, 21, 37] const arr2 = getRandomArray(10); console.log(arr2); //[18, 98, 76, 43, 77, 24, 44, 74, 58, 3]
- 在函数参数后面使用
=
就可以为其设置默认值。
函数声明和函数表达式
-
函数声明指通过
function functionName(){}
来得到一个函数. -
函数表达式是指通过
function(){}
创建一个匿名函数,再讲这个匿名函数赋值给一个变量functionName
.
区别
函数声明提升
所有函数声明在JS引擎执行代码之前会被提升到代码顶部,即以下两段代码是等价的。
console.log(sum(1,2)); // 不会报错,因为sum会被提升到顶部 function sum(x,y){ return x+y; }
function sum(x,y){ return x+y; } console.log(sum(1,2));
而函数表达式不会被提升:
console.log(sum(1,2)); // 报错 let sum = function(x,y){ return x+y; }
sum
作为一个变量:
-
如果使用
var
声明会被提升,但是定义不会被提升,undefined
无法被调用; -
如果使用
let
则声明和定义都不会被提升,一个未声明的变量显然无法被调用。
函数作为值
函数名是一个变量,因此函数可以作为函数参数传递给另一个函数,并且函数也可以作为一个函数的返回值。
回调函数
如果当前无法决定调用哪个函数,可以先把函数作为参数传递,等到可以做出明确的选择时,再回过头来调用这个函数。这种情况下作为参数传递的函数就被叫做回调函数(callback).
典型应用
向后端接口发送网络请求时,一般会附带上两个回调函数,一个success
函数,一个error
函数。在发送网络请求时,是无法确定请求是否发送成功的。只有拿到了返回结果,才能确定执行success
函数或error
函数。
闭包
闭包是指引用了另一个函数作用域中变量的函数。通常在嵌套函数中实现。
// 获取累加器 function getAccumulator(){ let num = 0; // 内部属性 return function(){ // 返回一个函数 return num++; } } // 调用函数获取累加器 let add = getAccumulator(); console.log(add()); // 0 console.log(add()); // 1 console.log(add()); // 2 console.log(add()); // 3 console.log(add()); // 4
在函数getAccumulator
的作用域中,有一个num
属性,而返回的函数中,引用了这个num
. 即返回的这个函数引用了另一个函数作用域中的变量.
num
这个变量和add
函数绑定在了一起,当getAccumulator
执行结束后也不会被回收,并且无法被直接访问。
立即调用的函数
立即调用的函数表达式(IIFE, Immediately Invoked Function Expression)
ES5没有块级作用域,可以使用立即调用的函数表达式来模拟块级作用域。
var funcName = function(){...}; funcName();
使用立即调用的函数表达式可以省略一个不必要的函数名。
(function(){ ... })();
ES6之后,IIFE就没有那么必要了,因为可以很方便地使用块级作用域。