目录
一、作用域
(一)局部作用域
1.函数作用域
2.块作用域
(二)全局作用域
二、垃圾回收机制 GC
(一)生命周期
1.内存分配
2.内存使用
3.内存回收
4.特殊情况——内存泄漏:
注意:
(二)算法说明
1.堆栈空间分配区别
2.常见的浏览器垃圾回收算法
引用计数法(基本不咋用)
标记清除法
三、闭包
(一)闭包简介
(二)闭包的基本格式
(三)闭包应用——实现函数的私有
四、变量提升
五、函数进阶
(一)函数提升
(二)函数参数
1.动态参数
2.剩余参数
展开运算符
求最大值
合并数组
(三)箭头函数
1.箭头函数介绍
2.基本语法
2.箭头函数参数
2.箭头函数 this
六、解构赋值
(一)数组解构
特殊情况
1.变量多单元值少
2.变量少单元值多
3.利用剩余参数解决 2
4.防止 undefined 传递
5.按需导入忽略某些值
6.支持多维数组的解构
(二)对象解构(特别重要)
1.对象解构语法
2.对象解构改名
3.简单数组对象解构
4.多级对象解构
遍历数组 forEach 方法 (重点)
筛选函数 filter 方法(重点)
练习: 对象解构在函数中的用处
一、作用域
(一)局部作用域
作用域链的本质是:底层的变量查找机制
函数被执行时,优先查找当前函数作用域,如果找不到再层层往父亲层次查找,直到全局作用域
嵌套关系的作用域串联起来形成了作用域链
而且父亲不能访问孩子
1.函数作用域
函数内部声明的变量,外部无法访问,函数执行完毕内部的变量被清空了
2.块作用域
在 JavaScript 中被 { } 包围的代码被称为代码块,在代码块内部被声明的变量有可能无法被访问
用 let 和 const 声明会产生块作用域,外面无法访问
var 定义的变量不会产生块级作用域,外面可以访问
(二)全局作用域
<script> 标签 和 .js 文件的最外层,就是全局作用域,在其中声明的变量在其他作用域也可被访问
window 对象添加的属性默认也是全局的,不推荐
函数中没用关键字声明的 也默认为全局变量,不推荐
尽量少的减少全局变量,防止污染。
二、垃圾回收机制 GC
js 中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回回收器自动回收
(一)生命周期
js 环境中分配的内存,一般的生命周期:
1.内存分配
声明变量函数对象时,系统自动分配内存
2.内存使用
读写内存,使用变量函数等
3.内存回收
使用完毕,由垃圾回收器自动回收不再使用的内存
4.特殊情况——内存泄漏:
程序中的内存由于某种原因未释放,或无法释放
注意:
全局变量一般不回收,关闭页面时回收
局部变量的值,不用了会被自动回收
(二)算法说明
1.堆栈空间分配区别
1、栈:操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈里面。
2、堆:由程序员分配释放,如果程序员不释放,就由垃圾回收机制回收,复杂数据类型放到堆里面。
2.常见的浏览器垃圾回收算法
引用计数法(基本不咋用)
定义内存不再使用的对象,看对象是否有指向它的引用,如果没有引用了就回收对象。
但是如果两个对象相会被引用 就无法回收了,因为一直使用这两个对象
算法:
记录被引用的次数
被引用就次数加 1 多次就累加
如果减少就减 1 --
引用次数为 0 就释放内存
标记清除法
定义无法到达的对象,从根部定期扫描对象,就是全局变量找不到的对象,就需要被回收。解决了上一个算法的问题
三、闭包
(一)闭包简介
一个函数堆周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
内存函数加外层函数的变量就是闭包,就是里层函数使用外层函数的变量
<body>
<script>
function outer() {
const a = 1
function f() {
console.log(a)
}
f()
}
</script>
</body>
(二)闭包的基本格式
外部的函数调用了函数内部的变量,最后本质是用 fun() 调用了 fn 函数
<body>
<script>
function outer() {
const i = 1
function fn() {
console.log(i)
}
return fn
}
const fun =outer()
fun()
</script>
</body>
(三)闭包应用——实现函数的私有
比如统计函数被调用的次数,调用就次数加一
不能使用全局变量 容易被修改 所以可以用闭包进行封装
在外面改变 i 的值不会影响 结果的计算 因为 i 是局部作用域 在外面无法改变
而且 i 局部变量不会被回收 这是很巧妙的地方,外面定义了个全局作用域 result 一直在调用
count(),这样函数不结束被调用就不会被销毁,就能一直加加 i
这也属于内存泄露的一种情况
<body>
<script>
function count() {
let i = 0
function fn() {
i++
console.log(i)
}
return fn
}
const result = count()
</script>
</body>
四、变量提升
es6 引入了块级作用域,let const 就更方便了
允许变量被声明前被访问 只有 var 变量存在
在代码执行前,会检测所有 var 变量 然后提到当前作用域的最前面进行定义
下面这段代码不会报错,按理来说会报错的,因为代码按顺寻执行,但是因为 var 变量 变量提升,所以定义了 var 类型的变量 num 但是赋值不会提升,所以输出 undefined
只提升声明 不提升赋值
<body>
<script>
console.log(num)
var num = 10
</script>
</body>
五、函数进阶
(一)函数提升
代码执行前也会 把所有函数声明提到当前作用域的最前面
<body>
<script>
fn()
function fn(){
console.log('函数提升')
}
</script>
</body>
只提升函数声明不提升函数调用,调用还是按顺序执行的
下面这种情况会报错注意,因为下面 var 变量提升 但是只提升定义不提升赋值
就相当于
var fn
fn()
fn = function()
fn 被调用时不是个函数,所以会报错
<body>
<script>
fn()
var fn = function (){
console.log('函数提升')
}
</script>
</body>
(二)函数参数
1.动态参数
不知道实参的数量时 就可以用动态参数
arguments 函数内部内置的伪数组变量,包含了调用函数时传入的所有实参
只存在于函数内部,是动态的,实参有几个,伪数组里面就有几个
<body>
<script>
function getSum(){
console.log(arguments)
}
getSum(2, 3, 1)
</script>
</body>
将 arguments 伪数组里面的元素求和
<body>
<script>
function getSum() {
let sum = 0
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
console.log(sum)
}
getSum(2, 3, 1)
</script>
</body>
2.剩余参数
剩余参数也能完成上面的任务,不知道实参的数量,它是个真数组,可以用 push pop 方法
剩余参数允许我们将一个不定数量的参数表示为一个数组,它是把剩余的参数变成一个数组
用法差不多,实际开发中提倡使用 剩余参数
下面的例子中,先把 2 传到 参数 1 然后把 3 传给参数 b 最后把1, 5 ,9 单独封装成一个数
组,剩余参数的名字就是这么来的,就是被剩下的参数
<body>
<script>
function getSum(a, b, ...arr) {
console.log(arr)
}
getSum(2, 3, 1, 5, 9)
</script>
</body>
展开运算符
... 三个点,如果不用在 函数参数中,就是起到展开数组的作用
最大的用处就是 求数组最大值,合并数组等
<body>
<script>
const arr = [1, 2, 3]
console.log(...arr)
</script>
</body>
求最大值
...能把数组变成字符串的形式
<body>
<script>
const arr = [1, 2, 3]
console.log(Math.max(...arr))
</script>
</body>
合并数组
<body>
<script>
const arr1 = [1, 2, 3]
const arr2 = [2, 3, 4]
const arr = [...arr1, ...arr2]
console.log(arr)
</script>
</body>
(三)箭头函数
1.箭头函数介绍
引入箭头函数的目的是让函数书写更简短,而且不绑定 this 箭头函数比函数表达式更简洁,
主要用于本来需要使用匿名函数的地方
2.基本语法
两种函数新旧对比,参数就正常写在小括号中
<body>
<script>
// 旧版函数写法
const fn = function(){
console.log(123)
}
// 新版箭头函数写法
const fn1 = () => {
console.log(123)
}
</script>
</body>
如果参数参数只有一个,小括号可以省略
<body>
<script>
const fn = x => {
console.log(x)
}
fn(1)
</script>
</body>
只有一行代码时可以省略大括号
<body>
<script>
const fn = x => console.log(x)
fn(1)
</script>
</body>
箭头函数能直接返回一个对象,因为后面大括号和对象的大括号冲突了,所以用小括号包住
<body>
<script>
const fn = (uname) => ({ uname: uname })
fn('一个人')
</script>
</body>
2.箭头函数参数
箭头函数没有 arguments 动态参数,但是有剩余参数 ...args
<body>
<script>
const getSum = (...arr) => {
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
console.log(getSum(2, 3))
</script>
</body>
2.箭头函数 this
函数外面的 this 指向 window 默认是 window
dom 回调函数中还是不推荐使用 箭头函数
this 之前的定义是 this 指向函数的调用者
这里的 this 指向对象 obj
<body>
<script>
const obj = {
uname: '一个人',
say: function () {
console.log(this)
}
}
obj.say()
</script>
</body>
箭头函数的 this 指向箭头函数的上一级作用域链
下面代码都指向 window
<body>
<script>
const fn = () => {
console.log(this)
}
</script>
</body>
<body>
<script>
const obj = {
uname: '一个人',
say: function () {
console.log(this)
}
}
obj.say()
</script>
</body>
六、解构赋值
(一)数组解构
是将数组的单元值快速批量的赋值给一系列变量的简洁语法,就是把数组元素赋给变量,能分别得到三个变量
<body>
<script>
const arr = [100, 60, 80]
const [max, min, avg] = arr
console.log(max)
</script>
</body>
例子如下:数组解构时一定要在数组的前面加上分号,同理立即执行函数,要不然数组前面的数字就会连上数组 ,变成2 [b, a] = [a, b] 从而报错
<body>
<script>
let a = 1
let b = 2;
[b, a] = [a, b]
console.log(a, b)
</script>
</body>
特殊情况
1.变量多单元值少
如下代码,变量有a,b,c ,d 四个变量,但是只有1,2,3 三个单元值,abc 分别被赋值123,d的值为 undefined 很像前面的变量的赋值。
<body>
<script>
const [a,b,c,d] = [1,2,3]
</script>
</body>
2.变量少单元值多
多余的单元值就不进行赋值了
3.利用剩余参数解决 2
利用 ... 展开运算符来存其余的数值
<body>
<script>
const [a,b,...c]= [1,2,3,4]
</script>
</body>
4.防止 undefined 传递
设置一个默认参数就能解决
<body>
<script>
const [a = 0,b = 0]= [1,2]
</script>
</body>
5.按需导入忽略某些值
如下面例子 就忽略了 后面的值 3
<body>
<script>
const [a,b, ,d]= [1,2,3,4]
</script>
</body>
6.支持多维数组的解构
下面成功帮助多维数组解构了
<body>
<script>
const [a, b, [c,d]] = [1, 2, [3, 4]]
</script>
</body>
(二)对象解构(特别重要)
将对象属性和方法快速批量赋值给一系列变量的简洁语法
1.对象解构语法
注意:必须等号左右两边的属性名和变量名必须相同,而且之前不能起和对象内部属性相同的变量名,要不然就会 undefined。
下面的例子和数组结构的方法类似
<body>
<script>
const { uname, age } = {
uname: '一个人',
age: 18
}
console.log(uname)
</script>
</body>
2.对象解构改名
对象解构的变量名可以改名,但是语法很特殊,新改的名字写在后面。
<body>
<script>
const { uname: name, age } = {
uname: '一个人',
age: 18
}
console.log(name)
</script>
</body>
3.简单数组对象解构
<body>
<script>
const arr = [{
uname: '一个人',
age: 18
}]
const [{uname, age}] = arr
console.log(uname)
</script>
</body>
4.多级对象解构
就如下形式书写即可,数组对象同理
<body>
<script>
const pig = {
name: '佩奇',
family:{
mother: '猪妈妈',
father: '猪爸爸',
sister: '乔治'
}
}
const {name, family:{mother, father,sister}} = pig
console.log(mother)
</script>
</body>
遍历数组 forEach 方法 (重点)
和 map 遍历类似 但是 map 最后返回一个数组 forEach 只进行遍历不返回数组,可以看作加强版的 for 循环
语法:被遍历的数组.forEach(function (当前数组元素,当前元素索引号)){
函数体
}
当前数组元素就是数组里面的值,会依次遍历输出出来,index 是每个元素的下标,下标可以省略
<body>
<script>
const arr = ['red', 'green', 'pink']
const result = arr.forEach(function (item, index) {
console.log(item)
console.log(index)
})
</script>
</body>
筛选函数 filter 方法(重点)
filter() 方法是创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,用于筛选数组中符合条件的元素,并返回筛选后的数组。
<body>
<script>
const arr = [10, 20, 30]
const newArr = arr.filter(item => item >= 20)
console.log(newArr)
</script>
</body>
练习: 对象解构在函数中的用处
结果展示:
代码部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const msg = {
"code": 200,
"msg": "获取新闻列表成功",
"data": [
{
"id": 1,
"title": "5G商用自己,三大运营商收入下降",
"count": 58
},
{
"id": 2,
"title": "5G商用自己,三大运营商收入下降",
"count": 56
},
{
"id": 3,
"title": "5G商用自己,三大运营商收入下降",
"count": 1669
}
]
}
function jie({ data: myData }) {
console.log(myData)
}
jie(msg)
</script>
</body>
</html>