循序渐进学编程5.JavaScript函数

本文深入解析JavaScript函数的语法、特点及应用,涵盖函数声明、调用、作用域、闭包、构造函数等核心概念,辅以实用代码示例,助您掌握函数编程精髓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5.1函数语法

函数语法

重复代码让程序难以维护,哪怕是一个很小的改动

函数主要用于减少重复代码

创建函数(定义/声明函数)

function 函数名()//函数名要语义化
{
    //函数体:函数的内容
}

函数体的代码不会直接运行,必须要手动调用函数,才能运行其中的代码.

调用函数

运行函数体

函数名();

函数提升

通过字面量声明的函数,会提升到脚本块(一个脚本块就是一个script标签)的顶部(就像变量声明提升一样)

通过字面量声明的函数,会成为全局对象的属性.(成为window对象的属性)

其他特点

通过typeof 函数名,得到的类型是"function"

函数内部声明的变量:

  1. 如果不使用var声明,和全局变量一致,表示给全局对象(window对象)添加属性
  2. 如果使用var声明, 变量提升到所在函数的顶部,函数外部不可以使用该变量

函数中声明的变量仅能够在函数内部使用,在外部无法访问

参数

参数表示函数运行的未知条件,需要调用者告知的数字;

//参数的有效范围:在函数体中, 参数不需要声明
function 函数名(参数1, 参数2, ...){

}
函数名(参数);

形参是指函数声明时()里的参数—形式参数,实参是指函数调用时()里的参数—实际参数

如果实参没有传递,对应的形参值为undefined

如果实参数量多于形参,多于的实参没有用处,因为形参与实参是一一对应的;

//删除数组末尾指定数量的数据
function deleteArray(arr, number)
{
    splice(-number, number);
}
var nums = [1, 2, 3, 4, 5];
deleteArray(nums, 3);//nums传给arr的是地址,数组为引用类型
console.log(nums);

返回值

函数运行后,得到的结果,调用函数时,调用表达式的值就是函数的值

return 会直接结束整个函数的运行

return 后面不跟任何数据, 返回undefined

如果函数中没有书写return, 则该函数默认返回undefined

//对一个数组求和
function sumArray(arr)
{
    var sum = 0;
    for(var i = 0;i < arr.length;i++)
    {
        sum += arr[i];
    }
    return sum;
}
var num1 = [234, 242, 34];
var num2 = {23, 19, 101};
console.log(sumArray(num1) + sumArray(num2));
//判断一个数是不是素数

function isPrime(n)
{
    for(var i = 0; i < n-1;i++)
    {
        if(n % i === 0||n < 2)
            return false;
        else
            return true;
    }
}

文档注释

在js中文档注释可以注释变量,也可以注释函数

文档注释的好处:当我们想调用的时候,写上函数名,就可以知道函数的作用:在vs code里面自动显示

/**
 * 两个数求和
 *@param {*} a 第一个数--对a的描述,下面同理 
 *@param {*} b 第二个数
 *@param {*} s 两数之和
*/
// {}里的*是指后面参数的类型,上述代码不会运行,只起到提示的作用
function sum (a, b)
{
    s = a + b;
    return s;//求和结果
}
sum

作业:

通用函数编写

新建一个js文件,编写一下函数

  1. 写一个函数,该函数用于判断某个数是不是奇数

函数名参考: isOdd

  1. 写一个函数,该函数用于判断某个数是不是素数

函数名参考: isPrime

  1. 写一个函数,该函数用于对数组求和

函数名参考: sumofArray

  1. 写一个函数,该函数用于得到数组中的最大值

函数名参考: maxofArray

  1. 写一个函数,该函数用于得到数组中的最小值

函数名参考:minofArray

函数使用

  1. 利用上面的某些函数,实现哥德巴赫猜想

任一大于2的偶数都可写成两个质数纸盒,比如: 8 = 3 + 5

让用户输入一个大于2的整数,输出其等于哪两个素数相加

  1. 让用户输入一个年份,输出该年每个月的天数

作用域与闭包

作用域

作用域表示一个代码区域,也表示一个运行环境

js中,有两种作用域

  1. 全局作用域

直接在脚本中书写的代码

在全局作用域中声明的变量,会被提升到脚本的顶部,并且会成为全局对象的属性

  1. 函数作用域

函数中的代码

在函数作用域中声明的变量,会被提升到函数的顶部,并且不会成为全局对象的属性,

因此,函数中声明的变量不会导致全局对象的污染

尽量把功能分装在函数中

函数的声明会污染全局变量,但是,当函数成为一个表达式时,它既不会提升,也不会污染全局对象。

将函数变成表达式的方法之一:将函数用小括号括起来;-----然而这样一来,函数无法通过名称调用;

如果书写一个函数表达式,然后将其立即调用,该函数称之为立即执行函数IIFE(imdiately Inovked Function Expression)

由于大部分情况下,函数表达式的函数名没有实际意义,因此,可以省略函数名;

没有名字的函数,叫做匿名函数,只有函数表达式才能没有名字;

作用域中可以使用的变量

全局作用域只能使用全局作用域中声明的变量(包括函数)

函数作用域不仅能使用自身作用域中声明的变量(包括函数),还能使用外部作用域中声明的变量(包括函数);

有的时候,某个函数比较复杂,在编写的过程,可能需要另外一些函数来辅助它完成一些功能,而这些函数仅仅会被该函数使用,不会在其他位置使用,则可以将这些函数中声明到该函数的内部;

函数内部声明的变量和外部冲突时,使用内部的;

闭包

闭包(closure),是一种现象,内部函数被保存到外部,外部函数可以访问该函数的作用域.
(后面会深入讲解)

函数表达式和this

函数表达式

