1、什么是函数 函数 可以理解为一个盒子
2、写一个函数(或者说一个完整的函数)
- 分为两部分
-
- 定义阶段
-
- 调用阶段
3、函数的定义
语法: function () {}
两种定义函数的方式
-
赋值式定义 var fn = function () {}
-
声明式定义 function 函数名() {}
eg:: 赋值式定义
var fn = function () {}
console.log(fn)
eg: // 声明式定义
function fn1() {}
console.log(fn1)
3、函数的调用
不管函数定义是如何定义的, 调用方式永远只有一种
语法: 函数名()
4、函数声明式和赋值式的区别
- 书写不同
- 打印时声明式会带上函数名, 赋值式没有
- 调用时有差异声明式: 可以在函数定义前调用
5、函数的参数
参数分为两个
形参 函数名后(function) 后边的小括号内部, 每书写一个 就相当于在函数内部声明一个变量, 可以在函数内部去调用
实参 调用函数时的小括号内部书写的值, 就是实参, 实参与形参为 一一对应的关系
形参与实参会一一对应
-
如果实参数量 少于 形参 那么前边的形参对应接受值, 没有对应实参 的 形参 值 为 undefined
-
如果形参数量 少于 实参 那么会一一对应后, 多余的实参无法通过 参数获取
6、函数的返回值
语法: return 需要返回的内容
函数默认返回值
- 我们可以不写 return 函数 会默认在代码段最后一行 写上 retrun undefined
return功能
-
具有中断函数执行的功能
-
如果不想中断函数, 那么一定要将 return 放在函数的最后
-
除非就是想要通过 return 中断函数的运行
return 中断函数
<script>
• function fn() {
• console.log(1)
• return
• console.log(2)
• console.log(3)
• console.log(4)
• console.log(5)
• }
• fn()
</script>
7、函数的预解析
- 函数的预解析, 可以能会遇上同名变量的变量提升(变量的预解析) 直接以函数为主
* 书写代码
*
* fn()
function fn() {
console.log(1)
}
fn()
预解析之后(会把函数提升到当前作用域最顶层) (暂时理解为 当前代码最开始的第一行)
1. fn() 不需要
2. function fn() {} 需要预解析
3. fn() 不需要
1. function fn() {} // 提升到了当前代码最开始的第一行
2. fn() // 正常调用
3. fn() // 正常调用
*
* var fn = 'hhh'
console.log(fn)
fn()
function fn() {
console.log(1)
}
fn()
在 JS 中 只有变量和函数才会有预解析或者 提升
1. var fn = 'hhh' // 有提升
2. console.log(fn) // 没有
3. fn() // 没有
4. function fn() { // 有提升
console.log(1)
}
5. fn() // 没有
预解析之后
1. function fn() {
console.log(1)
}
2. var fn; // 没意义
3. fn = 'hhh' // 这行很重要, 直接将变量内保存的 函数 更改为了 'hhh'
4. console.log(fn) // 按照分析, 此处应该打印 'hhh'
5. fn() // 按照分析, 此处 fn 的值应该 'hhh' 所以会报错 fn is not a function
6. fn() // 按照分析, 此处 fn 的还是 'hhh' ,所以还会报错 fn is not a function
8、函数的作用域
1、变量使用使用区间的, 变量不是说声明之后在哪里都可以用, 他有一个使用的范围, 我们把这个范围叫做 作用域
2、作用域分为两种
1、全局作用域
JS 给我们提供了一个叫做 window 的全局作用域, 可以理解为 整个 script 标签内的作用域, 就是全局作用域
全局变量都会挂载到 window 对象上
2、局部作用域
在 JS 中, 有 且只有 函数能够创建 局部作用域(函数作用域), 局部作用域开始和结束位置, 就是函数代码段的开始和结束位置
在局部作用域(函数作用域)内 声明的变量叫做局部变量
局部变量 不会挂载到 window 对象上
使用感受: 以局部变量为荣, 以全局变量为耻
9、作用域链
1、什么是作用域链
在查找变量时, 会先在当前作用域内查找, 找到就用, 没找到去上层查找, 一直会查找到顶层作用域(全局---window)
然后在查找过程中, 我们把逐层向上的一层一层查找 所构成的一个链条 叫做作用域链(实际是没有作用域链的,这是一个纯概念性的东西)
2、变量的访问规则
变量访问会 先在当前作用域内查找, 找到拿来直接用, 如果没有找到, 会去上层作用域查找, 找到直接用
如果上层作用域没找到, 会继续去上层作用域 的 上层作用域内查找, 找到直接用, 没找到会继续往上
如果找到了全局作用域内 还是没有变量, 那么就会报错 num is not defined
3、变量的赋值规则
变量赋值会 现在当前作用域内查找, 找到直接拿来赋值, 如果没找到, 会去上层作用域查找, 找到直接赋值 如果上层作用域也没找到, 会继续去上层作用域 的 上层作用域内查找, 找到直接赋值, 没找到继续往上 如果找到了全局作用域内, 还是没有找到变量, 那么会直接将变量定义在当前作用域内(全局作用域)然后赋值
重点: 作用域链 只会向上查找, 不会向下
10、递归函数
在函数内部, 调用自身, 此时就是写了一递归, 但 它是死递归。要想写一个正确的递归函数, 需要在递归内部, 写上 返回点(到某个条件时, 停止递归)
function fn() {
if (满足条件1) {
fn()
}
if (不满条件1, 满足条件2) {
// 结束递归函数
}
}
fn()
function fn(n) {
// 1. 先写折返点, 到某个条件停止递归
if (n == 1) return 1
// 2. 不符合结束递归时的代码
return n * fn(n - 1)
}