函数
JavaScript里面函数指的是封装任意多条语句,可以在任何地方任意多次的调用执行。ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数以及函数体
在学习函数之前,我们先要了解一个点,为什么我们需要函数???函数可以帮我们解决什么问题?
函数的定义
函数的定义有多种方式,在目前的基本阶段,我们只讲解标准方式。在这种方式下面它使用function
关键字来进行定义,它的语法格式如下
第一种定义方式
function 函数名(参数...?){
//函数代码体
}
注意事项:
- 函数名也算是一个标识符,所以它应该遵守标识符的命名规范(也就是我们之前所学习的变量命令规范)
- 函数代码体可以是任意多条语句
- 这里的花括号是一定不能省略了
function sayHello() {
console.log("大家好,我叫标哥哥");
console.log("我今年18岁了");
console.log("很高兴今天能跟大家见面");
}
上面的代码就相当于封装了一个sayHello
的方法
第二种定义方式
var 方法名 = function(参数...){
//函数代码体
}
根据上面的语法,我们也可以定义一个方法
var sayHello = function(){
console.log("大家好");
}
?注意:上面的两种方式定义的函数它是有区别的
abc();
function abc() {
console.log("hello abc");
}
//调用这个方法,去执行 这个时候代码就会报错,提示def is not a function
def();
var def = function () {
console.log("hello def");
}
通过第一种方式
function
定义出来的函数,可以在定义函数的代码之前调用,而通过var
定义的函数则会报错
方法也是有数据类型的,它也可以通过type
去检测,检测出来的结果是"function"
typeof sayHello; //"fucntion"
函数的调用
函数定义好了以后是不会自已执行,它需要经过调用才会执行(所以函数也叫调用执行)。函数的调用是通过函数名来进行的,它通过函数名()
来进行调用
也主是说上面我们封装的方法可以通过下面的代码来调用执行
sayHello();
函数的参数
现有如下场景,请看代码
// 思考这个变量应该定义在方法里面,还是在方法外边???
var userName;
function sayHello() {
console.log("*****************************");
console.log("大家好,我叫" + userName);
console.log("*****************************");
}
//现在是标哥打招呼
userName = "标哥哥";
sayHello();
//现在又是小奥打招呼
userName = "小奥";
sayHello();
//何陈龙
userName = "何陈龙";
sayHello();
当我们去调用sayHello()
的方法的时候,我们就看到可以根据变量userName去动态的改变打印的内容了
这个时候,我们把userName
的变量定义在方法的外边,因为如果定义在方法的里面以后它就是一个局部变量,外边无法访问到这个变量。这样定义在方法外边形成全局变量以后有没有什么隐患呢?
全局变量在外边任何地方,任何时候也都可以使用,这样很容易出问题。会很容易形成变量冲突。所以我们的userName
就会存在一个很尴尬的情况,既不能在外边,也不能在里边,那我就定义在方法上面
现在我们就以引入参数的概念了
function sayHello(userName) {
//这个时候的userName就相当于一个特殊的变量,里面可以使用,外边也可以使用
//这个特殊的变量,我们就叫函数的参数
//方法外边只能赋值,方法里边即可以赋值,也可以取值
console.log("*****************************");
console.log("大家好,我叫" + userName);
console.log("*****************************");
}
//现在是标哥打招呼
sayHello("帅气的标哥哥");
//现在又是小奥打招呼
sayHello("漂亮的小奥");
//何陈龙
sayHello("有钱的何陈龙");
参数是没有限制条件的,你需要多个少,你就定义多少个,下面的案例当中,我们就定义了两个参数
function sayHello(userName, age) {
console.log("********************************");
console.log("大家好,我叫" + userName);
console.log("我今年" + age + "岁了");
console.log("--------------------------------");
}
//标哥哥打招呼
sayHello("帅气小伙", 18);
//小奥打招呼
sayHello("卢小奥", 19);
//何陈龙打招呼
sayHello("何陈龙", 20);
?应用点:如果你在封装函数的过程当中,如果发些某值是需要通过外部来给定的,我们就可以使用参数了
实参与形参
形参:形式参数,指的是函数在定义的时候括号里面的参数名
实参:实际参数,调用函数的时候,括号里面的实际参
function add(a,b){
console.log(a+b);
}
这个时候的a与b就是形式参数,没有具体的值,也没有具体的类型
var x = 11,y=13;
add(x,y);
这个时候的x与y就是实际参数,因为这个时候的x与y就是一个实际的值的
通过我们之前的调用函数的理解,在调用函数add的时候,实参x赋值给了形参a,实参y赋值给了形参b
函数在调用的时候,是实参赋值给开参,但是实参与形参的个数是没有必要形成一一的对应关第的
所以上面的方法在调用的时候我们完全可以有现面的情况产生
add("hello"); //这个时候的实参小于形参
add(10,20,30); //这个时候的实参大于形参
函数的重载
函数的重载指的是函数在定义的时候,它的方法名相同,而函数的参数个数与参数类型不相同。这个时候这种同名的函数就称之为重载函数(overload)
JavaScript里面的函数没有重载。只有强类型语言里面的函数才有重载,现在我们来对比一下
C#强类型语言里面的重载
public static void add(int a, int b)
{
Console.WriteLine("第一个方法");
Console.WriteLine(a + b);
}
public static void add(int a, int b, int c)
{
Console.WriteLine("第二个方法");
Console.WriteLine(a + b + c);
}
public static void add(string a,string b)
{
Console.WriteLine("第三个方法");
Console.WriteLine(a + b);
}
//上面定义了三个同名的方法,只是它们的参数类型或参数的个数不相同
static void Main()
{
//add(1, 2); 调用的是第一个方法
//add(1, 2, 3); 调用的是第二个方法
//add("hello", "world"); 调用的是第三个方法
}
但是在JavaScript里面,它没有函数重载,为什么 ,请看下面的代码来分析结果
function add(a,b){
console.log("第一个方法");
console.log(a+b);
}
function add(a,b,c){
console.log("第二个方法");
console.log(a+b+c);
}
这个时候我们看到它的函数名相同,但是参数的个数不相同。我们的重载是根据参数的类型与个数来决定的。但是JavaScript的这两个点都法实现
- JavaScript是弱类型语言,变量在没有赋值之前全部都是
undefined
,所以不能通过类型去区分 - JavaScript函数里面的参数,实参与形参没有必要形成一一对应的关系,这样也无法通过参数的个数去区分
正是因为没有重载,所以当JavaScript里面出现同名函数的时候,是会发生覆盖的
函数的返回值
函数的返回值指的是函数将内部某个些值返回到函数的外边,它使用的关键字是return
。JavaScript每个函数都有返回值
首先我们可以通过下面最简单的代码来理解函数的返回值
function abc(){
return "hello";
}
var x = abc();
这个时候的abc函数就有一个返回值,这个返回值就是"hello"
,然后变量x就接收了函数 abc的返回值,x的值最终就是"hello"
function add(a, b) {
var c = a + b;
//这样变量c就会被返回出来
return c;
}
var x = 10;
var y = 20;
var z = add(x, y);
这个时候的变量z就是接收函数add
的返回值,最终的结果就是30
关于return
关键字
return
关键字在函数里面用将值返回到函数外边。同时要注意,一个函数一旦碰到了return
关键字就跳出了当前函数,return
后面的代码就不会再执行了,如下所示
function abc() {
var a = 11 + 22;
return a; //返回一个值,跳出函数
console.log("求和的结果为"+a); //这一行代码 永远也不会执行
}
var x = abc();
在有些时候,return
关键字也可以用于跳出当前函数,后面可以不用接任何值
function abc(){
var a = 123;
return ; //函数在这里就跳出去了,
console.log("hello");
}
abc();
递归函数
如果一个函数在内部又调用了自己,那么这个函数就称之为递归函数
递归函数是可以通过循环的演变来理解的,同学们请先看下面的代码
// 假设要实现1~10之前求和
var sum = 0;
for (var i = 1; i <= 10; i++) {
sum += i;
}
console.log(sum);
在上面的代码里面,我们会理清楚下面的几
循环的条件:
- 起始值:i=1;
- 结束条件:i<=10;
- 自变量:i++
循环的本质:
- i<=10;
- sum+=i;
- i++;
现在我们只需要让上面的代码不停的按1,2,3这三个步骤去运行就可以得到结果了,不一定非要使用循环
所以这个时候,我们可以将代码演变成下面的方式
var i = 1; //初始值
var sum = 0 //求和的值
function add() {
sum += i; //代码体
i++;
if (i <= 10) {
add(); //在这行代码里面又调用了自身
}
}
add();
console.log(sum);
在上面的代码里面,我们编写了一个
add
的函数 ,这个方法的内部就使用了递归,它在内部又调用了自己。我们通过分析可以得了,
add
的函数的代码运行其实就是循环里面的本质性代码运行
?案例:斐波拉契数列中递归的应用
现有一系列数,排列是这这样的1,1,2,3,5,8,13,21,34…,请求出这个数的第n项是多少?
第一种解法:通过循环语句去完成的
//假设我们要求如第20项的数是多少???
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; //当前的值就变成前一项,这们做是为了方便下一次求值
}
}
第二种解法:通过递归去完成的
//假设我们要求如第20项的数是多少???
//n是一个参数,代表求第几项
function abc(n) {
if (n == 1 || n == 2) {
//得到的结果应该是1
return 1;
} else {
//如果不是第1项或第2项,是拿前两项前一项之各
return abc(n - 1) + abc(n - 2);
}
}
//求第9项的值
var x = abc(9);
?递归案例:求1~n之前所有质数的和
第一种方式:使用for循环去完成
function getSum(n) {
var sum = 0; //所有质数的和
for (var a = 2; a <= n; a++) {
var flag = true; //假设这个数是质数
for (var b = 2; b < a; b++) {
if (a % b == 0) {
flag = false;
break;
}
}
if (flag) {
sum += a;
}
}
//将所有质数的和返回
return sum;
}
getSum(10);
第二种方试:使用递归去完成
function getSum(n) {
var sum = 0;
var a = 2;
_a();
function _a() {
var flag = true;
var b = 2;
_b();
function _b() {
if (a % b == 0 && b < a) {
flag = false;
return; //跳出当前方法
}
b++;
if (b < a) {
_b();
}
}
if (flag) {
sum += a;
}
a++;
if (a <= n) {
_a();
}
}
return sum;
}
getSum(10);
变量的作用域
我们之前已经学过通过var来定义了变量,通过var
关键字来定义的变量是没有“块级作用域”。具体可以看下面的代码
var a = 123; //全局变量
if (true) {
console.log(a);
}
//---------------------------------------------
{
var b = 456; //全局变量
}
console.log(b);
//---------------------------------------------
for(var i=1;i<2;i++){
var c = "hello"; //全局变量
}
console.log(c);
这几个变量在这里都是全局变量,这里的花括号没有形成作用域 ,它与其它的编程语言完成不全一样
但是要注意,function的花括号是会形成作用域
function abc() {
//局部变量,只能在这个函数的花括号内部去使用,出了花括号就不能使用了
var userName = "标哥哥";
}
console.log(userName); //这个地方,我们就会看到报错了,因为userName是一个局部量