一、变量和函数的预解析
1、预解析: (预解释)
2、变量的规则:也有人说是变量提升
- JS在运行我们的代码的时候, 会先整体阅读一次我们的 JS 代码,此时读取完毕后, 会将变量的声明(定义)提取到页面的最顶部(JS代码的最顶部)
- 但是注意: 提升的只有变量的声明, 没有变量的赋值。那么换句话说, 也就是在变量定义前去使用, 得到的是一个
undefined
原本的代码
console.log(a)
var a = 100
console.log(a)
开始运行代码, 浏览器读取我们JS整体代码
读取完毕, 预解析 后的代码
var a
console.log(a) // undefined
a = 100
console.log(a) // 100
3、函数的规则:也有人说是函数提升
- JS 在运行我们的代码的时候, 会先整体阅读一次我们的 JS 代码,此时读取完毕后, 会将声明式定义的函数 提取到页面的最顶部(JS代码的最顶部).所以我们在书写代码的时候, 可以在 声明式定义的函数前, 去调用函数
* 原本的代码
* console.log(a) // ƒ a () { console.log('我是a函数') }
* function a () { console.log('我是a函数') }
* console.log(a) // ƒ a () { console.log('我是a函数') }
*
* 浏览器读取我们的JS整体代码, 开始函数提升
*
* 提升后的代码:
* function a () { console.log('我是a函数') }
* console.log(a)
* console.log(a)
=========================================================
原本的代码
console.log(a)
var a = function () { console.log('我是a函数') }
console.log(a)
开始运行代码, 浏览器读取我们JS整体代码
读取完毕, 预解析 后的代码
var a
console.log(a) // undefined
a = function () { console.log('我是a函数') }
console.log(a) // ƒ () { console.log('我是a函数') }
二、作用域
- 简单来说: 就是一个变量生效的范围
1、全局作用域
- 整个 script 标签内部的区域声明的变量就是在全局作用域创建
- 如果是在全局作用域创建的变量或者函数我们统称为
全局变量或者全局函数,能在当前代码的所有位置去使用 - JS 在全局作用域中提供了一个 对象, 叫做 window, 我们书写的所有的 全局变量或者全局函数, 都在 window 对象内部存放
2、局部作用域(函数作用域)
- 在一个函数内部生成的变量就是存在于局部作用域的,在局部作用域创建的变量只能在当前作用域内使用
三、作用域链
- 每一个作用域上一层会有一个全新的作用域, 每个作用域之间的一个连接, 我们称之为作用域链
- 如果我们在一个作用域内寻找一个变量, 那么我们会在当前作用域内查找, 如果找到直接使用; 如果没有找到, 那么会继续去上一层作用域继续查找, 找到就使用, 如果没有找到继续去上一层查找;所以我们将这个层层查找的顺序之间的连接, 称之为作用域链
- 作用域链分为两个规则:
-
- 查找规则
在一个作用域内查找一个变量, 如果有直接使用, 如果没有会去上一层继续查找, 如果找到直接使用, 没有的话继续去上一层查找;直到找到了全局作用域, 找到了直接使用, 如果没有找到直接报错
注意: 查找的时候只会层层向上, 不会向下
- 查找规则
-
- 赋值规则
在一个作用域对一个变量进行赋值, 那么会先在当前作用域内查找变量, 找到直接修改, 如果没有, 会去上一层作用域查找;如果找到直接修改, 没有继续向上, 如果找到了全局作用域还是没有, 那么会在全局作用域创建一个变量, 然后对他进行赋值操作
- 赋值规则
-
var a = 100
function fn() {
// fn 函数内部会有一个独立的作用域, 也就是局部作用域
function fn2() {
console.log(a) // 100
}
fn2()
}
fn()//100
var a = 100
function fn() {
var a = 200
function fn2() {
console.log(a) // 200
}
fn2()
}
fn()//200
function fn() {
function fn2() {
a = 'QF001'
}
fn2()
}
fn()
console.log(a) // QF001
function fn() {
function fn2() {
var a = 'QF001'
}
fn2()
}
fn()
console.log(a)// 报错:a is not defined
四、递归函数
- 在编程中就是指 自己调用自己的一种手段;递归函数就是在一个函数内部调用了自身. 循环执行
- 递归函数有点类似于循环:也需要有初始化, 自增, 执行的代码, 判断条件;如果上述的内容缺少, 那么就是一个死递归, 永远不会结束
1、求 1~5 的所有数字相加的和
/**
* 求 1~5 的所有数字相加的和
*
* 1 + 2 === 3
* 3 + 3 === 6
* 6 + 4 === 10
* 10 + 5 === 15
*/
function fn(n) {
// 1. 先书写结束条件
if (n === 5) {
return 5
}
/**
* 2. 开始递归
*
* 假设我们的 fn 函数能够帮我们计算 某一个数字到5的所有数字相加的和
*
* 1~5 fn(1)
* 2~5 fn(2)
* 3~5 fn(3)
* 4~5 fn(4)
* 5~5 fn(5)
*
*
* 目前是要计算 1~5的所有数字相加
* 所以等式可以理解为 1 + 2~5
* 1 + fn(2)
*/
return n * fn(n + 1)
}
var res = fn(1)
console.log(res)
2、斐波那契数列
/**
* 斐波那契数列是一个特殊的数列
*
* 该数列中, 前两位的数字固定为 1, 从第三位开始, 每个位置上的数字是前两位的和
*
* 数列: 1,1,2,3,5,8,13,21,34,55,89
*
* 要求: 封装一个函数, 能够计算出斐波那契数列中某一个位置的具体数值
*
* 请使用递归函数解决
*/
function fn(n) {
let arr = []
arr[0] = 1
arr[1] = 1
for (let i = 2; i <= n; i++) {
arr[i] = arr[i - 1] + arr[i - 2]
}
return arr[n - 1]
}
let res = fn(6)
console.log(res)