1、为什么需要函数
函数:function,是被设计为执行特定任务的代码块
说明:函数可以把具有相同或相似逻辑的代码“包裹”起来,通过函数调用执行这些被“包裹”的代码逻辑,有利于精简代码方便复用。
比如之前使用的alert()、prompt()和console.log()都是一些js函数,只不过已经封装好了,可以直接使用的。
2、函数使用
2.1 函数的声明
function 函数名(){
函数体
}
例:
function sayHi(){
document.write('hai~~')
}
2.2 函数名命名规范
和变量名命名基本一致
尽量小驼峰式命名法
前缀应该为动词
命名建议:常用动词约定
动词 | 含义 |
can | 判断是否可以执行某个动作 |
has | 判断是否含义某个值 |
is | 判断是否为某个值 |
get | 获取某个值 |
set | 设置某个值 |
load | 加载某些数据 |
如:
function getName(){}
function addSquares(){}
2.3 函数调用
调用语法:
函数调用,这些函数体内的代码逻辑会被执行
函数名()
注意:声明(定义)的函数必须调用才会真正被执行,使用()调用函数
函数一次声明可以多次调用,每一个函数调用函数体里面的代码会重新执行一次
2.4 函数体
函数体是函数的构成部分,它负责将相同或相似代码“包裹”起来,直到函数调用时函数体的代码才会被执行。函数的功能代码都要写在函数体当中。
// 打招呼
function sayHi(){
console.log('嗨~')
}
sayHi()
2.5 封装练习
1.封装一个函数,计算两个数的和
2.封装一个函数,计算1-100之间所有数的和
<script>
// 1.求2个数的和
function getSum(){
let num1 = +prompt(`请输入第一个数`)
let num2 = +prompt(`请输入第二个数`)
console.log(num1 + num2)
}
getSum()
// 2.求1-100累加和
function getSum100(){
let sum = 0
for(let i = 1;i <= 100; i++){
sum += i
}
console.log(sum)
}
getSum100()
</script>
3、函数传参
3.1 语法格式
参数列表
- 传入数据列表
- 声明这个函数需要传入几个数据
- 多个数据用逗号隔开
// 声明语法
function 函数名(参数列表){
函数体
}
//单个参数
function getSquare(num1){
document.write(num1 * num1)
}
//多个参数
function getSum(num1,num2){
document.write(num1 + num2)
}
//调用语法
函数名(传递的参数列表)
//例
getSquare(8)
getSum(10,20)
// 在调用函数时,需要传入几个数据就写几个,用逗号隔开
function getSum(start,end){
// 形参 形式上的参数
let sum = 0
for(let i = 1;i <= end; i++){
sum += i
}
console.log(sum)
}
getSum(1,50) // 调用的小括号里面 实参 - 实际的参数
getSum(100,200) // 实参 - 实际的参数
3.2 参数传递过程
3.3 形参和实参
形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数),默认值是undefined
实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)
形参可以理解为是在这个函数内声明的变量(比如num1 = 10)实参可以理解为是给这个变量赋值
在开发中尽量保存形参和实参个数一致
3.4 函数封装求和
采取函数封装的形式:输入2个数,计算两者的和,打印到页面中
function getSum(num1,num2){
document.write(num1 + num2)
}
getSum(1,3)
3.5 函数传参-参数默认值
形参:可以看做变量,但是如果一个变量不给值,默认是什么?
- undefined
但是如果用户不输入实参,那么3.4函数封装求和,则出现undefined + undefined结果是什么?
- NaN
可以改进下,用户不输入实参,可以给形参默认值,可以默认为0,这样程序更严谨,如下操作:
function getSum(x=0,y=0){
document.write(x + y)
}
getSum()//结果是0,而不是N
getSum(1,2) // 结果是3
说明:这个默认值只会在缺少实参参数传递时才会被执行,所以有参数会优先执行传递过来的实参,否则默认为undefined
3.6 函数封装-求学生总分
学生的分数是一个数组,计算每个学生的总分
分析:
①:封装一个求和函数
②:传递过去的参数是一个数组
③:函数内部遍历数组求和
// 1.封装函数
// 给一个参数的默认值
function getArrSum(arr = []){
//console.log(arr)
for(let i = 0; i< arr.length;i++){
sum += arr[i]
}
console.log(sum)
}
getArrSum([1,2,3,4,5]) // 15
getArrSum([11,22,33]) // 66
getArrSum() // 0
// 求 n~m 的累加和
function getSum(n = 0, m = 0){
let sum = 0
for(let i = n; i <= m; i++){
sum += i
}
console.log(sum)
}
let num1 = +prompt('请输入起始值:')
let num2 = +prompt('请输入结束值:')
// 调用函数
getSum(num1,num2)
4、函数返回值
4.1 返回值概念
返回值函数的概念:
- 当调用某个函数,这个函数会返回一个结果出来
- 这就是有返回值的函数
- 在之前已经接触了很多的函数具备返回值,,如下:
let result = prompt('请输入你的年龄:')
let result2 = parseInt('111')
- 只是这些函数是js底层内置的,我们可以直接使用
- 当然有些函数,则没有返回值
alert('我是弹框,不需要返回值')
4.2 函数返回值语法
当函数需要返回数据出去时,用return关键字
return 数据
4.3 使用函数返回值
// 函数的返回值
function fn(){
return 20
}
// 相当于执行了 fn()小括号是调用者 fn() = 20
// return 的值返回给调用者
//console.log(fn())
let re = fn()
console.log(re) // 20
细节:
- 在函数体中使用return关键字能将内部的执行结果交给函数外部使用
- return后面代码不会再被执行,会立即结束当前函数,所以return后面的数据不要换行写
- return函数可以没有return,这种情况函数默认返回值为undefined
4.4 函数返回值练习
1.求任意2个数中的最大值,并返回
2.求任意数组中的最大值并返回这个最大值
3.求任意数组中的最小值并返回这个最小值
断点调试:进入函数内部看执行过程 F11
// 1.求最大值函数
function getMax(x,y){
return x > y ? x : y
}
let max = getMax(1,2)
console.log(max) // 2
// 2.求任意数组的最大值,并且返回
function getArrValue(arr = []){
// 先准备一个manx变量存放数组的第一个值
let max = arr[0]
// 遍历比较
for(let i = 1; i < arr.length; i++){
if(max < arr[i]){
max = arr[i]
}
}
// 返回值
return max
}
let max = getArrValue([1,3,4,5,8,9])
console.log(max)
// 3.求任意数组的最小值,并且返回
function getArrValue(arr = []){
// 先准备一个manx变量存放数组的第一个值
let min = arr[0]
// 遍历比较
for(let i = 1; i < arr.length; i++){
if(min > arr[i]){
min = arr[i]
}
}
// 返回值
return min
}
let min = getArrValue([1,3,4,5,8,9])
console.log(min)
注意:返回多个数据,是使用数组来实现的
// 求任意数组的最小值和最大值,并且返回
function getArrValue(arr = []){
// 先准备一个manx变量存放数组的第一个值
let min = arr[0]
let max = arr[0] // 最大值
// 遍历比较
for(let i = 1; i < arr.length; i++){
if(min > arr[i]){
min = arr[i]
}
// 最小值
if(max < arr[i]){
max = arr[i]
}
}
// 返回值
return [min,max]
}
let newArr = getArrValue([1,3,4,5,8,9])
console.log(newArr)
4.5 函数细节补充
两个相同的函数后面的会覆盖前面的函数(就近原则)
JavaScript中 实参的个数和形参的个数可以不一致
- 如果形参过多 会自动填上undefined(了解即可)
- 如果实参过多 那么多余的实参会被忽略(函数内部有一个arguments,里面装着所有的实参)
函数一旦碰到return就不会再往下执行了 函数的结束用return
5、作用域
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
全局作用域(全局有效)
作用于所有代码执行的环境(整个script标签内部)或者一个独立的js文件
局部作用域(局部有效)
作用于函数内的代码环境,就是局部作用域。因为跟函数有关系,所以也成为函数作用域。
在JavaScript中,根据作用域的不同,变量可以分为:
全局变量 (函数外部let 的变量)
全局变量在任何区域都可以访问和修改
局部变量(函数内部let的变量)
局部变量只能在当前函数内部访问和修改
<script>
let num = 10 // 全局变量
console.log(num)
function fn(){
console.log(num)
}
fn()
// 2. 局部变量
function fun(){
let str = 'hi'
}
console.log(str) //报错:str is not defind
</script>
注意:
- 如果函数内部,变量没有声明,直接赋值,也当全局变量看,强烈不推荐
- 但是有一种情况,函数内部的形参可以看做是局部变量
<script>
function fn(){
num = 10 // 全部变量来看 强烈不允许
}
fn()
console.log(num)//10
function fun(x,y){
// 形参可以看作是函数的局部变量
console.log(x)
}
fun(1,2) //1
console.log(x) // 错误的
</script>
变量的访问原则
- 只要是代码,就至少有一个作用域
- 写在函数内部的局部作用域
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
- 访问原则:在能够访问到的情况下,先局部,局部没有在找全局
- 采取就近原则的方式来查找变量最终的值
123,先局部后全局
0,采取就近原则的方式来查找变量最终的值
案例3:结果是几?
a的值是4
b的值是22
6、匿名函数
具名函数
声明:function fn() {}
调用:fn()
匿名函数
function() {}
没有名字的函数,无法直接使用
使用方式:
- 函数表达式
- 立即执行函数
6.1 函数表达式
将匿名函数赋值给一个变量,并且通过变量名称进行调用,这个称为函数表达式
let fn = function () {
// 函数体
}
//调用
fn() // 函数名()
//其中函数的形参和实参使用跟具名函数一致
<script>
// 1.函数表达式
fn(1,2)//错误 不能接受函数在初始化之前使用
let fn = function(){
// console.log('我是函数表达式');
console.log(x + y);
}
// fn() // 我是函数表达式
fn(1,2) // 3
// 函数表达式和具名函数的不同
// 1.具名函数的调用可以写到任何位置
// 2.函数表达式,必须先声明表达式,后调用
fun()
function fun(){
console.log(1);
}
// fun()
</script>
6.2 立即执行函数
场景介绍:避免全局变量之间的污染
//方式1
(function () { console.log(11) })();
//方式2
(function () { console.log(11) }());
// 不需要调用,立即执行
<script>
(function(){
let num = 10
})(); //立即执行函数要加封号,不然会报错,最后这个括号是调用函数
(function(){
let num = 20
})();
//方式1
(function(x,y){
console.log(x + y)
})(1,2);
//方式2
(function (x,y) {
console.log(x + y)
} (1,3));
</script>
注意:多个立即执行函数要用;隔开,要不然会报错
7、综合案例
转换时间案例
需求:用户输入秒数,可以自动转换为时分秒
分析:
①:用户输入总秒数(注意默认值)
②:计算时分秒(封装函数)里面包含数字补0
③:打印输出
计算公式:计算时分秒
小时: h = parseInt(总秒数 / 60 / 60 % 24)
分钟:m = parseInt(总秒数 / 60 % 60)
秒数: s = parseInt(总秒数 % 60)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1.用户输入
let second = +prompt('请输入秒数:')
// 2.封装函数
function getTime(t){
// console.log(t); // 总的秒数
// 3.转换
//小时: h = parseInt(总秒数 / 60 / 60 % 24)
//分钟:m = parseInt(总秒数 / 60 % 60)
//秒数: s = parseInt(总秒数 % 60)
h = parseInt(t / 60 / 60 % 24)
m = parseInt(t / 60 % 60)
s = parseInt(t % 60)
h = h < 10 ? '0' + h : h
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
// console.log(h,m,s)
return `转换完毕之后是${h}小时${m}分${s}秒`
}
let str = getTime(second)
document.write(str)
</script>
</body>
</html>
8、逻辑中断
在开发中,会见到以下写法:
其实类似参数的默认值写法
8.1 逻辑运算符里的短路
- 短路:只存在于&&和||中,当满足一定条件会让右边代码不执行
符号 | 短路条件 |
&& | 左边为false就短路 |
|| | 左边为true就短路 |
- 原因:通过左边能得到整个式子的结果,因此没必要在判断右边
- 运算结果:无论&&还是||,运算结果都是最后被执行的表达式值,一般用在变量赋值
如果&&两边都是真,则返回最后一个真值
如果||都是真,则输出第一个真值
9、转换为Boolean型
9.1显示转换
Boolean(内容)
记忆:''、0、undefined、null、false、NaN转换为布尔值后都是false,其余则为true
9.2 隐式转换
- 有字符串的加法"" +1 ,结果是"1"
- 减法 - (像大多数数学运算一样)只能用于数字,它会使空字符串""转换为0
- null经过数字转换之后会变为0
- undefined经过数字转换之后会变为NaN