javascript中的函数我们可以理解成是一个封装任意多条执行语句的过程,ES中函数使用function关键字来声明,后面跟一组参数以及函数体
函数的定义方式很多,再目前来讲我们只看一种标准方式,语法如下
function 函数名(参数, ...){
//函数体
}
注意点:
1、关于函数名,我们可以当成是一个标识符来看待,所以再给函数命名的时候我们需要遵循标识符的规范
2、函数的代码体(函数体)里面是可以有任意多条语句的
3、后面的大括号不能省
那么我们学习函数到底是为了什么,函数可以帮我们解决什么问题?
之前的学习中,我们在编写好js代码之后,在浏览器中运行会发现代码是被直接执行的,因为,js是一种解释性语言,解释性语言的执行特征就是,解释一句(读一句代码) ,执行一句,所以我们可以认为我们之前所写的代码都是一种立即执行的状态,那么现在我们不想让代码立即执行,而是让我们编写的代码,在程序运行之后,在某个适当的实际再执行,这个时候就可以使用到函数,将对应语句进行封装,从而让被封装到函数体内的语句变成一个等待执行的状态,然后我们再通过合适的时机调用这个函数,从而让函数体内封装的代码再次执行
function sayHello(){
console.log("everybody hello");
}
上面我们就相当于声明了一个函数,上面的过程我们也可以叫做封装了一个方法
其他的一些声明函数的方式
var 函数名 = function(){
//函数体
}
根据上面的写法
var sayHello = function(){
console.log("everybody hello");
}
注意:
上面两种方式的定义看起来好像一样,实际还是有点区别的
abc();
function abc(){
console.log("hello abc");
}
//下面的调用函数,去执行的时候会报错,def is not a function
def();
var def = function(){
console.log("hello def");
}
通过第一种方式声明出来的函数,可以在定义函数的代码之前调用,而通过var定义的函数会报错
函数自身也是有数据类型的,它可以通过typeof去检测,检测结果是function
定义好的函数是不会自己执行,它需要经过调用才会被执行,函数的调用是通过函数名来完成的,语法格式如下
函数名();
在实际调用过程中会有如下情况
function test(){
console.log("a");
}
test(); //调出该函数的函数体中所包含的代码,并执行
test //调出来函数,但是没有执行
如果只写函数名不写后面的小括号,我们会发现,函数体内的代码并没有被执行,通过也没有报错,原有在于在调用时需要跟在函数名后面的小括号中,我们可以把这个小括号理解成是一个立即执行符,前面的函数名代码调出函数体,后面的小括号代码立即执行函数体内的代码
也就是说,我们可以调用函数的语法理解成是由两个部分组成的,由调(函数名)和用(小括号)两部分组成
有如下情况
var userName;
function sayHello(){
console.log("大家好,我叫" + userName);
}
function sayGoodBye(){
console.log(userName + "说,拜拜");
}
userName = "张三";
sayHello(); //大家好,我叫 张三
sayGoodBye(); //李四说,拜拜
userName = "李四";
sayHello(); //大家好,我叫 李四
userName = "赵五";
sayHello(); //大家好,我叫 赵五
当我们调用 sayHello()
的函数的时候,我们看到可以根据变量userName去动态改变打印出来的内容
这个时候,我们把userName的变量定义在函数的外边,因为如果定义在函数体内,那么这个变量就是一个局部变量,局部变量是无法跨函数调用,所以我们把userName定义在外边供多个函数调用
定义在外边的变量我们叫全局变量,任何时候任何地方都可以使用,但是这样也容易出现问题,就是变量冲突,在上面的例子中我们就有体现
为了解决这个冲突问题,我们就引入了参数的概念
function sayHello(userName){
console.log("大家好,我叫" + userName);
}
//带参的函数,在声明的时候一般不给参数赋值,参数我们可以理解成就是一个变量,只不过这个变量一般情况下不在
//声明的时候赋值
sayHello("张三");
//参数一般在函数被调用的时候赋值
sayHello("李四");
sayHello("赵五");
当一个函数有了参数的引入之后,函数的作用的灵活性会有很大的提升
形参:形式参数,指的就是小括号里面写的参数名
实参:实际参数,调用函数的时候,括号里面的值
function add(a,b){
console.log(a+b);
}
//上面的函数中,a和b就是形参,没有具体的值,也没有具体的类型
再来看下面的情况
var x = 11,y = 13;
add(x,y); //add(11,13)
这个时候x,y是实参,因为这个是x,y是有一个实际的值存在的
通过调用函数,add的时候,实参x赋值给了a,实参y赋值给了b,函数再调用的时候,是实参赋值给形参,但是实参与形参的个数是没有必要形参一一对应的关系
所以也会有以下情况
add("hello");
add(10,20,30);
函数的重载指的是函数再定义的时候,它的函数名相同,而函数的参数的个数不同同时参数的类型也不同,这个同名的函数就称之为函数重载(overload)
JavaScript里面的函数没有重载,只有强类型语言才有重载
function add(a,b){
console.log(a+b);
}
function add(a,b,c){
console.log(a+b+c);
}
//因为js没有函数重载,所以不同通过同名函数的参数来做区分
这个时候我们看到函数名相同,但是函数的参数个数不同,我们的重载是根据参数的类型与个数来决定,但是JavaScript当中这两点都无法实现
1、javascript是弱类型语言,变量在没有赋值之前都是undefined,所以不能通过类型区分
2、JavaScript中的函数的参数,实参与形参没有必要形成一一对应的关系,这个也无法通过参数的个数去区分
所以js当中不存在重载,那么当出现同名函数的时候,会发生覆盖
函数的返回值是指函数将内部某一些值返回到函数外边,它使用 return
关键字,js中每个函数都可以有返回值
首先我们通过简单的代码来理解下返回值
function abc(){
return "hello";
}
var x = abc();
这个时候,abc函数就有一个返回值,这个返回值是 “hello” ,然后变量x就接收了这个abc函数的返回值,所以x的值就是“hello”
function add(a,b){
var c = a + b;
return c;
}
var z = add(10,20);
这个时候的变量z就是接收函数add的返回值,最终结果是30
return
是把函数内的值返回到函数外进行使用,同时注意,一个函数一旦碰到了 return关键字就会跳出当前函数,return后面的代码就不再执行了,如下
function abc(){
var a = 10 + 20;
return a;
console.log(a);
}
var x = abc();
有时候,return关键字也可以用于跳出函数使用,后面就可以不同跟任何的值
function abc(){
var a = 10 + 20;
return ;
console.log(a);
}
abc();
如果一个函数再内部又调用了自己,那么这个函数就称为递归函数
递归函数是可以通过循环的演变来解释的,所以我们先看一个循环
//假设我们要求1-10的和
var num = 0;
for(var i = 1; i <= 10;i++){
num += i; // num = num + i;
}
console.log(num);
上面的代码中我们要理清除几个东西
循环条件
1、初始值 i= 1
2、结束条件 i <= 10
3、自变量 i++
当前这个循环的本质
1、 i <= 10
2、num += i
3、i++
现在我们只需要让上面3各步骤去运行就可以得到结果了,不一定非要使用循环
这个时候代码就演变如下
var i = 1;
var num = 0;
function add(){
num += i;
i++;
if(i <= 10){
add(); //这行代码,代码再函数体里面又调用了自己
}
}
add();
console.log(num);
在上面的代码里面,我们编写了一个add函数,这个函数的内部就是用了递归,它的在内部又调用了自己
通过整个分析,我们可以得到 add的函数代码的运行其实就是一个循环的执行
现在有一个数列,排列时这样的,1,1,2,3,5,8 … 问第N位应该是多少?
var a = 1; //前二项
var b = 1; //前一项
var c; //当前项
for(var i = 1; i <= 9; i++){
if(i == 1 || i == 2){
c = 1;
}else{
c = a + b; //相当于完全了当前项的计算
a = b; //前一项值变成前二项
b = c; //当前项的值变成前一项
}
}
console.log(c);
如果通过递归的方式来完成:
function abc(n){
if(n == 1 || n == 2){
return 1; //代表第一项和第二项的时候
}else{
return abc(n - 1) + abc(n - 2);
// abc(5) + abc(4)
// abc(4) + abc(3) + abc(3) + abc(2)
// abc(3) + abc(2) + abc(2) + abc(1) + abc(2) + abc(1)
// abc(2) + abc(1) + abc(2) + abc(2) + abc(1) + abc(2) + abc(1) + abc(2)
}
}
var x = abc(6);
我们之前学习的时候是通过var来定义变量的,通过var定义的变量是没有所谓的“块级作用域”
var a = 123; //全局变量
if(true){
console.log(a);
}
//-------------------------------------
{
var a = 123; //全局变量
}
console.log(a);
//--------------------------------------
for(var i = 1; i <= 10;i++){
var c = "haha";
}
console.log(c);
这几个变量在这里全都是全局变量,这里的大括号没有形成作用域,它与其他的编程语言不一样的,但是这样注意一点,function的大括号会形成作用域
function abc(){
var a = 1; //这是一个局部变量,只能在大括号内调用,出了大括号就不能再使用了
}
console.log(a); //这里会报错,因为a是一个局部变量