复习
循环分支 - do while
+ 也是循环语法的一种
+ 和 while 循环很像, 使用场景比较少
while 语法
while (条件) {
代码
}
do while 语法
do {
代码
} while (条件)
和 while 的区别
while 是先进行条件判断, 在决定是否执行代码
do while 先执行一遍代码, 再开始进行条件判断, 决定时候继续执行代码
区分:
+ 当初始条件在 结束条件以内的时候, while 和 dowhile 是一样的
+ 当初始条件在 结束条件以外的时候
=> while 循环一次都不会执行
=> do while 会执行一次
做一个 1 ~ 10 的循环
var n = 1
do {
console.log(n)
n++
} while (n <= 10)
循环分支 - for 循环
语法:
for (定义初始变量; 条件判断; 改变初始变量) {
重复执行的代码
}
原始形态
定义初始变量
for (; 条件判断; ) {
重复执行的代码
改变初始变量
}
1 ~ 10 的数字
var n = 1
for (; n <= 10;) {
console.log(n)
n++
}
流程控制语句
1. break
+ 使用在循环内的关键字
+ 当循环内遇到 break 的时候, 会直接结束当前循环
2. continue
+ 使用在循环内的关键字
+ 当循环内遇到 continue 的时候, 会结束循环的本次, 直接去到修改变量位置
3. 标记语法
+ 可以给 循环打上标记
+ 在 break 或者 continue 的时候, 直接跳出到标记位置
+ 目的: 默认 break 和 continue 是在当前循环内生效
gx:
for (var i = 1; i <= 5; i++) {
console.log('------------------------------')
console.log(`我拿起了第 ${ i } 个包子`)
for (var j = 1; j <= 3; j++) {
console.log(`我咬下了第 ${ j } 口`)
if (i === 3 && j === 2) {
console.log('看到了 半条虫子 ')
// 不吃了
// 当你结束的时候, 我是想结束 gx 这个被标记的循环
break gx
}
}
console.log(`第 ${ i } 个包子吃完了`)
console.log('------------------------------')
}
认识函数
1. 什么是函数
=> 一个 javascript 内的复杂数据类型
=> 私人: 一个承载一段代码的 "盒子"
2. 函数的两个阶段
2-1. 函数的定义阶段
=> 声明式函数: function 函数名() {}
=> 函数表达式: var 函数名 = function () {}
2-2. 函数的调用阶段
=> 函数名()
=> 注意: 声明式函数可以先调用可以后调用, 函数表达式只能后调用
3. 函数的参数
3-1. 形参
=> 书写在函数定义阶段的小括号内
=> 就是一个只能在函数内使用的变量
=> 可以书写多个, 多个之间使用 逗号(,) 分隔
=> 值由函数调用时的实参决定
3-2. 实参
=> 书写在函数调用阶段的小括号内
=> 就是按照顺序依次给函数的形参赋值
=> 可以书写多个, 多个之间使用 逗号(,) 分隔
3-3. arguments
=> 函数内天生自带的变量, 就是函数所有实参的集合
=> length 属性
-> 语法: arguments.length
-> 得到: 该 arguments 的长度, 也就是有多少个实参
=> 索引 属性
-> 索引: 从 0 开始, 依次 +1
-> 通过索引可以访问到 arguments 内某一个数据
-> 语法: arguments[索引]
=> 遍历
-> 利用循环对 arguments 进行遍历
-> 示例:
for (var i = 0; i < arguments.length; i++) {
// i 分别表示 arguments 内每一个数据的索引
// arguments[i] 分别表示每一个数据
}
4. 函数的 return
4-1. 函数的返回值
=>在函数内可以使用一个叫做 return 的关键字
=>给该函数创建一个返回值(结果),写在 return 后面的内容就是该函数执行完毕以后得到的结果
4-2. 打断函数
=> 当函数内遇到 return 关键字的时候
=> 后续代码不在执行了
//1. 声明式函数
function fn() {
console.log('我是被装进盒子的一段代码')
}
console.log(fn)
//2. 赋值式函数
var fn2 = function () {
console.log('我是第二段代码, 我也被装进了盒子')
}
console.log(fn2)
//函数调用
fn()
fn2()
fn()
fn2()
函数的参数
+ 目的: 为了在你封装函数的时候, 增加更多的定制性
函数的参数分成两种
+ 形参
=> 书写在函数定义阶段的 小括号 内
=> 就是一个只能在函数内使用的变量
=> 可以书写多个, 多个之间使用逗号(,) 分隔
=> 形参的值, 由函数调用时的实参决定
+ 实参
=> 书写在函数调用阶段的 小括号 内
=> 就是按照顺序依次给函数的形参赋值
=> 可以书写多个, 多个之间使用逗号(,) 分隔
arguments
arguments
+ 是什么?
=> 是函数内天生自带的变量, 可以直接使用
=> 是一个集合("盒子"), 承载的是该函数被调用时的全部实参
arguments 的基本操作
1. length 属性
+ 语法: arguments.length
+ 得到: 是一个数值类型, 表示该 arguments 的长度, 也就是 arguments 内有多少个数据
2. 索引 属性
+ 索引:
=> arguments 内的数据排列是有顺序的
=> 从 0 开始, 依次 +1
=> 我们管这个数据排列的 "序号", 叫做 索引或者下标
+ 索引和 length 的关系
=> 最后一位的索引一定是 length-1
+ 利用索引可以访问 arguments 内的数据
=> 语法: arguments[索引]
-> 如果 arguments 内有该索引位置, 那么就是该索引位置的数据
-> 如果 arguments 内没有该索引位置, 那么就是 undefined
3. 遍历 arguments
+ 意义: 依次访问数据集合内的每一个数据
+ 规律: arguments 内每一个数据的索引是一组有规律的数字
循环可以给我们提供一组有规律的数字
利用 循环, 来遍历 arguments
预解析
+ 在代码执行之前, 对代码进行通读并解释
+ 在预解析的过程中
=> 只会解析全局代码, 函数内的代码不会进行预解析
=> 会在函数执行的时候, 才进行预解析
解释的内容
1. var 关键字声明的变量
=> 告诉浏览器, 我定义了一个 xxx 变量, 但是还没有赋值
=> 赋值式函数按照 var 的规则来执行
2. 声明式函数
=> 告诉浏览器, 我定义了一个 xxx 变量(函数名), 并且赋值为一个 函数
var 的预解析
console.log(num)
var num = 100
console.log(num)
代码执行过程
1. console.log(num)
2. var num = 100
3. console.log(num)
打开浏览器
预解析
1. 不需要预解析
2. 需要预解析
=> var num
=> 告诉浏览器我定义了一个叫做 num 的变量
3. 不需要预解析
执行代码
1. console.log(num)
=> 预解析已经声明了 num 变量, 目前内存中是有 num 变量的
=> undefined
2. num = 100
=> 给 num 变量赋值为 100
3. console.log(num)
=> 100
声明式函数预解析
fn()
function fn() { console.log('fn') }
fn()
代码执行过程
1. fn()
2. function fn() { console.log('fn') }
3. fn()
打开浏览器
预解析
1. 不需要预解析
2. 需要预解析
=> 在浏览器声明了一个变量叫做 fn, 并且赋值为一个函数
3. 不需要预解析
代码执行
1. fn()
=> 因为在预解析阶段, 已经把 fn 定义为一个函数了
=> 正常执行
2. 不需要
3. fn()
=> 原因同上
赋值式函数预解析
fn()
var fn = function () { console.log('你好 世界') }
fn()
代码执行过程
1. fn()
2. var fn = function () { console.log('你好 世界') }
3. fn()
打开浏览器
预解析
1. 不需要预解析
2. 需要
=> var fn
=> 告诉浏览器我定义了一个叫做 fn 的变量, 暂时没有赋值
3. 不需要预解析
代码执行
1. fn()
=> 此时 fn 变量存在, 但是值是一个 undefined
=> fn() 把 undefined 当做一个函数来调用
=> 报错: fn is not a function
函数名和变量名重名
说明: 该文件内
=> 函数指函数名
=> 变量指的是保存非函数的变量
函数名和变量名重名
=> 当一个 变量 和 函数名 重名的时候
=> 在 **预解析阶段** , 以函数为准
fn()
function fn() { console.log('我是一个函数') }
fn()
var fn = 100
fn()
代码执行过程
1. fn()
2. function fn() { console.log('我是一个函数') }
3. fn()
4. var fn = 100
5. fn()
打开浏览器
预解析
1. 不需要
2. 需要
=> 告诉浏览器, 我定义了一个叫做 fn 的变量, 并且赋值为一个函数
3. 不需要
4. 需要
=> var fn
=> 告诉浏览器, 我定义了一个叫做 fn 的变量
5. 不需要
+ 当预解析结束的时候, 浏览器内只有一个 fn 变量, 并且被赋值为了一个 函数
执行代码
1. fn()
=> 因为预解析阶段已经把 fn 赋值为一个函数了
=> 正常调用
2. 不用管
3. fn()
=> 原因同上
4. fn = 100
=> 给 fn 变量赋值为 100
=> 因为 fn 本身保存的是 一个函数
=> 100 把 函数覆盖了
=> 从此以后 fn 就是 100 这个数字了
5. fn()
=> 因为 第 4 行代码的执行, 把 fn 赋值为 100 了
=> 把 100 当做一个函数来调用
=> fn is not a function
函数的执行过程(牢记)
内存的存储过程
+ 当你定义一个变量(变量和函数)的时候
+ 会在内存中开辟一段区域来存储内容
+ 浏览器把存储部分分成了两个区域
1. 栈内存
2. 堆内存
+ 在存储的过程中
=> 基本数据类型, 直接把值保存在栈内存里面
=> 复杂数据类型
-> 数据存储在堆里面
-> 把地址存储在栈内的变量里面
函数的两个阶段(熟读并背诵全文)
函数定义阶段
1. 在堆内存中开辟一段空间
2. 把函数体内的代码一模一样的当做一个字符串存储在空间中(不执行代码, 不解析变量)
3. 把空间地址赋值给栈内存中的变量名
函数调用阶段
1. 按照变量名存储的地址找到一个对应的堆内存中的空间
1-1. 判断变量名是否存在, 如果不存在: xxx is not defined
1-2. 按照变量名存储的地址查找, 如果不是一个函数存储空间: xxx is not a function
2. 在 调用栈 中开辟一段新的代码执行空间
3. 在开辟的代码执行空间中进行 形参赋值
4. 在开辟的代码执行空间中进行 预解析
5. 把存储空间内的代码一模一样的复制一份放在执行空间内执行一遍(此时才开始解析变量)
6. 当代码执行完毕以后, 该执行空间销毁
代码解读
var num = 100
function fn() {
// 如果我在定义阶段不解析变量, 那么我存储起来的应该是 'console.log(num)'
// 如果我在定义阶段就解析变量, 那么我存储起来的应该是 'console.log(100)'
console.log(num)
}
num = 200
如果 定义阶段没有解析变量, 那么 num = 200 会影响函数的执行, 打印出来就是 200
如果 定义阶段解析了变量, 那么 num = 200 不影响函数的执行, 打印出来依旧是 100
fn()
分析:
假设: 先 形参赋值 后 预解析
1. 给 a 赋值为 100
2. 在预解析阶段给 a 赋值为 函数, 此时会覆盖 100
+ 当函数体内的代码在执行的时候, a 打印出来就是 函数体
结社: 先 预解析 后 形参赋值
1. 在预解析阶段给 a 赋值为 函数
2. 给 a 赋值为 100, 就会覆盖本身的 函数
+ 当函数体内的代码在执行的时候, a 打印出来的就是 100
*/
function fn(a) {
// 打印 a 变量
console.log(a)
function a() {}
}
// 本次调用的时候, 100 赋值给了形参 a
fn(100)
作用域
一个变量(变量和函数)的生效范围
作用域的分类
1. 全局作用域
=> 一个 html 页面打开, 就是一个全局作用域
=> window
2. 私有作用域(函数作用域)
=> 概念: **只有函数生成私有作用域**
提供了三个机制
1. 变量定义机制
=> 定义在哪一个作用域下的变量就是哪一个作用域的私有变量
=> 只能在该作用域以及后代作用域使用
2. 变量访问机制
=> 当你需要访问某一个变量的值的时候
=> 首先在自己作用域内查找, 如果有直接使用, 停止查找
=> 如果没有, 去到父级作用域查找, 如果有直接使用, 停止查找
=> 如果还没有, 在去到父级作用域查找
=> 以此类推, 直到全局作用域(window) 都没有, 报错: xxx is not defined
3. 变量赋值机制
=> 当你需要给一个变量赋值的时候
=> 首先在自己作用域内查找, 如果有直接赋值, 停止查找
=> 如果没有, 去到父级作用域查找, 如果有直接赋值, 停止查找
=> 如果还没有, 就在去到父级作用域查找
=> 以此类推, 直到全局作用域(window) 都没有
=> 把该变量定义为 **全局变量** 在进行赋值
分清变量的三种情况
1. 变量定义
=> 必须要有定义变量的关键字
-> var / function
=> 函数的形参
2. 变量访问
=> 当你需要用到某一个变量的值
3. 变量赋值
=> 当你需要给一个变量进行赋值操作
=> 特点: 有赋值符号 或者 实参和形参的交互
function a() {
// 此处位置就是 a 私有作用域, 父级作用域是 window
// 子级作用域是 b
function b() {
// 此处位置就是 b 私有作用域
// 父级作用域是 a
// 子级作用域是 c
function c() {
// 此处位置就是 c 私有作用域
// 父级作用域 b
}
}
}
function d() {
// 此处位置就是 d 私有作用域
// 父级作用域是 window
}
封装函数
- 计算两个数字的最大公约数
function fun(a,b){
if(a%b == 0) return b;
else return fun(b,a%b);
}
console.log(fun(12,8));
var gcd = function(a, b){
return b ? gcd(b, a % b) : a;
}
-------------解析--------------
var gcd = function(a, b){
return b ? gcd(b, a % b) : a;//把a%b==0这个条件直接简化为递归后判断最新的b !== 0
// 8 8 4
// 4 4 0
// 0 4
}
console.log(gcd(12,8));
-------------转化为箭头函数--------------
var a = 12,b = 8;
fun_2 = (a,b)=> b ? fun_2(b, a % b) : a
console.log('最小公约数',a*b/fun_2(a,b));