1.8 函数
函数就是把一段代码打包成一个"工具包",起个名字,想用的时候喊一声就行。不用每次都从头写。
函数的核心价值:代码复用和逻辑封装。
1.8.1 定义和调用
用 function 关键字定义函数,用函数名() 调用函数。
定义语法:function 函数名() { 函数体 }
调用语法:函数名(); —— 不调用不执行!
演示:点击按钮调用函数
sayHello 函数
sayHello()
等待调用...
sayGoodbye 函数
sayGoodbye()
等待调用...
1// 定义函数
2function sayHello() {
3 console.log('你好!欢迎学习JavaScript!');
4}
5
6// 调用函数
7sayHello(); // 输出:你好!欢迎学习JavaScript!
8
9// 可以多次调用
10sayHello();
11sayHello();
1.8.2 参数(形参和实参)
参数让函数更灵活——调用时传不同的值,函数就做出不同的反应。
形参(parameter):定义函数时写在括号里的"占位符"。
实参(argument):调用函数时传进去的"实际值"。
1// 定义:name 是形参
2function greet(name) {
3 console.log('你好,' + name + '!');
4}
5
6// 调用:传不同的实参
7greet('小明'); // 输出:你好,小明!
8greet('小红'); // 输出:你好,小红!
9
10// 多个参数
11function introduce(name, age) {
12 console.log('我叫' + name + ',今年' + age + '岁');
13}
14
15introduce('李四', 25); // 输出:我叫李四,今年25岁
1.8.3 返回值
函数处理完数据后,可以用 return 把结果"还回来"。
关键点:return 会把值返回给调用者,同时函数立即结束。
如果没有 return,函数默认返回 undefined。
演示:add 函数的返回值
add(10, 20)
add(10, 20)
等待调用...
add(5, 7)
add(5, 7)
等待调用...
嵌套调用
add(add(1,2), add(3,4))
等待调用...
1// 定义一个加法函数
2function add(a, b) {
3 let result = a + b;
4 return result; // 把结果返回
5}
6
7// 调用函数,把返回值存起来
8let sum = add(10, 20);
9console.log('10 + 20 = ' + sum); // 输出:10 + 20 = 30
10
11// 返回值可以直接使用
12let total = add(add(1, 2), add(3, 4));
13console.log(total); // 输出:10(3 + 7)
1.8.4 箭头函数(ES6 简写)
箭头函数是 ES6 提供的一种更简洁的函数写法。
function 变成 =>,如果只有一行代码可以省略 {} 和 return。
三种写法对比
| 写法 |
代码 |
说明 |
| 传统 function |
function double(n) { return n * 2; } |
标准写法 |
| 箭头函数(完整) |
let double = (n) => { return n * 2; }; |
function 换成 => |
| 箭头函数(简写) |
let double = (n) => n * 2; |
单行省略 {} 和 return |
传统写法 double1(5)
double1(5)
等待调用...
箭头函数 double2(5)
double2(5)
等待调用...
简写 double3(5)
double3(5)
等待调用...
1// 传统写法
2function double1(n) {
3 return n * 2;
4}
5
6// 箭头函数写法
7let double2 = (n) => {
8 return n * 2;
9};
10
11// 最简写法(只有一行,省略{}和return)
12let double3 = (n) => n * 2;
13
14console.log(double1(5)); // 10
15console.log(double2(5)); // 10
16console.log(double3(5)); // 10
📋 函数核心要点
| 要点 |
说明 |
| 定义 |
function 函数名() {} |
| 调用 |
函数名() —— 不调用不执行 |
| 形参 |
定义时写括号里,是占位符 |
| 实参 |
调用时传进去,是实际值 |
| 返回值 |
return 返回结果,函数立即结束 |
| 箭头函数 |
let fn = () => {},更简洁 |
1.9 数组 — 定义数组
数组就是一个有序的列表,可以把一堆数据排好队放在一起。好比一个购物清单。
用中括号 [] 定义,元素之间用逗号分隔。
下标从 0 开始:第一个元素是 [0],最后一个是 [长度-1]。
下标与元素对应关系
数组长度:fruits.length = 4 | 最后一个元素:fruits[fruits.length - 1]
1// 定义一个数组
2let fruits = ['苹果', '香蕉', '橘子', '西瓜'];
3
4console.log(fruits); // ["苹果", "香蕉", "橘子", "西瓜"]
5console.log(fruits.length); // 4(有4个元素)
6
7// 通过下标(索引)访问元素,下标从0开始
8console.log(fruits[0]); // 第一个元素:苹果
9console.log(fruits[1]); // 第二个元素:香蕉
10console.log(fruits[3]); // 第四个元素:西瓜
11console.log(fruits[4]); // 不存在的下标:undefined
1.9.2 遍历数组
遍历就是把数组里的元素一个个取出来。
用 for 循环 + .length 配合,可以逐个访问每个元素。
演示:for 循环遍历成绩数组
点击"开始遍历"查看过程
1let scores = [85, 92, 78, 96, 88];
2let total = 0;
3
4// 遍历数组求和
5for (let i = 0; i < scores.length; i++) {
6 total += scores[i];
7}
8
9let average = total / scores.length;
10console.log('总分:' + total); // 439
11console.log('平均分:' + average); // 87.8
1.9.3 常用方法
JavaScript 给数组准备了一些"工具方法",可以直接用。
交互演示:push / pop / unshift / shift
初始数组: [1, 2, 3]
方法速查表
| 方法 |
作用 |
示例 |
结果 |
push(值) |
末尾添加 |
[1,2].push(3) |
[1,2,3] |
pop() |
末尾删除 |
[1,2,3].pop() |
[1,2] |
unshift(值) |
开头添加 |
[1,2].unshift(0) |
[0,1,2] |
shift() |
开头删除 |
[1,2,3].shift() |
[2,3] |
indexOf(值) |
查找位置 |
['a','b'].indexOf('b') |
1 |
concat(数组) |
拼接数组 |
[1].concat([2]) |
[1,2] |
join('分隔符') |
转字符串 |
[1,2,3].join('-') |
"1-2-3" |
1let arr = ['a', 'b', 'c'];
2
3// push:往末尾加
4arr.push('d');
5console.log(arr); // ["a", "b", "c", "d"]
6
7// pop:从末尾删
8arr.pop();
9console.log(arr); // ["a", "b", "c"]
10
11// unshift:往开头加
12arr.unshift('z');
13console.log(arr); // ["z", "a", "b", "c"]
14
15// shift:从开头删
16arr.shift();
17console.log(arr); // ["a", "b", "c"]
18
19// 查找元素位置
20let index = arr.indexOf('b');
21console.log(index); // 1
22
23// 拼接数组
24let arr2 = [1, 2, 3];
25let arr3 = [4, 5, 6];
26let merged = arr2.concat(arr3);
27console.log(merged); // [1, 2, 3, 4, 5, 6]
1.10 对象 — 创建对象
对象就是一组相关数据的集合,每个数据都有一个名字(属性名)和一个值(属性值)。好比一张"个人信息表"。
用花括号 {} 创建对象,里面是键值对(key: value)。
演示:person 对象的结构
👤 person 对象
name'小明'
age18
city'北京'
sayHellofunction() {...}
点击按钮调用方法
1// 创建一个"人"对象
2let person = {
3 name: '小明', // 属性:姓名
4 age: 18, // 属性:年龄
5 city: '北京', // 属性:城市
6 sayHello: function() { // 方法(对象里的函数)
7 console.log('你好,我叫' + this.name);
8 }
9};
10
11// 访问属性:用 对象名.属性名
12console.log(person.name); // 小明
13console.log(person.age); // 18
14
15// 另一种访问方式:用 对象名['属性名']
16console.log(person['name']); // 小明
17
18// 调用方法
19person.sayHello(); // 你好,我叫小明
1.10.2 属性与方法访问
访问对象属性有两种方式:点语法和方括号。
对象里的函数叫做方法,调用时必须加括号 ()。
this 在对象方法里表示"这个对象自己"。
演示:product 对象的属性和方法
📦 product 商品对象
name'笔记本'
price29.9
quantity100
totalValue()this.price × this.quantity
info()打印商品信息
点击按钮调用方法
📝 访问方式对比
| 方式 |
语法 |
结果 |
| 点语法 |
person.name |
'小明' |
| 方括号 |
person['name'] |
'小明' |
| 方法(加括号) |
person.sayHello() |
执行方法 |
| 方法(不加括号) |
person.sayHello |
函数本身 |
1// 创建一个商品对象
2let product = {
3 name: '笔记本',
4 price: 29.9,
5 quantity: 100,
6 // 方法:计算总价值
7 totalValue: function() {
8 return this.price * this.quantity;
9 },
10 // 方法:显示商品信息
11 info: function() {
12 console.log('商品:' + this.name + ',单价:' + this.price + '元');
13 }
14};
15
16// 调用方法
17let total = product.totalValue();
18console.log('总价值:' + total + '元'); // 2990元
19
20product.info(); // 商品:笔记本,单价:29.9元,库存:100
1.11 作用域
作用域就是变量的"可视范围"——变量在哪些地方能被访问到,哪些地方不能。
简单说:函数内部声明的变量,外面用不了;函数外面声明的变量,里面都能用。
1.11.1 全局变量 vs 局部变量
全局变量:在函数外面声明的,整个页面都能用。
局部变量:在函数里面声明的,只有函数内部能用。
演示:全局与局部作用域
🌐 全局作用域
let globalVar = '我是全局变量'
整个页面任何地方都能访问
🔒 函数 testScope() 内部
let localVar = '我是局部变量'
只有函数内部能访问
| 变量类型 |
在哪里声明 |
在哪能访问 |
| 全局变量 |
所有函数外面 |
整个页面的任何地方 |
| 局部变量 |
函数内部 |
只有这个函数内部 |
1// 全局变量
2let globalVar = '我是全局变量';
3
4function testScope() {
5 // 局部变量
6 let localVar = '我是局部变量';
7
8 console.log(globalVar); // ✅ 函数内部能访问全局变量
9 console.log(localVar); // ✅ 函数内部能访问自己的局部变量
10}
11
12testScope();
13
14console.log(globalVar); // ✅ 函数外面能访问全局变量
15// console.log(localVar); // ❌ 报错!函数外面访问不到局部变量
1.11.2 变量提升(var 的特殊行为)
变量提升是 var 的一个特点:用 var 声明的变量,会被"提升"到当前作用域的最顶部。
let 和 const 不会变量提升,使用前必须先声明。
var 变量提升过程
⚠️ var 的行为
console.log(a); // undefined
var a = 10;
console.log(a); // 10
声明被提升,赋值留在原地
✅ let 更安全
// console.log(b); // ❌ 报错!
let b = 20;
console.log(b); // 20
不会提升,声明前访问直接报错
1// var 的变量提升
2console.log(a); // undefined(不报错,因为变量被提升了)
3var a = 10;
4console.log(a); // 10
5
6// 实际过程相当于:
7var a; // 声明被提升到最前面
8console.log(a); // undefined(还没赋值)
9a = 10; // 赋值还在原地
10console.log(a); // 10
11
12// let 不会有变量提升
13// console.log(b); // ❌ 报错!let 不会提升
14let b = 20;
15console.log(b); // 20
1.11.3 作用域链
当你在函数内部访问一个变量时,JavaScript 会先在当前函数里找,找不到就去外层函数找,再找不到就去全局找——这条查找路径就是作用域链。
查找规则:从里往外找,找到就停,找不到就报错。
作用域链可视化:从内向外逐层查找
🌐 全局作用域
let global = '全局变量'
→
📦 outer() 函数
let outerVar = '外部变量'
🔒 inner() 函数
let innerVar = '内部变量'
inner 找变量:
① 自己有 → 直接用
② 没有 → 去 outer 找
还没有 → 去全局找
点击按钮查看变量查找过程
1let global = '全局变量';
2
3function outer() {
4 let outerVar = '外部变量';
5
6 function inner() {
7 let innerVar = '内部变量';
8
9 console.log(innerVar); // ✅ 当前函数有,直接取
10 console.log(outerVar); // ✅ 当前函数没有,去 outer 找
11 console.log(global); // ✅ 当前和 outer 都没有,去全局找
12 }
13
14 inner();
15}
16
17outer();
📋 作用域核心要点
| 要点 |
说明 |
| 全局变量 |
函数外声明,全局可用 |
| 局部变量 |
函数内声明,仅函数内可用 |
| 变量提升 |
var 会把声明提到顶部,let/const 不会 |
| 作用域链 |
从内向外逐层查找变量,找到就停 |