目录
学习目标:
学习内容:
学习时间:
学习内容讲解:
作用域
• 局部作用域
全局作用域
作用域链
JS垃圾回收机制
拓展-JS垃圾回收机制-算法说明
闭包
变量提升
函数进阶
函数提升
函数参数
箭头函数
解构赋值
对象解构
遍历数组 forEach 方法(重点)
案例
学习目标:
1. 掌握作用域等概念加深对JS理解
2. 学习ES6新特性让代码书写更加简洁便利
学习内容:
作用域
函数进阶
解构赋值
综合案例
学习时间:
提示:这里可以添加计划学习的时间
例如:
- 周一至周五晚上 7 点—晚上9点
- 周六上午 9 点-上午 11 点
- 周日下午 3 点-下午 6 点
学习内容讲解:
作用域
• 局部作用域
目标:了解作用域对程序执行的影响及作用域链的查找机制,使用闭包函数创建隔离作用域避免全局变量污染。
l 作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,
l 作用域分为:
Ø 局部作用域
Ø 全局作用域
局部作用域分为函数作用域和块作用域。
1. 函数作用域:
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
总结:
1. 函数内部声明的变量,在函数外部无法被访问
2. 函数的参数也是函数内部的局部变量
3. 不同函数内部声明的变量无法互相访问
4. 函数执行完毕后,函数内部的变量实际被清空了
局部作用域分为函数作用域和块作用域。
块作用域:
在 JavaScript 中使用 { } 包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。
总结:
1. let 声明的变量会产生块作用域,var 不会产生块作用域
2. const 声明的常量也会产生块作用域
3. 不同代码块之间的变量无法互相访问
4. 推荐使用 let 或 const
全局作用域
<script> 标签 和 .js 文件 的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。
全局作用域中声明的变量,任何其它作用域都可以被访问
注意:
1. 为 window 对象动态添加的属性默认也是全局的,不推荐!
2. 函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
3. 尽可能少的声明全局变量,防止全局变量被污染
作用域链
作用域链本质上是底层的变量查找机制。
Ø 在函数被执行时,会优先查找当前函数作用域中查找变量
Ø 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
JS垃圾回收机制
目标: 了解JS垃圾回收机制的执行过程
学习目的: 为了闭包做铺垫
学习路径:
1. 什么是垃圾回收机制
2. 内存的声明周期
3. 垃圾回收的算法说明
1. 什么是垃圾回收机制?
垃圾回收机制(Garbage Collection) 简称 GC
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
正因为垃圾回收器的存在,许多人认为JS不用太关心内存管理的问题
但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况
不再用到的内存,没有及时释放,就叫做内存泄漏
2.内存的生命周期
JS环境中分配的内存, 一般有如下生命周期:
1. 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
2. 内存使用:即读写内存,也就是使用变量、函数等
3. 内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
4. 说明:
Ø 全局变量一般不会回收(关闭页面回收);
Ø 一般情况下局部变量的值, 不用了, 会被自动回收掉
拓展-JS垃圾回收机制-算法说明
但它却存在一个致命的问题:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
function fn() { let o1 = {} let o2 = {} o1.a = o2 o2.a = o1 return '引用计数无法回收' } fn()
标记清除法
现代的浏览器已经不再使用引用计数算法了。
现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
核心:
1. 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
2. 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。
3. 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
下面是JS标记清除法的工作原理:
- JavaScript引擎会记录所有变量和对象的引用关系表,也就是从根节点开始遍历所有被引用的变量或对象。
- 引擎会从根节点出发,遍历所有可达对象,并标记这些对象为“存活”对象。
- 与存活对象没有引用关系的对象都会被标记为“垃圾”对象,并等待被清除。
- 在适当的时刻,JavaScript引擎会执行垃圾回收操作,将所有未被标记为“存活”对象的内存释放掉,并交还给操作系统。
闭包
目标: 能说出什么是闭包,闭包的作用以及注意事项
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包 = 内层函数 + 外层函数的变量 先看个简单的代码:
闭包应用:实现数据的私有
比如,我们要做个统计函数调用次数,函数调用一次,就++
总结
1. 怎么理解闭包?
Ø 闭包 = 内层函数 + 外层函数的变量
2. 闭包的作用?
Ø 封闭数据,实现数据私有,外部也可以访问函数内部的变量
Ø 闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来
3. 闭包可能引起的问题?
Ø 内存泄漏
变量提升
目标:了解什么是变量提升
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
注意:
1. 变量在未声明即被访问时会报语法错误
2. 变量在var声明之前即被访问,变量的值为 undefined
3. let/const 声明的变量不存在变量提升
4. 变量提升出现在相同作用域当中
5. 实际开发中推荐先声明再访问变量
总结
1. 用哪个关键字声明变量会有变量提升?
Ø var
2. 变量提升是什么流程?
Ø 先把var 变量提升到当前作用域于最前面
Ø 只提升变量声明, 不提升变量赋值
Ø 然后依次执行代码
我们不建议使用var声明变量
函数进阶
函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
函数提升指的是在JavaScript中,函数声明会在代码执行过程中提前到当前作用域的最顶部,而不是在代码中声明的位置。这意味着,你可以在函数声明前调用该函数,而不会报错。
例如:
foo(); // "Hello"
function foo() {
console.log("Hello");
}
在上面的例子中,我们在函数声明后调用了foo()
,但是没有报错,因为函数在代码执行前已经被提升到当前作用域的顶部。
需要注意的是,函数表达式不会被提升,只有函数声明才能被提升。例如:
bar(); // TypeError: bar is not a function
var bar = function() {
console.log("Hello");
}
在上面的例子中,我们在函数表达式前调用了bar()
,但是报错了,因为函数表达式不会被提升。
函数参数
函数参数的使用细节,能够提升函数应用的灵活度。
学习路径:
1. 动态参数
2. 剩余参数
1. 动态参数
arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
总结:
1. arguments 是一个伪数组,只存在于函数中
2. arguments 的作用是动态获取函数的实参
3. 可以通过for循环依次得到传递过来的实参
剩余参数
目标: 能够使用剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组
展开运算符
目标:能够使用展开运算符并说出常用的使用场景
展开运算符(…),将一个数组进行展开
典型运用场景: 求数组最大值(最小值)、合并数组等
展开运算符 or 剩余参数
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开
箭头函数
目标: 能够熟悉箭头函数不同写法
目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方
学习路径:
1. 基本语法
2. 箭头函数参数
3. 箭头函数this
语法3:如果函数体只有一行代码,可以写到一行上,并且无需写 return 直接返回值
语法4:加括号的函数体返回对象字面量表达式
箭头函数参数
1. 普通函数有arguments 动态参数
2. 箭头函数没有 arguments 动态参数,但是有 剩余参数 ..args
箭头函数通常用于包含单个表达式的函数体中,这个表达式将自动成为函数的返回值。如果你需要函数体内包含多条语句,那么你就需要使用大括号来定义函数体,并且需要手动添加return
语句。
下面是一些箭头函数的例子:
// 简单的箭头函数
const add = (a, b) => a + b;
// 包含多条语句的箭头函数
const multiply = (a, b) => {
const result = a * b;
return result;
};
// 没有参数的箭头函数
const greet = () => console.log('Hello, world!');
// 只有一个参数的箭头函数
const square = x => x * x;
// 使用箭头函数作为回调函数
const names = ['Alice', 'Bob', 'Charlie'];
const initials = names.map(name => name[0]);
除此之外,箭头函数还具有以下特点:
- 箭头函数没有独立的
this
,它会继承外部作用域的this
。 - 箭头函数不能通过
new
关键字调用,因为它没有自己的this
。 - 箭头函数不能用作构造函数,因为它没有自己的
this
。 - 箭头函数不能使用
arguments
对象,但是可以使用rest
参数(用...
表示)。 - 箭头函数对于匿名函数非常方便,可以在定义函数时避免显式地写出函数名。
- 箭头函数的语法更加简洁,可以减少代码量。
箭头函数 this
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值, 非常令人讨厌。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
箭头函数 this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
箭头函数 this
在开发中【使用箭头函数前需要考虑函数中 this 的值】,事件回调函数使用箭头函数时,this 为全局的 window,因此
DOM事件回调函数为了简便,还是不太推荐使用箭头函数
解构赋值
目标:知道解构的语法及分类,使用解构简洁语法快速为变量赋值
结构赋值是ES6中的一种语法,可以用来从数组和对象中提取值,然后对这些值进行赋值操作。
结构赋值的语法非常简洁,下面是一些例子:
// 对象解构赋值
const person = { name: 'Alice', age: 28, country: 'USA' };
const { name, age } = person;
console.log(name); // 'Alice'
console.log(age); // 28
// 数组解构赋值
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
在对象解构赋值中,我们使用了对象字面量的语法来匹配对象的键值对。在这个例子中,我们从person
对象中提取了name
和age
属性的值,并将其分别赋值给了name
和age
变量。
在数组解构赋值中,我们使用数组字面量的语法来匹配数组中的值。在这个例子中,我们从numbers
数组中提取了前两个元素的值,并将其分别赋值给了first
和second
变量。我们还使用了rest
参数来获取数组中剩余的元素,并将其放入名为rest
的新数组中。
除此之外,结构赋值还具有以下特点:
- 在解构赋值时,可以使用默认值,当变量对应的值为
undefined
时,将会使用默认值。 - 解构赋值也可以嵌套使用,可以从嵌套的对象或数组中提取值并进行赋值操作。
- 结构赋值还可以用来交换变量的值,以及用来获取函数返回值的多个属性值。
- 如果解构赋值的目标值不存在或为
null
或undefined
,那么左侧的变量将等于undefined
。
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
基本语法:典型应用交互2个变量
注意: js 前面必须加分号情况
1. 立即执行函数
数组结构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
允许初始化变量的默认值,且只有单元值为 undefined 时默认值才会生效
对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
1. 基本语法:
1. 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
2. 对象属性的值将被赋值给与属性名相同的变量
3. 注意解构的变量名不要和外面的变量名冲突否则报错
4.对象中找不到与变量名一致的属性时变量值为 undefined
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
2.给新的变量名赋值:
可以从一个对象中提取变量并同时修改新的变量名
遍历数组 forEach 方法(重点)
l forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
l 主要使用场景: 遍历数组的每个元素
l 语法:
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
注意:
1. forEach 主要是遍历数组
2. 参数当前数组元素是必须要写的, 索引号可选。
案例
核心思路:有多少条数据,就渲染多少模块,然后 生成对应的 html结构标签, 赋值给 list标签即可
①:利用forEach 遍历数据里面的 数据
②:拿到数据,利用字符串拼接生成结构添加到页面中
③:注意:传递参数的时候,可以使用对象解构