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();
unnamed function

  2.直接用函数本体后加()的形式自我执行

  这里的原理是:用function声明的函数会被识别并预解析,而在function前加合法的符号(+-!~等),不让函数以function打头,则函数不会被预解析,从而实现自执行

  因此,在执行的()外/内加一组括号都是可以的

1 !+-~function(){
2     alert(1);
3 }();
unnamed function2
1 (function (){
2     alert(1);
3 })();
unnamed function3

  但是要注意,自执行的函数也是有作用域的,执行完毕后,内部的作用域被销毁

  

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方法将其返回为字符串

  

posted @ 2019-08-07 22:09  且听风吟720  阅读(206)  评论(0编辑  收藏  举报