国庆节10月1日
--


JS 函数用 function 定义(具名、函数表达式、箭头函数三种写法),自定义对象用字面量描述一个东西;再加上 JSON 互转与数组常用方法
.webp)
14 篇把变量、类型、运算符、流程控制讲完了——代码会写,但都是”一次性”的:同样的逻辑要用第二次就得复制一遍。这一篇补上两个”打包”的工具:函数把代码打包成可复用的块,自定义对象把相关的数据和方法打包成一个整体。最后再补上对象在网络上的形态 JSON、以及数组常用方法——它们是下一步 DOM 操作和后面 Vue、Ajax 的地基。
介绍:函数(
function)是被设计用来执行特定任务的代码块,方便程序的封装复用。
定义语法:
1function functionName(参数1, 参数2 ...){2 //要执行的代码3}调用语法:函数名称(实际参数列表)
课程代码 04. JS-函数.html 的具名函数:
1//1. 函数定义及调用 - 具名函数2function add(a, b){3 return a + b; // return 返回结果4}5
6let result = add(10,20); // 调用函数,实参是 10 和 207console.log(result); // 30写代码时把定义放在调用前面(课程代码就是这么排的),读起来最清楚。
由于 JS 是弱类型语言,形参、返回值都不需要指定类型——function add(a, b) 里不写 a、b 是什么类型,也不写返回什么类型(Java 要写成 int add(int a, int b))。另外:调用函数时,实参个数与形参个数可以不一致,但是建议一致。
实参个数不一致会发生什么(课程代码注释里没展开,这里补上):
1function add(a, b){2 return a + b;3}4
5console.log(add(10)); // NaN —— b 是 undefined,10 + undefined 得不到数字6console.log(add(10, 20, 30)); // 30 —— 多出来的 30 被忽略“实参个数可以不一致”是 JS 的灵活之处,也是 bug 的高发区:少传一个参数,函数照样执行,只是默默算出一个 NaN(或 undefined),不会报错。写代码时老老实实保证个数一致,让问题暴露在调用处。
匿名函数是指一种没有名称的函数,可以通过两种方式定义:函数表达式 和 箭头函数。
1//2.1 函数表达式:把一个没有名字的函数赋给变量2let add = function(a, b){3 return a + b;4}5
6//2.2 箭头函数:=> 代替 function7let add = (a, b) => {8 return a + b;9}10
11// 匿名函数定义后,可以通过变量名直接调用12let result = add(100, 200);13console.log(result); // 300(上面两段来自课程 04. JS-函数.html:函数表达式演示的是 add(100,200),箭头函数演示的是 add(1000,2000);两者选一个用即可。)
为什么叫”匿名”?function 后面没有名字,函数本身被赋给了变量 add,所以调用时要写 add(...)(用变量名,不是原来的 functionName)。两种写法的区别:函数表达式内还是熟悉的 function;箭头函数用 => 把参数和函数体连起来,写起来更短——这也是后面写事件监听时最常用的写法(如 addEventListener('click', () => { ... }))。
箭头函数还有一个”极简形态”(了解):只有一个表达式时,{} 和 return 都能省掉——
1// 完整写法2let add = (a, b) => {3 return a + b;4}5
6// 简写:只有一个表达式,省略 {} 和 return(结果会被自动返回)7let add2 = (a, b) => a + b;定义格式:
let 对象名 = { 属性名1: 属性值1, 属性名2: 属性值2, 属性名3: 属性值3, 方法名: function (形参列表) {} }
课程代码 05. JS-自定义对象.html 用一个大括号描述了”一个人”:
1//1. 自定义对象2let user = {3 name: 'Tom',4 age: 18,5 gender: '男',6 sing: function(){7 alert(this.name + '悠悠的唱着最炫的民族风~')8 }9}对象的方法还有一种简写——把 : function 去掉,直接写 方法名(){}:
1let user = {2 name: 'Tom',3 age: 18,4 gender: '男',5 sing(){6 alert(this.name + '悠悠的唱着最炫的民族风~')7 }8}调用格式(PPT 的两行总结):
1// 对象名.属性名;2// 对象名.方法名();3
4alert(user.name); // Tom5user.sing(); // 弹出:Tom悠悠的唱着最炫的民族风~前面用过的 console.log(user.name)、user.sing() 都是这个套路:点号(.)就是”取这个对象身上的东西”。
方法里要拿”当前对象自己”的属性,用 this——上面 sing() 里的 this.name 就是 user.name。
课程代码里专门演示了一种错误写法:定义对象中的方法时,尽量不要使用箭头函数(this)。
1let user = {2 name: 'Tom',3 age: 18,4 gender: '男',5 sing: () => { //注意: 在箭头函数中, this并不指向当前对象 - 指向的是当前对象的父级 【不推荐】6 alert(this + ':悠悠的唱着最炫的民族风~')7 }8}箭头函数的 this 不指向”调用它的对象”,而是指向外层(父级)作用域——在我们这种顶层写法里就是 window(浏览器窗口对象),所以弹出来的是 [object Window] 而不是 Tom。给对象写方法,用 方法名(){} 或 方法名: function(){},不写箭头函数。
概念:JavaScript Object Notation,JavaScript 对象标记法(JS 对象标记法书写的文本)。
由于其语法简单,层次结构鲜明,现多用于作为数据载体,在网络中进行数据传输。
把”JS 对象”和”JSON 文本”摆在一起就能看出区别:
1// JS 对象:属性名可以不加引号,字符串可以用单引号,还能带方法2let person = {3 name: 'Tom',4 age: 20,5 gender: '男'6}1{2 "name": "Tom",3 "age": 20,4 "gender": "男"5}JSON 格式特点:json 格式的文本所有的 key 必须使用双引号引起来("name" 不能写成 name 或 'name')。
对象和 JSON 之间的转换靠两个方法(PPT 的两问两答):
| 方法 | 作用 |
|---|---|
JSON.parse(..) | 将 json 字符串转为 js 对象 |
JSON.stringify(..) | 将 js 对象转为 json 字符串 |
课程代码 05. JS-自定义对象.html 的最后一段:
1//3. JSON - JS对象标记法2let person = {3 name: 'itcast',4 age: 18,5 gender: '男'6}7alert(JSON.stringify(person)); //js对象 --> json字符串8
9let personJson = '{"name": "heima", "age": 18}';10alert(JSON.parse(personJson).name); // heima —— 先转成对象,再取 name浏览器里弹出来的第一句是:
1{"name":"itcast","age":18,"gender":"男"}为什么要有这一步? PPT 用一张”请求/响应”的图说明了数据的流向:
| 步骤 | 数据形态 |
|---|---|
| 浏览器里的 JS 对象 | { name: "Tom", age: 20, gender: "男" } |
发请求前用 JSON.stringify(..) 转成文本 | {"name":"Tom","age":20,"gender":"男"} |
| 网络传输(请求 / 响应) | JSON 文本(纯字符串,任何语言都能解析) |
收到响应用 JSON.parse(..) 转回对象 | 又是 JS 对象,可以 对象.属性名 取值 |
记住这个方向感:stringify 是”对象 → 文本”(准备发出去),parse 是”文本 → 对象”(收进来用)。后面学 Ajax / Vue 时,从后端拿到的响应就是一个 JSON 文本,第一步永远是 JSON.parse(或让 Axios 自动转)。
数组是 JS 里最常用的”容器”(14 篇的对照表里提过:Java 的数组长度固定,JS 的数组长度可变)。用中括号定义、元素用逗号隔开:
1let arr = [10, 20, 30, 'JS', true]; // 元素类型可以混搭(弱类型)2
3console.log(arr.length); // 5 —— 长度4console.log(arr[0]); // 10 —— 索引从 0 开始5console.log(arr[4]); // true| 方法 | 作用 |
|---|---|
push(元素) | 在末尾添加一个或多个元素,返回新长度 |
pop() | 删除并返回末尾的元素 |
unshift(元素) | 在开头添加元素 |
shift() | 删除并返回开头的元素 |
splice(开始索引, 删除个数, 新增元素…) | 从中间删除 / 插入元素(会改变原数组) |
slice(开始, 结束) | 截取一段,返回新数组(不改原数组) |
join(分隔符) | 把数组元素拼成字符串 |
concat(数组) | 拼接数组,返回新数组 |
indexOf(元素) | 返回元素第一次出现的索引,找不到返回 -1 |
includes(元素) | 判断是否包含某元素,返回 true / false |
forEach(函数) | 遍历每个元素(类似 for 循环) |
map(函数) | 遍历并加工每个元素,返回新数组 |
filter(函数) | 筛选符合条件的元素,返回新数组 |
1let arr = [10, 20, 30];2
3arr.push(40); // 末尾加:arr 变成 [10, 20, 30, 40]4arr.pop(); // 末尾删:arr 又变回 [10, 20, 30]5arr.splice(1, 0, 15); // 在索引 1 处插入 15:arr 变成 [10, 15, 20, 30]6
7console.log(arr.join('-')); // 10-15-20-30(拼成字符串)8console.log(arr.includes(20)); // true9
10// 遍历:箭头函数在这里最常见11arr.forEach(item => {12 console.log(item);13});数组里放对象是最常用的组合(“多条数据”就是这样存的,后面的员工列表案例全靠它):
1let users = [2 { name: 'Tom', age: 18 },3 { name: 'Jerry', age: 20 }4];5
6console.log(users.length); // 27console.log(users[0].name); // Tom —— 先按索引取出对象,再 .属性名取属性8
9for (let i = 0; i < users.length; i++) {10 console.log(users[i].name); // 依次打印 Tom、Jerry11}遍历数组最标准、最不容易出错的写法是 for (let i = 0; i < arr.length; i++)——课程后面的员工列表案例里遍历数据行用的就是它(for (let i = 0; i < trList.length; i++))。forEach()、map()、filter() 属于进阶写法,认识即可,后面写 Vue 时会大量出现。
| 问题 | 答案 |
|---|---|
| 函数是什么? | 被设计用来执行特定任务的代码块,方便程序的封装复用;用 function 定义,用 函数名称(实际参数) 调用 |
| 函数要写类型吗? | 不用——JS 是弱类型,形参、返回值都不需要指定类型;实参个数与形参个数可以不一致(建议一致) |
| 匿名函数怎么写? | 函数表达式 let add = function(a,b){...}、箭头函数 let add = (a,b) => {...};定义后通过变量名调用 |
| 自定义对象的格式? | let 对象 = {属性名: 属性值, 方法名: function(){}},方法可简写成 方法名(){} |
| 怎么访问对象成员? | 对象名.属性名、对象名.方法名();方法内用 this.属性名 取自己的属性 |
| 对象方法能用箭头函数吗? | 不要——箭头函数的 this 指向外层(window),拿不到对象自己的属性 |
| JSON 是什么? | JavaScript Object Notation(JS 对象标记法),是文本,用作数据载体在网络中传输;key 必须用双引号 |
| 两个 JSON 方法? | JSON.stringify(对象) 对象 → json 字符串;JSON.parse(字符串) json 字符串 → 对象 |
| 数组怎么定义、怎么取元素? | let arr = [10, 20, 30];arr[0] 按索引取(从 0 开始)、arr.length 拿长度 |
| 数组常用方法? | push() 末尾加、pop() 末尾删、splice() 中间删/插、join() 拼字符串、forEach() 遍历 |
function 关键字,调用写 函数名称(实际参数列表)NaN,多传的会被忽略,都不报错let add = function(a,b){...} 和 箭头函数 let add = (a,b) => {...};定义后通过变量名调用(add(10,20)){} 和 return(如 let add = (a,b) => a + b;);后面写事件监听(如 addEventListener('click', () => {...}))最常用它let 对象名 = { 属性名1: 属性值1, 属性名2: 属性值2, 方法名: function (形参列表) {} };方法还能简写成 方法名(){}对象名.属性名、对象名.方法名();方法内部用 this 取当前对象自己的属性(this.name)this 不指向当前对象,而是指向外层(父级)——顶层写法里就是 window,拿不到属性JSON.stringify(js对象) 把 js 对象转为 json 字符串;JSON.parse(json字符串) 把 json 字符串转为 js 对象[ ] 定义、索引从 0 开始、数组.length 取长度;常用方法:push() 末尾加、pop() 末尾删、splice() 中间删/插、join() 拼成字符串、includes() 判断包含、forEach() 遍历;数组里放对象就是”多条数据”的存法 2-1 写一个具名函数做加法
定义一个函数:接收两个参数,返回它们的和。调用它计算 10 + 20,把结果打印到控制台。
做完想一想:这个函数在 Java 里要写成什么样?多了哪两种类型声明?
(练习文件 test_15_具名函数.html 里已经给了骨架和写作区。)
一级 · 思路:函数 = 名字 + 参数 + 返回值;“返回”是把结果交回给调用方,还需要一个地方把结果打印出来
二级 · 方法:定义用 function 函数名(参数){ return ...; };调用写 函数名(10, 20);打印用 console.log()
三级 · 骨架:function add(____, ____){ return ____ + ____; } / let result = add(10, 20); / console.log(____);
1// 定义函数:求两个数的和2function add(a, b){3 return a + b;4}5
6// 调用函数并打印结果7let result = add(10, 20);8console.log(result); // 30对照课程 04. JS-函数.html 的第一段。Java 版本要写成 int add(int a, int b){ return a + b; }——多出的两个 int 就是因为 JS 是弱类型,形参和返回值都不用写类型。
2-2 用箭头函数重写一边加法,再故意少传一个参数 分两步:
100 和 200 传进去、打印结果test_15_箭头函数.html 里已经给了写作区。)一级 · 思路:匿名函数没有名字,要把它”存”在变量里才能调用;箭头函数用 => 连接参数和函数体
二级 · 方法:let 变量名 = (参数) => { return ...; };调用用变量名;打印用 console.log()
三级 · 骨架:let add = (a, b) => { return ____ + ____; } / let result = ____(100, 200); / 再调用一次 add(____) 观察结果
1// 箭头函数:匿名函数赋给变量 add2let add = (a, b) => {3 return a + b;4}5
6let result = add(100, 200);7console.log(result); // 3008
9// 少传一个参数:b 是 undefined10console.log(add(100)); // NaN —— 100 + undefined 得不到数字,但不报错对照课程 04. JS-函数.html 的第 2 部分(函数表达式与箭头函数二选一)。第 2 步说明:JS 里实参个数与形参个数可以不一致,缺的形参是 undefined,参与算术运算就变成 NaN(Not a Number)——这正是弱类型语言”灵活但容易埋 bug”的地方。
2-3 定义一个员工对象并打印它的属性
定义一个对象表示一名员工:姓名 Tom、年龄 18、性别 男,另外给它一个”自我介绍”的方法(打印一句带上自己名字的话)。
然后:打印这个对象的姓名和年龄,再调用它的自我介绍方法。
(练习文件 test_15_自定义对象.html 里已经给了写作区。)
一级 · 思路:对象用一对大括号装”属性名: 属性值”;方法就是”值是函数的属性”;取值用点号
二级 · 方法:let user = { name: 'Tom', age: 18, gender: '男', intro(){ ... } };取值 user.name、调用 user.intro();方法里用 this.name 拿自己的名字;打印用 console.log()
三级 · 骨架:let emp = { name: '____', age: ____, gender: '____', intro(){ console.log('我是' + ____.name); } } / console.log(emp.____); / emp.____();
1// 定义员工对象2let emp = {3 name: 'Tom',4 age: 18,5 gender: '男',6 // 方法:打印一句自我介绍7 intro(){8 console.log('我是' + this.name + ',今年' + this.age + '岁');9 }10}11
12// 访问属性13console.log(emp.name); // Tom14console.log(emp.age); // 1815
16// 调用方法17emp.intro(); // 我是Tom,今年18岁对照课程 05. JS-自定义对象.html 的第一段(课程里方法名是 sing,用 alert 弹出 this.name + '悠悠的唱着最炫的民族风~')。注意:方法不要写成箭头函数——箭头函数的 this 指向 window,this.name 就取不到 Tom 了。
2-4 写一个函数判断成绩等级
定义一个函数:接收一个分数,返回对应的等级文字——90 及以上返回 优秀、80 及以上返回 良好、60 及以上返回 及格、其余返回 不及格。
调用它分别在 95、85、72、50 四个分数上测试,把返回的等级打印出来。
(练习文件 test_15_成绩等级.html 里已经给了写作区。)
一级 · 思路:函数里做”多重条件判断”,每个分支把等级返回出去(不是打印);测试时循环几个分数调用它
二级 · 方法:function 函数名(分数){ if (… >= 90) { return '优秀'; } else if (…) { … } };打印用 console.log()
三级 · 骨架:function level(score){ if (score >= 90) { return '____'; } else if (score >= 80) { return '良好'; } else if (score >= ____) { return '及格'; } else { return '不及格'; } } / console.log(level(95));
1// 判断成绩等级2function level(score){3 if (score >= 90) {4 return '优秀';5 } else if (score >= 80) {6 return '良好';7 } else if (score >= 60) {8 return '及格';9 } else {10 return '不及格';11 }12}13
14// 测试四个分数15console.log(level(95)); // 优秀16console.log(level(85)); // 良好17console.log(level(72)); // 及格18console.log(level(50)); // 不及格三个检查点:① 判断顺序必须从高到低——score >= 60 若写在最前面,95 分也会被判成”及格”;② 每个分支用 return 把结果交回去,函数里不打印;③ return 执行后函数立刻结束,后面的 else if 不会再判断。
3-1 用”对象 + 数组 + 函数 + JSON”做一份员工列表数据 不碰页面(DOM 以后再学),先在控制台里把”多条数据”处理明白:
describe(emp):接收一个员工对象,返回一句话(如 Tom,18岁,男,讲师)——用模板字符串拼describe,把结果打印出来test_15_综合_员工列表.html 里已经给了写作区。)一级 · 思路:第 12 步是”对象装数据、数组装对象”;第 34 步是”函数加工 + 循环遍历”;第 6 步是对象与 JSON 文本之间的两个互转方法
二级 · 方法:数组 let emps = [ {…}, {…} ];函数 function describe(emp){ return …; };遍历 for (let i = 0; i < emps.length; i++);长度 emps.length;互转 JSON.stringify(emps)、JSON.parse(字符串)
三级 · 骨架:let emps = [ { name: '____', age: 18, gender: '男', position: '____' }, … ]; / function describe(emp){ return emp.name,{emp.____}岁,…; } / for (let i = 0; i < emps.____; i++) { console.log(describe(____[i])); } / let json = JSON.stringify(____); / let obj = JSON.parse(____); console.log(obj[0].____);
1//1. 用对象描述员工,放进数组(多条数据)2let emps = [3 { name: 'Tom', age: 18, gender: '男', position: '讲师' },4 { name: 'Jerry', age: 20, gender: '女', position: '班主任' },5 { name: 'Lucy', age: 22, gender: '女', position: '学工主管' }6];7
8//2. 写一个函数:接收员工对象,返回一句描述9function describe(emp){10 return `${emp.name},${emp.age}岁,${emp.gender},${emp.position}`;11}12
13//3. 遍历数组,逐个打印描述14for (let i = 0; i < emps.length; i++) {15 console.log(describe(emps[i]));16}17
18//4. 数组长度19console.log(emps.length); // 320
21//5. 对象数组 → JSON 字符串(准备发给服务器)22let json = JSON.stringify(emps);23console.log(json); // [{"name":"Tom","age":18,...}, ...]24
25//6. JSON 字符串 → 对象,再取值26let obj = JSON.parse(json);27console.log(obj[0].name); // Tom三个检查点:① describe(emp) 里用 emp.name 取的是传进来的那个对象,不能写成固定的名字;② 模板字符串的引号是反引号,${} 里放的是表达式(emp.age);③ 第 5 步打印出的 JSON 里所有 key 都是双引号的文本——它就是”网络传输时数据的样子”,第 6 步的 JSON.parse 是它的逆向操作。
如果你喜欢,那么欢迎来到我的世界!
了解更多暂未播放



"所有的相遇都是久别重逢。"—— 未知