JS中,函数也是一个数据,语法上,函数可以用于任何需要数据的地方

JS中,函数和其他数据是一样进行操作;—可以将函数放在变量里.

var a = function()
{
    console.log("andsjf");
}
a();
//这种写法没有函数提升 ,所以只能在赋值之后使用

函数是一个引用类型,将其保存在变量中,即将其地址保存在变量中,这块地址指向存放函数内容的那块内存中

和普通函数声明的区别:赋值给变量的函数没有函数提升,并且不会附着在window对象上面;

函数可以作为函数的参数传递

function test(callback)
{
    console.log("test运行");
    callback();
}

var func = function()
{
    console.log("abc");
}
test(func);//将func这个函数作为实参传递,把这种函数作为参数传递叫做回调函数
//也可以这样写
// test(function()
// {
//     console.log("abc");
// });

this关键字(易考面试题)

this无法赋值

  1. 在全局作用域中,this关键字固定指向全局对象

  2. 在函数作用域中,取决于函数是如何被调用的

    1. 函数直接调用,this指向全局对象
    2. 通过一个对象调用,格式为对象.属性()对象["属性"](),this指向对象

5.6构造函数

构造函数

用于创建对象的函数

用函数创建对象,可以减少繁琐的对象创建流程;

  1. 函数返回一个对象
function createUser(){
    return {
        name: "xxx",
        age :12,
        //...
        function sayHello()
        {
           console.log(`我叫${this.xxx},今年${this.age}岁`); 
        }
    };
}
var u1 = createUser1;
u1();

对象中的属性如果是一个函数,也可以将该属性称之为方法;

  1. 使用构造函数:专门用于创造对象的函数
new 函数名(参数);

如果使用上面的格式创建对象,则该函数叫做构造函数

  1. 函数名使用大驼峰命名法

  2. 构造函数内部,会自动创建一个新的对象,this指向新创建的对象,并且自动返回新对象

  3. 构造函数中如果出现返回值,如果返回的是原始类型,则直接忽略;如果返回的是引用类型,则使用返回的结果;

  4. 所有的对象,最终都是通过构造函数创建的;

new.target

该表达式在函数中使用返回的是当前的构造函数,但是,如果该函数不是通过new调用的,则返回undefined

通过判断某个函数是否通过new调用的;

作业
英雄打怪兽的小游戏

英雄和怪兽都具有生命值、攻击力、防御力、暴击几率(暴击时伤害翻倍)
攻击伤害:攻击力 - 防御力
攻击最少为1
创建一个英雄和一个怪兽,让他们互相攻击,直到一方死亡,输出整个攻击过程

5.7 函数的本质

函数的本质

函数的本质就是对象

某些教程中,将构造函数称之为构造器
所有的对象都是通过关键字new出来的,new 构造函数()

函数的本质就是对象,而所有的对象都是通过关键字new出来的,因此,函数也都是通过new Function创建的

在浏览器引擎启动的时候,就已经帮我们写好放到了内存,可以直接使用,所以Function不是new出来的

Funcion ---> 产生函数对象(构造函数) ---> 产生普通对象
//function sum(a, b)
//{
//    return a + b;
//}

var sum = new Function("a", "b", "return a + b");
//Function是所有函数的构造函数,系统帮我们已经写好了
//通过new Function构造出sum函数
console.log(typeof sum);//function
console.log(sum(3, 5));//8

由于函数本身就是对象,因此函数中可以拥有各种属性;

包装类

js为了增强原始类型的功能,为boolean, string,number分别创建了一个构造函数

  1. Boolean
  2. String
  3. Number

如果语法上将原始类型当作对象使用时(一般是在使用属性时),js会自动在该位置利用对应的构造函数,创建对象来访问原始类型的属性;

类:在js中,可以认为类就是构造函数

成员属性(方法)/实例属性(方法): 表示该属性是通过构造函数创建的对象调用的.

静态属性(方法)/类属性(方法): 表示该属性是通过构造函数本身调用的.

5.8递归

递归

函数直接或间接调用自身

避免无限递归,无限递归会导致执行栈溢出。(要给出口)

对比死循环

  • 死循环不会报错,也不会导致栈溢出
  • 无限递归会导致栈溢出(会报错)
//求斐波那契数列第n位的值
function f(n)
{
    if(n == 1||n == 2)
    {
       return 1; 
    }
    return f(n - 1) + f(n - 2);
}
console.log(f(7));//13
//求n的阶乘
function f(n)
{
    if(n = 1)
    {
        return 1;
    }
    return n * f(n - 1);
}
console.log(f(5));//120
function hannuo(no1, no2, no3, n)
{
    if(n === 1)
    {
        console.log(`${no1}->${no3}`);
    } else
    {
        hannuo(no1, no3, no2, n - 1);
        console.log(`${no1}->${no3}`);
        hannuo(no2, no1, no3, n - 1);
    }
}
hannuo('A', 'B', 'C', 2);

执行栈(call stack)

无论任何代码的执行,都必须有一个执行环境,执行环境为代码的执行提供支持

执行环境和执行栈都是一块内存
执行环境是放到执行栈中的;

每个函数的调用(哪怕是函数调用自身),都需要创建一个函数的执行环境,函数调用结束,执行环境销毁。

执行栈有相对固定的大小,如果执行环境太多,执行栈无法容纳,会报错

尾递归

如果一个函数最后一条语句是调用函数,并且调用函数不是表达式的一部分(也就是说最后一条语句只有纯粹的调用函数),则该语句称为尾调用,如果尾调用是调用自身函数,则称为尾递归。

某些语言或执行环境会对尾调用进行优化,它们会立即销毁当前函数,避免执行栈空间被占用。

在浏览器执行环境中,尾调用没有优化。但在nodejs环境中有优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值