15.JavaScript 函数与作用域完全指南:语法、参数、表达式与作用域链实战

目录

一、函数的基本语法与调用机制

二、关于参数个数的匹配规则

三、函数表达式

四、作用域与作用域链

[1. 作用域的基本概念](#1. 作用域的基本概念)

[2. 作用域链](#2. 作用域链)


一、函数的基本语法与调用机制

在 JavaScript 中,函数是第一类公民,其核心语法如下:

复制代码
// 创建函数/函数声明/函数定义
function 函数名(形参列表) {
    函数体
    return 返回值;
}

// 函数调用
函数名(实参列表)        // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值

关键特性:

  • 函数定义并不会执行函数体内容,必须调用才会执行,调用几次就执行几次。

  • 调用函数时进入函数内部执行,函数结束时回到调用位置继续执行,可借助调试器观察。

  • 函数的定义和调用顺序没有要求(这一点和变量不同,变量必须先定义再使用)。

示例代码:

复制代码
function hello() {
    console.log("hello");
}
// 如果不调用函数,则没有执行打印语句
hello();

动态语言特性:

JS 是一个动态语言,支持灵活的函数操作。


二、关于参数个数的匹配规则

实参和形参之间的个数可以不匹配,但实际开发一般要求形参和实参个数要匹配:

  1. 如果实参个数比形参个数多,则多出的参数不参与函数运算:

    sum(10, 20, 30); // 30

  2. 如果实参个数比形参个数少,则此时多出来的形参值为 undefined:

    sum(10); // NaN,相当于 num2 为 undefined

**注意:**​ JS 的函数传参比较灵活,这一点和其他语言差别较大,事实上这种灵活性往往不是好事。

完整示例:

html 复制代码
<script>
    // 定义的没有参数列表,也没有返回值的一个函数
    function hello() {
        console.log("hello")
    }
    // 定义一个有参数列表,有返回值的一个函数
    function HelloXiaobite(num, name) {
        console.log(num + "Hello" + name)
        return 1;
    }

    let b = hello()
    console.log(typeof(b))
    let a = HelloXiaobite(1, "小明")
    console.log(typeof(a))

    // 实参个数比形参个数少
    HelloXiaobite();
    HelloXiaobite(1, "小明", 3)
</script>

三、函数表达式

除了函数声明,JS 还支持函数表达式,这是另一种函数定义方式:

html 复制代码
var add = function() {
    var sum = 0;
    for (var i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}
console.log(add(10, 20));        // 30
console.log(add(1, 2, 3, 4));    // 10
console.log(typeof add);         // function

此时形如 function() { }的写法定义了一个匿名函数,然后将这个匿名函数用一个变量来表示,后面就可以通过这个 add变量来调用函数了。

JS 中函数是一等公民,可以用变量保存,也可以作为其他函数的参数或者返回值。

命名函数表达式示例:

html 复制代码
let result = function Sum() {
    // 计算1~100之间的和
    ret = 0
    for(i = 0; i <= 100; i++) {
        ret += i
    }
    return ret
}
console.log(result())

也可以写成下方的匿名函数:

html 复制代码
let b = function() {
    console.log(arguments)
}
b();            
b(1, 2, 3)

四、作用域与作用域链

1. 作用域的基本概念

**作用域:**​ 某个标识符名字在代码中的有效范围。

在 ES6 标准之前,作用域主要分成两个:

  • 全局作用域:在整个 script 标签中,或者单独的 js 文件中生效。

  • 局部作用域/函数作用域:在函数内部生效。

示例:

html 复制代码
<script>
    let num = 10;
    function test01() {
        let num = 100;
        console.log(num)
    }
    function test02() {
        let num = 200;
        console.log(num)
    }
    console.log(num)
    test01()
    test02()
    for(let i = 1; i <= 100; i++) {
    }
    console.log(i)
    // 在JS中,如果定义一个变量不使用let、var此时这个变量就变成一个全局变量
</script>

上面这个代码会报错,处理方法就是把for循环里面的let去掉
注:如果在函数内使用 let或 var定义变量,则是局部变量;若不使用关键字,则变成全局变量(不推荐)。


2. 作用域链

背景:

  • 函数可以定义在函数内部。

  • 内层函数可以访问外层函数的局部变量。

  • 内部函数可以访问外部函数,外部函数可以访问全局变量,依次进行查找。

作用域链示例 1:

html 复制代码
let num = 10
function test01() {
    let num = 100
    // 100
    console.log(num)
    function test02() {
        let num = 200
        // 200
        console.log(num)
    }
    test02()
}
test01()

作用域链示例 2(注释说明):

复制代码
let num = 10
function test01() {
    let num = 100
    // 100
    console.log(num)
    function test02() {
        // let num = 200
        // 200
        // 100
        console.log(num)
    }
    test02()
}
test01()

作用域链示例 3(更深嵌套):

html 复制代码
let num = 10
function test01() {
    // let num = 100
    // 100
    console.log(num)//10
    function test02() {
        // let num = 200
        // 200
        // 100
        console.log(num)//10
    }
    test02()
}
test01()
相关推荐
apcipot_rain1 小时前
C++转Java日记(06)
java·开发语言
SomeOtherTime8 小时前
Rust所有权(AI回答)
开发语言·后端·rust
Easyhood9 小时前
Android 开发入门实践(十四):Compose 副作用入门与四个常用 API
android·开发语言·kotlin·android jetpack·compose
白杨尚青9 小时前
C++进阶篇(六):map 和 set 的模拟实现——用红黑树封装 mymap/myset(迭代器 + operator[])
开发语言·c++·迭代器·stl·set·map·红黑树
深蓝AI10 小时前
Meta 开源用了八年的设计系统 astryx:连坐两天 GitHub 总榜第一,组件库开始写给 agent 的说明书
前端·人工智能
IT_陈寒11 小时前
Vue的watch把我坑惨了,原来要这样用才靠谱
前端·人工智能·后端
1-1=011 小时前
Vue 3 模板语
前端·javascript·vue.js
用户76103044737111 小时前
【转战智能眼镜开发】ROKID AIUI 小游戏 亚马逊标枪项目实战讲解
前端
风卿11 小时前
看着AI写代码太孤独了,来欣赏星云吧:一个DSH 装饰插件
前端·javascript
牧艺12 小时前
弹层不用再手算坐标了:CSS Anchor Positioning 从原理到可上线
前端·javascript·css