JS函数
本质
函数用来封装需要多次使用的代码块
函数也是一个对象,因此可以给函数添加属性和方法,并作为数据值(利用函数的返回值)/参数(特定函数如setTimeout)/返回值使用
把函数当参数传递时,函数名即为函数本体(里面的代码块),无需添加括号(添加括号即为执行函数)
当函数作为返回值时,为了调用返回的函数,需要在函数本体(函数名)后加()来执行
组成
函数包括函数名、形式参数、实际参数、返回值
函数名是指向函数在内存中位置的介质,可省略(此时叫匿名函数)
定义函数时函数名后的括号里即为形式参数(形参),不是必填,且形参和实参的数目不必相等
使用函数时实际传入的参数为实际参数(实参)
函数内部return后的语句即返回值
意义
函数使得代码得以复用(不需要多次Ctrl+C、Ctrl+V复制粘贴,只要调用函数即可使用相同的代码块执行特定功能)
便于代码的维护和修改(只需在函数中修改代码即可,调用函数的地方随之修改)
增加程序的可读性(把实现方法的细节封装在函数中,用函数名来表示实现的方法,更易理解)
定义方法
字面量
包括function声明和var赋值表达式
1 function add(){ 2 console.log('I like swimming'); 3 }
1 var add = function(){ 2 console.log('I like swimming'); 3 }
function声明函数会预解析,执行函数放在函数定义前也可以顺利执行(声明提前)
var赋值表达式定义函数,在预解析时会将变量赋值为undefined,如果执行函数的操作在定义函数前,则报错
构造函数
构建一个function对象的实例,其中形参和代码块都放在引号中
var add = new Function('num1','num2','return num1+num2;');
定义位置
函数可定义在全局环境或局部环境(函数/对象内部)中
在全局中定义的函数,在全局内部任意位置都可以找到并调用这个函数
在局部中定义的函数,只可通过作用域链,向上一层找到该函数,如果找不到则报错
调用
直接调用
1.匿名函数
1.将其赋值给一个变量,用变量名()来实现调用
1 var add = function(){ 2 alert(1); 3 } 4 add();
2.直接用函数本体后加()的形式自我执行
这里的原理是:用function声明的函数会被识别并预解析,而在function前加合法的符号(+-!~等),不让函数以function打头,则函数不会被预解析,从而实现自执行
因此,在执行的()外/内加一组括号都是可以的
1 !+-~function(){ 2 alert(1); 3 }();
1 (function (){ 2 alert(1); 3 })();
但是要注意,自执行的函数也是有作用域的,执行完毕后,内部的作用域被销毁
2.递归调用
在函数内部调用自身
注意调用次数不要超过最大的递归层数
1 function factorial(num){ 2 if(num == 1){ 3 return 1; 4 } 5 else{ 6 return num*factorial(num-1); 7 } 8 } 9 console.log(factorial(5));
3.方法调用
不合法的方法名(属性名)需要用引号括起来(例如@),调用时用[](例如add['@'](1,2))
如果调用的方法名是一个字符串,则也使用[]
方法也可以链式调用,但该对象里的每个方法需要返回当前对象(否则会造成undefined.方法的结果,出现报错)
不推荐过度使用链式调用
1 var operation = { 2 add:function(num1,num2){ 3 console.log(+num1+ +num2); 4 return this; 5 }, 6 subtract:function(num1,num2){ 7 console.log(num1-num2); 8 return this; 9 } 10 } 11 operation.add(1,2).subtract(3,2);
4.构造函数调用
通过new 构造函数名()来调用(例如new Object())
调用完成后返回一个对象(该构造函数实例化的对象)
间接调用
即不使用函数中的方法名()来调用方法,而是用函数名.方法名.apply()/call()来间接调用方法
注意此处函数名后不可跟()
call和apply方法的第一个参数都是用来改变方法中this的指向的,第二个参数apply可接收一个数组,把数组中每个元素作为参数传入,而call只可接收一个个独立的参数
参数
类型
形式参数(形参):声明函数时的‘占位用’参数(在预解析时被当做函数的局部变量),可用函数名.length属性来访问到其个数
实际参数(实参):调用函数时传入的参数,可用arguments.length属性访问其个数
参数传递的本质:实参赋值给形参
个数
1.实参数目=形参数目:按照定义函数时的顺序,把实参一一赋值给形参
2.实参数目<形参数目:未被赋值的形参为undefined
一般会在有可选参数时使用(如果不传递某个参数,给这个参数默认值)
1 function power(base,power){ 2 if(!power){ 3 power = 2; 4 } 5 return Math.pow(base,power); 6 } 7 console.log(power(3));//返回的是9
3.实参数目>形参数目:多余的实参不被纳入函数的调用范围,会正常运行
参数的类数组对象arguments
arguments里保留了所有的实参(以键值对形式存储)、length属性、callee方法
建议不要在函数中对arguments对象进行修改操作
callee方法可以返回arguments对象的函数,防止函数名变化时,内部递归调用的函数名称也需要同时改变(严格模式下callee方法不可用)
1 function jiecheng(num){ 2 if(num == 1){ 3 return 1; 4 } 5 else{ 6 return num*arguments.callee(num-1); 7 } 8 } 9 console.log(jiecheng(5));
严格模式下,可以把命名函数赋值给通过var声明的变量,来达到类似callee的效果(变量名变化时,函数名以及函数内部自调用的名称都不要变,只要改变变量名)
1 var jiecheng = function f1(num){ 2 if(num == 1){ 3 return 1; 4 } 5 else{ 6 return num*f1(num-1); 7 } 8 } 9 console.log(jiecheng(5));
可当参数的东西
1.参数可以为空(声明函数时,括号中可以为空)
2.可以通过作用域链,用上一级的局部变量/全局变量来作为函数的参数
3.可以传递基本类型数据(数字、字符串、布尔值、undefined、null)或引用类型数据(数组、对象(参数三个以上时推荐使用)、函数)
其中传入对象可以解决了参数传递时必须按照形参顺序的问题
返回值
含义
函数结束,将后续的内容返回(否则返回undefined)
可当返回值的东西
1.返回值可以为空,即函数到此终止,返回undefined
2.返回值可为基本类型数据(数字、字符串(alert函数)、布尔值、undefined、null)、引用数据类型(数组、对象、函数)
document.write()方法期望返回的是字符串,如果不是,会调用toString方法将其返回为字符串