## 1.函数的概念
- 空调:由遥控器或开关控制,可以实现调解空气的功能的家用电器
- 函数:由用户或自身控制,可以实现某个功能的 代码段(很多代码)
- 家用电器(空调)的特点:
- 忽略细节
- 重复使用
- 选择使用
- 函数的特点:
- 忽略细节:在**使用过程**中,只需要关注其实现的功能,而不需要关注其内部原理
- 重复使用:多次使用
- 选择使用:按需使用
## 2.创建函数
- 函数从哪来
- 内置(系统提供,公司发的)
- `parseInt()`
- `parseFloat()`
- `alert()`
- 自定义(自己写,自己给自己做)
- 声明式创建函数
- 需要配合关键字:`function`
- 语法:
```js
function 函数名(){}
```
- 赋值式创建函数
- 需要配合关键字:`var`和`function`
```js
var 变量名 = function(){}
```
- 函数其实就是一个变量,只不过内部存的是一段代码,这段代码还被一个容器包裹了
## 3.执行函数
- 函数名()
- 只要函数名后面有小括号,必然会立即执行**当前**函数
- 固定语法
- 通过事件执行
- 执行无名函数
```js
元素.事件 = function(){}
```
- 执行有名函数
```js
元素.事件 = 函数名
```
- 无名函数配合有名函数,使用较多
```js
元素.事件 = function(){
函数|变量名()
}
```
## 4.函数分类
- 根据写法分类
- 有名函数:
- 正常函数
- 使用频率最多的函数
- 声明式和赋值式创建的函数
```js
function 函数名(){}
var 变量名 = function(){}
```
- 执行
- 常规执行:函数名或变量名()
- 无名函数:
- 非正常函数
- 没有名字
```js
function(){}
```
- 不允许直接存在于代码空间中,否则会报错
- 使用场景:
1. *作为*变量的**值**存在(赋值式创建函数时的**值**)
```js
var 变量名 = function(){}
```
- 执行:函数名|变量名()
2. *作为*事件处理函数执行
```js
btn.onclick = function(){
// 当事件被触发时要执行的内容...
}
```
3. ...
- 匿名函数:
...
## 5.函数的参数
- 什么是参数
- 根据用户传入不同的参数,选择执行函数中不同的功能
- 参数的分类:
- 发:实参:函数执行时的参数
- 收:形参:函数定义时的参数
- 实参和形参的关系,赋值的关系,形参相当于变量,实参相当于值,一对一
- 数量对应关系
- 参数可以有很多个,语法上没有数量限制,但是行业有习惯,自定义函数,如非特殊需要,尽量不要超过3个
- 实参和形参数量一致:
- 按照顺序,一一对应
- 实参多:
- 没有形参接收,通过形参找不到
- 在函数内部有个神秘的空间(arguments),这个空间会将所有的实参全部保存,不论有没有被接收
- arguments是个对象类型的数据(类数组的数据)
- 长度(个数),表示接收到了几个实参
- `arguments.length`
- 索引(序号,编号),表示数组内部的数据位置,索引从0开始
- `arguments[索引]`
- 形参多:
- 多出来的形参是undefined
- 形参其实就是一个变量,实参是赋值,如果实参不够,表示没有赋值,undefined
## 6.函数的返回值
- 返回值概念
- 函数自身的处理数据或执行结果,需要被二次使用或其他程序调用时,需要将数据返回出来
- 如何拿到返回值
- 关键字:`return 要返回的数据`
- return的功能
- 可以让函数返回指定的**值**
- 可以立即结束当前函数
- 一个函数中可以写无数个,但是只能执行一次return,一次只能返回一个数据,不允许返回多个数据
- 返回到哪
- 返回到函数的执行语句
- 函数名(),既是在执行函数,也是在拿返回值
- **注意**
- 一个函数如果没有return,那么返回的是undefined;如果有return,那么返回值就是return后面的值
- 可以返回哪些数据
- 所有数据
- 当返回函数时,形成了闭包(后期概念)
- 什么时候需要返回值?
- 功能性函数(打印表格,改颜色)
- 可以有,但是没有必要有返回值
- 处理数据的函数(补零,计算器,计算圆的面积)
- 一般都有,返回数据
## 补充:事件分类
- 鼠标类
- 键盘类
- 表单类
举例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#box{width: 100px;height: 100px;background: red}
</style>
</head>
<body>
<div id="box"></div>
<input type="text" id="txt">
</body>
<script>
// 人机交互
// 输入和输出
// 输入设备:鼠标,键盘,...
// 输出设备:显示器,音响,...
// 事件按照输入设备分:
// 鼠标类:
// 单击:click
// 双击:dblclick
// 按下:mousedown
// 抬起:mouseup
// 移动:mousemove
// 进入:mouseover / mouseenter
// 离开:mouseout / mouseleave
// 右键:contextmenu
// 键盘类:
// 按下:keydown
// 抬起:keyup
// 按下并抬起:keypress
// 网页的特色事件
// 浏览器类:
// 加载:load
// 滚动:scroll
// 改变大小:resize
// 表单类:
// 获取焦点:focus
// 失去焦点:blur
// 输入:input
// 内容改变:change
// 提交事件:submit
// 重置事件:reset
// 。。。。。。。。
// 所有事件的绑定方式都是一致的
// 目前学习了一种:on绑定,赋值式绑定
// 元素.on事件名 = function(){}
// 注意:行为的触发方式是否正确
// 尤其是表单类
// 喝水需要经过哪些行为?
// 假设杯子里有水,放在手边
// 伸手,找到杯子,抓住杯子,移动到嘴边,张嘴,抬手,喝
// 在计算机上拖拽一个文件:
// 按下,移动,抬起
// 鼠标事件测试
var box = document.getElementById("box");
box.onclick = function(){
console.log("单击事件发生了")
}
box.ondblclick = function(){
console.log("双击事件发生了")
}
box.onmousedown = function(){
console.log("按下事件发生了")
}
box.onmouseup = function(){
console.log("抬起事件发生了")
}
box.onmouseover = function(){
console.log("进入事件发生了")
}
box.onmouseout = function(){
console.log("离开事件发生了")
}
box.onmousemove = function(){
console.log("移动事件发生了")
}
box.oncontextmenu = function(){
console.log("右键事件发生了");
}
// 表单事件测试
var txt = document.getElementById("txt");
txt.onfocus = function(){
console.log("获取焦点事件")
}
txt.onblur = function(){
console.log("失去焦点事件")
}
txt.oninput = function(){
console.log("输入事件")
}
txt.onchange = function(){
console.log("改变内容事件")
}
# 二、作用域
- 谁作用的哪个区域
- 谁:数据(变量)
- 哪个:如何划分,函数就是一个区域
- 全局
- 不属于任何一个函数
- 全局作用域中的变量,叫全局变量
- 全局作用域中的函数,叫全局函数
- 生命周期:一直存在
- 耗性能,但是方便
- 少用全局
- 局部
- 任何一个函数内部都是局部作用域
- 局部作用域中的变量,叫局部变量
- 局部作用域中的函数,叫局部函数
- 生命周期:朝生暮死
- 节省内存,不方便
- 函数的参数(形参):是当前函数的局部变量
- 匿名函数
- 利用匿名函数解决全局耗性能,局部不方便的问题
- `(function(){})()`
# 三、变量的读写规则
- 当全局和局部的变量名重复时
- 读的规则:
- 向上级作用域查找,找到了,就使用,同时,停止查找;找到顶级作用域,都没有,**报错**
- 写(设置,赋值)的规则:
- 向上级作用域查找,找到了,就写入,同时,停止查找;找到顶级作用域,都没有,**会默认在顶级作用域声明这个变量,然后使用**
- 注意:声明变量时,必须加声明关键字,不加声明关键字,可能能执行,但是不规范
# 四、JS的编译和执行
- 偏原理
- 可以不懂原理,只需要注意现象,可以正常写代码
- 懂原理,帮助你提升代码性能,优化;方便面试
- js是一门解释性语言
- 预先编译,再执行
- 先通读全文,在解释含义
- 找到var和function,做出对应提升
- 编译时
- **提升**
- 执行时
- 按照逻辑,结构正常执行
## 变量和函数会提升
- var的提升
- 提前声明,=号的位置赋值
```js
console.log(a); // undefined
var a = 10;
console.log(a); // 10
```
- ↑↑↑↑等价于↓↓↓↓
```js
var a;
console.log(a); // undefined
a = 10;
console.log(a); // 10
```
- function的函数提升
- 整体提升
- 即提前声明,有提前赋值
- 如果var遇到function
1. 赋值式创建函数 ********
```js
var fn = fucntion(){}
```
- 提升的是var,不是function
2. 变量和函数重名
```js
var a = "hello";
function a(){}
```
- var提升的更高,所以function占便宜,生效的是function
五、函数的高级应用-递归
- 在函数内部执行自己
- 递归就是函数自己调用自己
- 递归类似于循环
- 递归函数要注意停止/返回,否则会造成死递归
- 递归比较消耗性能,尽量少用
举例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<div id="box"></div>
</body>
<script>
// 在函数内部执行自己
// 自己调用自己
// function fn(){
// fn();
// }
// fn();
// 以上代码会:死
// 递归就像:一条蛇吞了自己的尾巴
// 递归如果控制好结束条件,可以模拟循环,解决循环能解决的问题
// 所以,直接递归虽然不好,但是可以利用递归的思想,解决一些实际需求
// 递归的缺点:内存溢出,浪费大量的性能,占用过多的进程
// 递归是一个逻辑算法的体现
// 思想,不是浪费的性能
// 看电影,去晚了,开场了,灯关了,很黑,啥都看不见,只能看见人
// 递归:
// 递:不断向内执行的过程
// 归:不断向上返回的过程
// 递归必须包含这两个过程,如果只有递没有归,那么程序未响应
// 递就是在执行自身,自己调用自己
// 归就是结束,或返回,返回值
// -----------------电影院问作为的案例得出------------------
// 每个座位号都是前一个座位号+1
// 当前座位号 = 前一个座位号 + 1
// 假设,计算当前座位的功能:fn,计算第n排
// fn(n) = fn(n-1) + 1
// 递:
// fn(5) = fn(5-1) + 1
// fn(4) = fn(4-1) + 1
// fn(3) = fn(3-1) + 1
// fn(2) = fn(2-1) + 1
// fn(1) = fn(1-1) + 1
// fn(0) = 0
// 归:
// fn(5) = 4 + 1
// fn(4) = 3 + 1
// fn(3) = 2 + 1
// fn(2) = 1 + 1
// fn(1) = 0 + 1
// fn(0) = 0
// 阶乘:5的阶乘------------其实就是当前数字~1的累乘
// 5! = 5 * 4!
// 4! = 4 * 3!
// 3! = 3 * 2!
// 2! = 2 * 1!
// 1! = 1
// 5! = 5 * 4 * 3 * 2 * 1
// 4! = 4 * 3 * 2 * 1
// 3! = 3 * 2 * 1
// 2! = 2 * 1
// 1! = 1
// 假设,计算阶乘的功能:fn,计算n的阶乘:fn(n)
// 当前数字的阶乘 = 当前数字 * 前一个数字的阶乘
// fn(n) = n * fn(n-1);
// n = 1;
// fn(n) = 1;
// function fn(a){
// if(a === 1){
// return 1;
// }else{
// return a * fn(a-1);
// }
// }
// console.log(fn(10));
// --------计算当前数字~1的累加---------
// function fn(a){
// if(a === 1){
// return 1;
// }else{
// return a + fn(a-1);
// }
// }
// console.log(fn(10));
// ----------斐波那契数列----------
// 1,1,2,3,5,8,13,21,34,55......
// 规律:从第三位开始:当前数字 = 前一个数字 + 再前一个数字
// 假设一个计算当前数字的功能:fn,如果向计算第n个数字
// fn(n) = fn(n-1) + fn(n-2);
// 实现假设
function fn(n){
if(n === 1 || n === 2){
return 1;
}else{
return fn(n-1) + fn(n-2);
}
}
console.log(fn(10));
// 找不到规律,适合用递归么
</script>
</html>