一、函数
1、函数的概念
- 对于 js 来说,函数就是把任意一段代码放在一个 **盒子** 里面
- 在我想要让这段代码执行的时候,直接执行这个 **盒子** 里面的代码就行
2、函数的两个阶段
-**放在盒子里面** 和 **让盒子里面的代码执行**
①函数定义阶段
- 定义阶段就是我们把代码 **放在盒子里面**
- 我们要怎么 **放进去**,也就是书写一个函数
- 我们有两种定义方式 **声明式** 和 **赋值式**
####声明式
- 使用 `function` 这个关键字来声明一个函数
- 语法:
function fn() {
// 一段代码
}
// function: 声明函数的关键字,表示接下来是一个函数了
// fn: 函数的名字,我们自己定义的(遵循变量名的命名规则和命名规范)
// (): 必须写,是用来放参数的位置
// {}: 就是我们用来放一段代码的位置(也就是我们刚才说的 “盒子”)
```
#### 赋值式
- 其实就是和我们使用 `var` 关键字是一个道理了
- 首先使用 `var` 定义一个变量,把一个函数当作值直接赋值给这个变量就可以了
- 语法:
```javascript
var fn = function () {
// 一段代码
}
// 不需要在 function 后面书写函数的名字了,因为在前面已经有了
```
②、函数调用阶段
- 就是让 **盒子里面** 的代码执行一下
- 让函数执行
- 两种定义函数的方式不同,但是调用函数的方式都以一样的
#### 调用一个函数
- 函数调用就是直接写 `函数名()` 就可以了
总结: 1.2种定义函数的区别
声明式 :可以先定义在调用 也可以先调用在定义
赋值式: 只能先定义在调用 否则会报错为 undefined
2.函数的调用
1.函数名()
2.通过事件触发来调用函数;
3、函数的参数
- 我们在定义函数和调用函数的时候都出现过 `()`
- 现在我们就来说一下这个 `()` 的作用
- 就是用来放参数的位置
- 参数分为两种 **行参** 和 **实参**
```javascript
// 声明式
function fn(行参写在这里) {
// 一段代码
}
fn(实参写在这里)
// 赋值式函数
var fn = function (行参写在这里) {
// 一段代码
}
fn(实参写在这里)
```
①、行参和实参的作用
1.行参
- 就是在函数内部可以使用的变量,在函数外部不能使用
- 每写一个单词,就相当于在函数内部定义了一个可以使用的变量(遵循变量名的命名规则和命名规范)
- 多个单词之间以 `,` 分隔
- 如果只有行参的话,那么在函数内部使用的值个变量是没有值的,也就是 `undefined`
- **行参的值是在函数调用的时候由实参决定的**
2. 实参
- 在函数调用的时候给行参赋值的
- 也就是说,在调用的时候是给一个实际的内容的
- **函数内部的行参的值,由函数调用的时候传递的实参决定**
- **多个参数的时候,是按照顺序一一对应的**
①. 行参比实参少
- 因为是按照顺序一一对应的
- 行参少就会拿不到实参给的值,所以在函数内部就没有办法用到这个值
②. 行参比实参多
- 因为是按照顺序一一对应的
- 所以多出来的行参就是没有值的,就是 `undefined`
3.不定参 :参数的个数不确定
// 计算所有参数的和 但是参数的个数在调用的时候不确定 ;
// 通过隐藏参数接收不定参数 :每个函数内部 都会预定一个变量 叫 arguments 里面放的是调用时候的实参
****** curry:咖喱,科里化:把多元参数转换成一元参数的过程;
// var res = fn(1)(2)(3);
// console.log(res);
// // y = a + b + z;
// function fn(a,b,c){
// return a + b + c;
// }
4、函数的return
- return 返回的意思,其实就是给函数一个 **返回值** 和 **终断函数**
- 函数调用本身也是一个表达式,表达式就应该有一个值出现
- 现在的函数执行完毕之后,是不会有结果出现的
-`return` 关键字就是可以给函数执行完毕一个结果
5、函数的优点
- 函数就是对一段代码的封装,在我们想调用的时候调用
- 函数的几个优点
1. 封装代码,使代码更加简洁
2. 复用,在重复功能的时候直接调用就好
3. 代码执行时机,随时可以在我们想要执行的时候执行
二、解释代码
- 因为是在所有代码执行之前进行解释,所以叫做 **预解析(预解释)**
- 需要解释的内容有两个
- 声明式函数
- 在内存中先声明有一个变量名是函数名,并且这个名字代表的内容是一个函数
- `var` 关键字
- 在内存中先声明有一个变量名
- 赋值是函数会按照 `var` 关键字的规则进行预解析
1. 变量的提升(hosting),声明前置;
预解析:js 执行之前 会把变量做提升 ,提升之后再执行;
2.函数的提升;
预解析 :无论你在哪里定义函数 ,在预解释代码阶段 都会把函数 提升到最顶部;
3. 既有变量提升 也有 函数提升;
函数提升优先于变量的提升;
4.在函数内部的预解析
①.函数的提升最大
②.变量的提升
③.最后考虑参数
二、作用域
1、作用域
- 什么是作用域,就是一个变量可以生效的范围
- 变量不是在所有地方都可以使用的,而这个变量的使用范围就是作用域
①全局作用域
- 全局作用域是最大的作用域
- 在全局作用域中定义的变量可以在任何地方使用
- 页面打开的时候,浏览器会自动给我们生成一个全局作用域 window
- 这个作用域会一直存在,直到页面关闭就销毁了
②局部作用域
- 局部作用域就是在全局作用域下面有开辟出来的一个相对小一些的作用域
- 在局部作用域中定义的变量只能在这个局部作用域内部使用
- **在 JS 中只有函数能生成一个局部作用域,别的都不行**
2、变量使用规则
- 有了作用域以后,变量就有了使用范围,也就有了使用规则
- 变量使用规则分为两种,**访问规则** 和 **赋值规则**
①访问规则
- 当我想获取一个变量的值的时候,我们管这个行为叫做 **访问**
- 获取变量的规则:
- 首先,在自己的作用域内部查找,如果有,就直接拿来使用
- 如果没有,就去上一级作用域查找,如果有,就拿来使用
- 如果没有,就继续去上一级作用域查找,依次类推
- 如果一直到全局作用域都没有这个变量,那么就会直接报错(该变量 is not defined)
- 变量的访问规则 也叫做 作用域的查找机制
- 作用域的查找机制只能是向上找,不能向下找
②赋值规则
- 当你想给一个变量赋值的时候,那么就先要找到这个变量,在给他赋值
- 变量赋值规则:
- 先在自己作用域内部查找,有就直接赋值
- 没有就去上一级作用域内部查找,有就直接赋值
- 在没有再去上一级作用域查找,有就直接赋值
- 如果一直找到全局作用域都没有,那么就把这个变量定义为window全局变量,在给他赋值
三、递归函数
1、什么是递归函数
- 在编程世界里面,递归就是一个自己调用自己的手段
- 递归函数: 一个函数内部,调用了自己,循环往复
- 其实递归函数和循环很类似
- 需要有初始化,自增,执行代码,条件判断的,不然就是一个没有尽头的递归函数,我们叫做 **死递归**
```javascript
function add(n) {
// 传递进来的是 1
// 当 n === 5 的时候要结束
if (n === 5) {
return 5
}
}
add(1)
四、自执行函数
1、定义以后立即执行 var fn = function(){代码}()
2、自执行函数是为了区分作用域的 可以防止变量的污染