学习目标:
掌握函数的基本使用,让代码具备复用能力
理解封装的意义,能够具备封装函数的能力
学习内容:
函数
综合案例
学习时间:
- 周一至周五晚上 7 点—晚上9点
- 周六上午 9 点-上午 11 点
- 周日下午 3 点-下午 6 点
学习产出:
函数
-
• 为什么需要函数
关于封装函数“
封住简单的意思 就是减少代码的重复性
体验函数的魅力
代码复用
目标: 能说出为什么需要函数
l 函数:
function,是被设计为执行特定任务的代码块
l 说明:
函数可以把具有相同或相似逻辑的代码“包裹”起来,通过函数调用执行这些被“包裹”的代码逻辑,这么做的优势是有利于
精简代码方便复用。
比如我们前面使用的 alert() 、 prompt() 和 console.log() 都是一些 js 函数,只不过已经封装好了,我们直接使用的
函数是编程中非常重要的概念,它具有以下几方面的作用:
-
代码复用:通过函数可以将一段常用的代码封装起来,以便在其他地方重复使用,避免重复编写相同的代码,提高代码的复用性和可维护性。
-
模块化编程:通过将程序分解为多个相互独立、具有特定功能的函数,可以使程序结构更加清晰明了,便于理解和维护。
-
提高代码的可读性:将复杂的操作封装成函数,可以使代码更加简洁和易懂,减少重复性的代码,便于阅读和理解。
-
方便调试和测试:在程序中使用函数,可以更方便地进行调试和测试。由于函数是独立的模块,可以单独测试每个函数的功能,减小问题的范围,提高调试效率。
-
提高代码的可维护性:将程序分解为多个函数,每个函数负责完成一个具体的功能,有助于代码的维护和修改。当需要修改某个功能时,只需要修改对应的函数,而不需要修改整个程序。
总之,函数在编程中起到了重要的作用,可以提高代码的复用性、可维护性和可读性,使程序更加模块化、易于调试和测试。
-
• 函数使用
目标:掌握函数语法,把代码封装起来
函数的声明语法
函数名命名规范
在JavaScript中,函数的命名需要遵循一些规范,以提高代码的可读性和可维护性。以下是一些常见的命名规范:
-
使用有意义的名称:函数的名称应该能够清晰地表达其功能和用途,避免使用过于抽象或模糊的名称。
-
使用驼峰命名法:函数名应该采用驼峰命名法,即第一个字母小写,后续单词首字母大写。例如:calculateTotalAmount。
-
使用动词或动词短语作为前缀:函数名称应该以动词或动词短语开头,这样可以更清晰地表达函数的行为。例如:calculateTotalAmount,getUserName,sendEmail。
-
避免使用单个字母作为函数名称:函数名应该具有一定的描述性,避免使用过于简短的名称,除非名称能够明确表达函数的意义。
-
遵循命名约定:在JavaScript中,通常使用小写字母开头的函数名表示普通函数,使用大写字母开头的函数名表示构造函数或类。
-
尽量避免使用保留字:避免使用JavaScript的保留字或关键字作为函数名,以免产生命名冲突或错误。
总之,函数的命名应该具有描述性、清晰明了,遵循驼峰命名法,使用动词或动词短语作为前缀,并避免使用保留字或过于简短的名称。这些规范可以提高代码的可读性,并使函数的用途和功能更加清晰明确。
函数的调用语法
function functionName(parameter1, parameter2, ...) {
// 函数体
return result; // 可选
}
函数体
函数体是函数的构成部分,它负责将相同或相似代码“包裹”起来,直到函数调用时函数体内的代码才会被执行。函
数的功能代码都要写在函数体当中。
总结一下:
1. 函数是用那个关键字声明的?
Ø function
2. 函数不调用会执行吗?如何调用函数?
Ø 函数不调用自己不执行
Ø 调用方式: 函数名()
3. 函数的复用代码和循环重复代码有什么不同?
Ø 循环代码写完即执行,不能很方便控制执行位置
Ø 随时调用,随时执行,可重复调用
-
• 函数传参
关于函数传参大家要思考一个问题:这样的函数只能求 10 + 20, 这个函数功能局限非常大
解决方法:
如果一个函数只能求 10 + 20,其功能的局限非常大,这意味着它只能执行一个固定的操作,无法应对不同的输入和需求。这样的函数缺乏灵活性和可扩展性,无法适应复杂的问题和多样的场景。
为了提高函数的功能性和适用性,可以考虑以下几个方面:
-
参数的设计:通过调整函数的参数,可以使函数具有更多的灵活性。可以将参数设置为变量,允许传入不同的数值,而不是固定的值。
-
参数的类型:考虑参数的类型,可以使函数能够处理不同类型的输入。例如,可以使用泛型参数或重载函数,以适应不同的数据类型。
-
参数的数量:通过增加参数的数量,可以传递更多的信息和控制选项,使函数更加通用。可以使用可选参数、默认参数或者参数对象等方法来扩展函数的功能。
-
函数的复用性:将函数的功能进行拆分,将其拆分成多个小的子函数,每个子函数负责解决一个小的问题,然后通过组合这些子函数来实现复杂的功能。
-
函数的设计:在设计函数时,要考虑函数的可扩展性和复用性。尽量避免硬编码固定的数值和操作,而是通过参数来控制函数的行为。
通过以上的方法和思考,可以使函数更加通用、灵活和可扩展,从而可以应对不同的输入和需求。
结论:
Ø 若函数完成功能需要调用者传入数据,那么就需要用有参数的函数
Ø 这样可以极大提高函数的灵活性
声明语法
参数列表
Ø 传入数据列表
Ø 声明这个函数需要传入几个数据
Ø 多个数据用逗号隔开
在JavaScript中,函数可以接受单个参数或多个参数。以下是两种不同的方式来传递参数:
-
单个参数:
- 可以直接在函数定义中声明一个参数,然后在调用函数时传递一个值作为参数。例如:
function square(num) { return num * num; } console.log(square(5)); // 输出 25
-
多个参数:
- 可以在函数定义中声明多个参数,用逗号分隔,并在调用函数时按照参数的顺序传递多个参数值。例如:
function sum(a, b) { return a + b; } console.log(sum(3, 5)); // 输出 8
使用单个参数或多个参数取决于函数的需求。如果函数只需要一个输入值,那么使用单个参数是更简洁和直观的方式。如果函数需要多个输入值,那么使用多个参数可以提供更强大的灵活性和功能。在函数定义时根据需求选择适合的参数个数,以便在调用函数时传递相应的参数值。
形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数)
Ø 实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)
Ø 形参可以理解为是在这个函数内声明的变量(比如 num1 = 10)实参可以理解为是给这个变量赋值
Ø 开发中尽量保持形参和实参个数一致
Ø 我们曾经使用过的 alert('打印'), parseInt('11'), Number('11') 本质上都是函数调用的传参
总结:
在JavaScript中,有一些关于函数形参和实参的要点需要注意:
-
形参:函数定义时声明的参数称为形参。形参是函数内部的变量,用于接收调用函数时传递的实参值。形参在函数内部被当作局部变量使用。
function greet(name) { // name 是形参 console.log("Hello, " + name); }
-
实参:调用函数时传递给函数的值称为实参。实参是调用函数时传递的具体数值或变量。
greet("Alice"); // "Alice" 是实参
-
形参与实参的对应:当调用函数时,实参的值会按照顺序与函数定义中的形参一一对应。形参和实参的对应关系决定了实参值如何赋给形参,在函数执行时使用。
function add(a, b) { console.log(a + b); } add(3, 5); // 形参 a 对应实参 3,形参 b 对应实参 5
-
形参默认值:在函数定义时,可以为形参指定默认值,这样在调用函数时如果没有传递相应的实参,形参将使用默认值。
function multiply(a, b = 2) { console.log(a * b); } multiply(3); // 形参 a 对应实参 3, 形参 b 没有传递,默认值为 2
-
参数个数:函数的形参个数可以是任意的,可以是零个,一个或多个。如果实参的个数少于形参的个数,则未传递的形参将被赋值为 undefined。而如果实参的个数多于形参的个数,则多余的实参将被忽略。
function greet(name) { console.log("Hello, " + name); } greet(); // 形参 name 没有传递,值为 undefined greet("Alice", 25); // 形参 name 对应实参 "Alice",多余的实参 25 被忽略
-
参数传递的顺序:在调用函数时,实参的值按照顺序传递给函数的形参。可以根据需要在调用函数时,以任意顺序传递实参,但是实参值会按照位置与形参一一对应。
function greet(firstName, lastName) { console.log("Hello, " + firstName + " " + lastName); } greet("Alice", "Smith"); // 形参 firstName 对应实参 "Alice",形参 lastName 对应实参 "Smith" greet("Smith", "Alice"); // 形参 firstName 对应实参 "Smith",形参 lastName 对应实参 "Alice"
理解这些要点可以帮助我们正确传递参数,并在函数内部正确处理和使用这些参数的值。
函数封装求和
需求:采取函数封装的形式:输入2个数,计算两者的和,打印到页面中
函数传参-参数默认值
形参: 可以看做变量,但是如果一个变量不给值,默认是什么?
Ø undefined
但是如果做用户不输入实参,刚才的案例,则出现 undefined + undefined 结果是什么?
Ø NaN
我们可以改进下,用户不输入实参,可以给 形参默认值,可以默认为 0, 这样程序更严谨,可以如下操作:
说明:这个默认值只会在缺少实参参数传递时 才会被执行,所以有参数会优先执行传递过来的实参, 否则默认为
undefined
-
• 函数返回值
提问:什么是函数?
函数是被设计为执行特定任务的代码块
l 缺点:把计算后的结果处理方式写死了,内部处理了
l 解决:把处理结果返回给调用者
l 有返回值函数的概念:
Ø 当调用某个函数,这个函数会返回一个结果出来
Ø 这就是有返回值的函数
l 提问:执行完特定任务之后,然后呢?
把任务的结果给我们
函数返回值是指函数执行完毕后,将要返回给调用者的值。通过使用关键字 return
可以将某个值作为函数的返回值。
下面是一些关于函数返回值的要点:
-
单个返回值:函数可以返回一个值,可以是任何数据类型,包括数字、字符串、布尔值、对象等。
function add(a, b) { return a + b; } let result = add(3, 5); console.log(result); // 输出 8
-
多个返回值:JavaScript中的函数只能返回一个值。但是可以将多个值封装到一个对象中,然后返回该对象。
function calculate(a, b) { return { sum: a + b, difference: a - b, product: a * b }; } let result = calculate(8, 3); console.log(result.sum); // 输出 11 console.log(result.difference); // 输出 5 console.log(result.product); // 输出 24
-
返回 undefined:如果函数没有显式地使用
return
关键字返回值,或者return
后面没有表达式,则函数的返回值为undefined
。function doSomething() { // 没有 return 语句 } let result = doSomething(); console.log(result); // 输出 undefined
-
提前终止函数执行:在函数中,可以使用
return
关键字来提前终止函数的执行,并返回一个值。在这种情况下,函数会立即停止执行,并将返回值返回给调用者。function checkNumber(num) { if (num < 0) { return "Negative number"; } if (num > 0) { return "Positive number"; } return "Zero"; } let result = checkNumber(-5); console.log(result); // 输出 "Negative number"
理解函数返回值的使用可以帮助我们在函数中获取计算结果或处理完毕的数据,并根据需要将其返回给调用者。
其实我们前面已经接触了很多的函数具备返回值:
是的,你说得对。在前面我们已经接触了很多具有返回值的函数。下面是一些例子:
-
数学运算函数:
function add(a, b) { return a + b; } let result = add(3, 5); console.log(result); // 输出 8
-
字符串处理函数:
function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } let result = capitalize("hello"); console.log(result); // 输出 "Hello"
-
数组操作函数:
function findMax(numbers) { let max = numbers[0]; for (let i = 1; i < numbers.length; i++) { if (numbers[i] > max) { max = numbers[i]; } } return max; } let numbers = [5, 3, 8, 1, 9]; let result = findMax(numbers); console.log(result); // 输出 9
-
对象方法:
let person = { firstName: "John", lastName: "Doe", getFullName: function() { return this.firstName + " " + this.lastName; } }; let fullName = person.getFullName(); console.log(fullName); // 输出 "John Doe"
在这些例子中,函数执行完毕后返回了一个值,可以将其赋值给一个变量,用于后续操作或输出。函数返回值可以根据需要进行使用,使函数具有更大的灵活性和功能。
当函数需要返回数据出去时,用return关键字
l 语法
细节:
Ø 在函数体中使用 return 关键字能将内部的执行结果交给函数外部使用
Ø return 后面代码不会再被执行,会立即结束当前函数,所以 return 后面的数据不要换行写
Ø return函数可以没有 return,这种情况函数默认返回值为 undefined
函数返回值练习
1. 求任意2个数中的最大值, 并返回
1. 求任意数组中的最大值并返回这个最大值
2. 求任意数组中的最小值并返回这个最小值
函数细节补充:
当涉及到函数形参和实参的一些细节时,我们可以考虑以下几个要点:
-
形参和实参的数量应匹配:函数声明时指定的形参数量应与在调用函数时传递的实参数量相匹配。如果实参数量少于形参数量,那么未传递的形参将被赋值为
undefined
。如果实参数量多于形参数量,额外的实参将被忽略。在某些情况下,我们可以使用剩余参数(rest parameters)来接收任意数量的实参。 -
形参和实参的位置应匹配:当传递实参给函数时,其位置应与在函数声明或函数表达式中指定的形参位置相匹配,即第一个实参对应第一个形参,第二个实参对应第二个形参,依此类推。
-
实参的值会被传递给对应的形参:在函数调用时,实参的值会被传递给对应的形参,形参将被赋予实参的值。这意味着在函数内部,我们可以使用形参来访问传递给函数的实参值。
-
形参可以用于定义默认值:在函数声明或函数表达式中,我们可以为形参指定默认值。如果在函数调用时未传递对应的实参,形参将取默认值。这使得函数的使用更加灵活,可以避免错误或缺少参数的情况。
-
函数可以返回一个值:函数可以通过使用
return
关键字来返回一个值。返回值可以是任何有效的JavaScript表达式。在函数中遇到return
语句时,函数会立即停止执行并返回指定的值。如果函数没有指定返回值,或者没有return
语句,函数将返回undefined
。
总结一下,函数形参和实参的要点是:数量匹配、位置匹配、传递值、默认值和返回值。了解这些要点有助于我们正确地定义和使用函数,并确保函数的正常运行和预期的结果。
-
• 作用域
在讲解作用域请大家思考一下:
function myFunction() {
myVariable = 10; // 没有使用 var/let/const 声明变量
}console.log(myVariable); // 10
function myFunction() {
myVariable = 10; // 没有使用 var/let/const 声明变量
}console.log(myVariable); // 10
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
全局作用域
局部作用域
全局有效
局部有效
作用于所有代码执行的环
境(整个 script 标签内部)
或者一个独立的 js 文件
作用于函数内的代码环境,就是
局部作用域。 因为跟函数有关系,
所以也称为函数作用域。
作用域是在编程语言中用来描述变量和函数的可访问性和可见性的概念。它规定了在不同上下文环境中哪些变量和函数可以被访问。
在JavaScript中,有三种主要的作用域:
-
全局作用域(Global Scope):全局作用域是在整个代码中可访问的顶层作用域。在全局作用域中声明的变量和函数可以在代码中的任何位置被访问。
-
函数作用域(Function Scope):函数作用域是在函数内部声明的变量和函数只在函数内部可见,无法在函数外部访问。每次调用函数时都会创建一个新的函数作用域。
function myFunction() {
var x = 10; // 只在函数内部可见
}
console.log(x); // ReferenceError: x is not defined
- 块级作用域(Block Scope):块级作用域是在代码块(例如,if语句、for循环、while循环等)中声明的变量。这些变量只在代码块内部可见,不会被外部代码所访问。
if (true) {
var x = 10; // 只在if语句块内可见
}
console.log(x); // 10 (在某些情况下,这是一个JavaScript的特殊行为,应使用let或const关键字声明变量以创建块级作用域)
在ES6及以后的JavaScript版本中,let
和const
关键字引入了块级作用域。使用这两个关键字声明的变量只在声明所在的块级作用域内可见,不会被外部代码访问。
作用域的概念对于理解变量的可见性和访问性非常重要,可以帮助我们编写更清晰、可维护和避免命名冲突的代码。
总结:
1. JS 中作用域分为哪2种?
Ø 全局作用域。函数外部或者整个script 有效
Ø局部作用域。也称为函数作用域,函数内部有效
2. 根据作用域不同,变量分为哪2种?
Ø 全局变量
Ø局部变量
3. 有一种特殊情况是全局变量是那种?我们提倡吗?
Ø 局部变量或者块级变量 没有let 声明直接赋值的当全局变量看
Ø 我们强烈不提倡
Ø 还有一种特殊情况,函数内部的形参可以当做局部变量看
变量的访问原则
变量的访问原则是指在程序中如何访问和使用变量的规则。以下是一些常见的变量访问原则:
-
变量的作用域:变量的作用域决定了它在程序中可以被访问的范围。一般来说,变量可以是全局作用域(在整个程序中都可以访问)或局部作用域(只能在特定的代码块或函数中访问)。
-
变量的可见性:变量的可见性指的是该变量在程序中的哪些地方可以被访问。一般来说,变量可以是公有的(可以在整个程序中被访问),私有的(只能在特定的类或对象中被访问)或受保护的(只能在特定的类或子类中被访问)。
-
变量的命名规则:变量的命名规则指定了给变量赋予的名称应该遵循的规则。一般来说,变量的名称应该是有意义的、易于理解和记忆的,并且符合编程语言的命名规范(例如,不能以数字开头,只能包含字母、数字和下划线等)。
-
变量的生命周期:变量的生命周期指的是它在程序执行过程中存在的时间段。一般来说,变量可以是局部变量(在代码块或函数执行期间存在)或全局变量(在整个程序执行期间存在)。
通过遵守这些变量访问原则,可以提高程序的可读性、可维护性和安全性。
let a = 1 function fn1() { let a = 2 let b = '22' fn2() function fn2() { let a = 3 fn3() function fn3() { let a = 4 console.log(a) //a的值 ? console.log(b) //b的值 ? } } } fn1()
-
• 匿名函数
函数可以分为:
在JavaScript中,函数也可以分为两类:命名函数和匿名函数。
-
命名函数:使用function关键字定义,可以给函数取一个名字,以便在其他地方调用。 例如: function add(x, y) { return x + y; }
console.log(add(3, 5)); // 输出8
在上面的例子中,函数add是一个命名函数,可以通过函数名add来调用,传入参数3和5,得到结果8。
-
匿名函数:没有名字的函数,可以直接在需要的地方定义和使用。 例如: let sum = function(x, y) { return x + y; };
console.log(sum(3, 5)); // 输出8
在上面的例子中,函数sum是一个匿名函数,可以使用变量sum来调用,传入参数3和5,得到结果8。
匿名函数通常用于需要临时定义和执行的情况,特别是作为另一个函数的参数或返回值。
函数表达式
在JavaScript中,匿名函数的基本语法是使用function关键字定义一个函数,但没有为函数指定一个名字。基本语法如下:
let functionName = function(parameters) {
// 函数体
// 可以包含一系列语句和逻辑
};
这里的functionName
表示一个变量,用于存储匿名函数。匿名函数可以接受参数,参数列表在圆括号内。函数体定义在花括号内,可以包含一系列的语句和逻辑。
匿名函数可以根据需要在任何需要的地方使用,如下所示:
- 调用匿名函数:可以直接通过变量名调用匿名函数,如
functionName(parameters)
。
let sum = function(x, y) {
return x + y;
};
console.log(sum(3, 5)); // 输出8
- 作为其他函数的参数:匿名函数可以作为其他函数的参数传递进去。
let numbers = [1, 2, 3, 4, 5];
let sum = function(x, y) {
return x + y;
};
let result = numbers.reduce(sum);
console.log(result); // 输出15,求和结果
在上面的例子中,reduce()
函数接受一个函数作为参数,这个函数就是一个匿名函数sum
。
- 自执行匿名函数:匿名函数可以立即执行,不需要通过变量名调用。
(function() {
// 函数体
// 可以包含一系列语句和逻辑
})();
这种自执行匿名函数常用于创建一个局部作用域,避免污染全局命名空间。
以上是匿名函数的基本语法和使用方法,可以根据具体的需求和场景来决定是否使用匿名函数。
立即执行函数
场景介绍: 避免全局变量之间的污染
在ES6中,我们可以使用箭头函数和块级作用域来创建立即执行函数,以避免全局变量之间的污染。
箭头函数是一种匿名函数的简写形式,它可以更简洁地定义函数。在箭头函数内部,this的指向是固定的,并且不会创建自己的作用域。
下面是使用箭头函数创建立即执行函数的语法:
(() => {
// 在这里编写代码
})();
通过将箭头函数用括号包裹,并加上一个后面的括号立即执行,就可以创建一个立即执行的函数。
举个例子,假设我们有一个全局变量count,我们使用箭头函数来创建一个立即执行函数,增加它的值,但不影响全局作用域中的count变量:
let count = 0;
(() => {
let count = 10;
count++;
console.log(count); // 输出: 11
})();
console.log(count); // 输出: 0
在这个例子中,全局的count变量为0,而在箭头函数内部创建的块级作用域中的count为10,对它进行自增操作后输出11。而在箭头函数外部访问count时,输出的是全局变量的值0。
使用ES6的箭头函数和块级作用域,我们可以更清晰地创建立即执行函数,并避免全局变量之间的污染。这是一种更现代化和易读的方式来实现这一功能。
综合案例:
需求: 用户输入秒数,可以自动转换为时分秒
需求: 用户输入秒数,可以自动转换为时分秒
分析:
①: 用户输入总秒数 (注意默认值)
②:计算时分秒(封装函数) 里面包含数字补0
③:打印输出
代码:
下面是使用ES6箭头函数和模板字面量的代码实现用户输入秒数并自动转换为时分秒:
const convertSecondsToTime = (seconds) => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
const formatNumber = (number) => {
return String(number).padStart(2, '0');
}
return {
hours: formatNumber(hours),
minutes: formatNumber(minutes),
seconds: formatNumber(remainingSeconds)
};
};
const totalSeconds = parseInt(prompt('请输入总秒数:', 0));
const { hours, minutes, seconds } = convertSecondsToTime(totalSeconds);
console.log(`${hours}:${minutes}:${seconds}`);
这段代码与之前的代码几乎相同,只是将函数表达式转换为箭头函数,并使用箭头函数的简洁写法。
箭头函数 convertSecondsToTime
接收一个秒数作为参数,并返回一个包含时、分、秒的对象。在函数内部,它仍然使用了 Math.floor
函数来计算小时和分钟,使用取余操作符计算剩余的秒数。
辅助函数 formatNumber
仍然用来将数字转换为两位数格式。
最后,使用了模板字面量将时、分、秒格式化为时分秒形式,并通过 console.log
输出结果。
在浏览器的控制台或Node.js环境中运行该代码,使用ES6的语法风格完成用户输入秒数转换为时分秒的需求。