1、作用域
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问。
1.1 局部作用域
局部作用域可分为函数作用域和块作用域。
1.1.1 函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
总结:
- 函数内部声明的变量,在函数外部无法被访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
1.1.2 块作用域
在JavaScript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将 {有可能} 无法被访问。
- let声明的变量会产生块作用域,var不会产生块作用域
- const声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用let 或const
1.2 全局作用域
<script>标签和.js文件的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。
全局作用域中声明的变量,任何其它作用域都可以被访问。
注意:
- 为window对象动态添加的属性默认也是全局的,不推荐
- 函数中未使用任何关键字声明的变量为全局变量,不推荐
- 尽可能少的声明全局变量,防止全局变量被污染
1.3 作用域链
作用域链本质上是底层的变量查找机制
- 在函数被执行时,会优先查找当前函数作用域中查找变量
- 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
总结:
- 嵌套关系的作用域串联起来形成了作用域链
- 相同作用域链中按着从小到大的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域
1.4 JS垃圾回收机制
垃圾回收机制(Garbage Collecttion)简称GC
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
1.4.1 内存的生命周期
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
说明:
- 全局变量一般不会回收(关闭页面回收)
- 一般情况下局部变量的值,不用了,会被自动回收掉
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
1.4.2 JS垃圾回收机制-算法说明
1.栈(操作系统):由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面
2.堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。
常见的浏览器垃圾回收算法:引用计数法和标记清除法
(1)引用计数法
IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象。
算法:
- 跟踪记录被引用的次数
- 如果被引用了一次,那么就记录次数1,多次引用会累加++
- 如果减少一个引用就减1 --
- 如果引用次数是0,则释放内存
优点:
实时回收:引用计数可以在对象不再被引用时立即回收,不需要等待垃圾收集器的运行。这可以减少内存占用和提高程序的性能。
简单高效:引用计数是一种简单的垃圾收集算法,实现起来相对容易,不需要复杂的算法和数据结构。
缺点:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露
因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。因为这些对象仍然占据内存空间,却无法被释放。
(2)标记清除法
现代浏览器已经不再使用引用计数算法了
现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
核心:
- 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
- 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
- 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
标记所有的引用
1.5 闭包
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包=内层函数+外层函数的变量
作用:封闭数据,提供操作,外部也可以访问函数内部的变量。允许将函数与其所操作的某些数据(环境)关联起来
闭包的基本格式:
闭包应用:实现数据的私有
比如,我们要做个统计函数调用次数,函数调用一次,就++
但是,这个count是个全局变量,很容易被修改,可以改成以下形式
这样实现了数据私有,无法直接修改count,闭包也会有内存泄漏的问题。
1.6 变量提升
变量提升是JavaScript中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
注意:
- 变量在未声明即被访问时会报语法错误
- 变量在var声明之前即被访问,变量的值为undefined
- let/const声明的变量不存在变量提升
- 变量提升出现在相同的作用域当中
- 实际开发中推荐先声明再访问变量
2、函数进阶
2.1 函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用
总结:
- 函数提升能够使函数的声明调用更加灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
2.2 函数参数
2.2.1 动态参数
arguments是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
总结:
- arguments是一个伪数组,只存在于函数中
- arguments的作用是动态获取函数的实参
- 可以通过for循环依次得到传递过来的实参
2.2.2 剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组
(1)...是语法符号,置于最末函数形参之前,用于获取多余的实参
(2)借助...获取的剩余实参,是个真数组
开发中,还是提倡多使用剩余参数
2.2.3 展开运算符
展开运算符(...),将一个数组进行展开
说明:
- 不会修改原数组
典型运用场景:求数组最大值(最小值)、合并数组等
2.3 箭头函数
2.3.1 语法
语法1:基本写法
语法2:只有一个参数可以省略小括号
语法3:如果函数体只有一行代码,可以写到一行上,并且无需写return直接返回值
语法4:加括号的函数体返回对象字面量表达式,可以直接返回一个对象
总结:
- 箭头函数属于表达式函数,因此不存在函数提升
- 箭头函数只有一个参数时可以省略圆括号()
- 箭头函数函数体只有一行代码时可以省略花括号{},并自动做为返回值被返回
- 加括号的函数体返回对象字面量表达式
2.3.2 箭头函数参数
1.普通函数有arguments动态参数
2.箭头函数没有arguments动态参数,但是有剩余参数...args
2.3.3 箭头函数this
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值,非常令人讨厌。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
在开发中【使用箭头函数前需要考虑函数中this的值】,事件回调函数使用箭头函数时,this为全局的window,因此DOM事件回调函数为了简便,还是不太推荐使用箭头函数
3、解构赋值
以上要么不好记,要么书写麻烦,此时可以使用解构赋值的方法让代码更简洁
3.1 数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。
3.1.1 语法
基本语法:
- 赋值运算符 = 左侧的[]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
- 变量的顺序对应数组单元值的位置依次进行赋值操作
基本语法:典型应用交互2个变量
注意:js前面必须加分号情况
1.立即执行函数
2.数组解构,以数组开头
练习 完成数组解构赋值
需求①:有个数组:const pc = ['海尔','联想','小米','方正']
结构为变量:hr lx mi fz
需求②:请将最大值和最小值函数返回解构 max和min两个变量
<script>
const pc = ['海尔','联想','小米','方正']
const [hr,lx,mi,fz] = ['海尔','联想','小米','方正']
console.log(hr)
console.log(lx)
console.log(mi)
console.log(fz)
// 请将最大值和最小值函数返回值解构max 和min 两个变量
function getValue(){
return [100,60]
}
const [max,min] = getValue()
console.log(max)
console.log(min)
</script>
3.1.2 变量多 单元值少的情况
变量的数量大于单元值数量时,多余的变量将被赋值为undefined
3.1.3 变量少 单元值多的情况
3.1.4 利用剩余参数解决变量少 单元值多的情况
剩余参数返回的还是一个数组
3.1.5 防止有undefined传递单元值的情况,可以设置默认值
允许初始化变量的默认值,且只有单元值为undefined时默认值才会生效
3.1.6 按需导入,忽略某些返回值
3.1.7 支持多为数组的结构
3.2 对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
3.2.1 基本语法
- 赋值运算符 = 左侧的 {} 用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为undefined
3.2.2 给新的变量名赋值
可以从一个对象中提取变量并同时修改新的变量名
对象解构的变量名 可以重新改名 旧变量名:新变量名
3.2.3 数组对象解构
3.2.3 练习对象解构赋值
需求①:有个对象:const pig = {name:'佩奇',age:6}
结构为变量:完成对象解构,并以此打印出值
需求②:请将pig对象中的name,通过对象解构的形式改为uname,并打印输出
需求③:请将数组对象,完成 商品名和价格的解构
<script>
//①
// const pig = {name:'佩奇',age:6}
// const {name,age} = pig
// console.log(name,age)
//②
const pig = [{name:'佩奇',age:6}]
const [{name : uname,age}] = pig
console.log(uname,age)
// ③
const goods = [{goodsName:'小米',price:1999}]
const [{goodsName,price}] = goods
console.log(goodsName,price)
</script>
3.2.4 多级对象解构
3.2.5 练习 完成对象解构赋值
将数据完成3个需求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 1. 这是后台传递过来的数据
const msg = {
"code": 200,
"msg": "获取新闻列表成功",
"data": [
{
"id": 1,
"title": "5G商用自己,三大运用商收入下降",
"count": 58
},
{
"id": 2,
"title": "国际媒体头条速览",
"count": 56
},
{
"id": 3,
"title": "乌克兰和俄罗斯持续冲突",
"count": 1669
},
]
}
// 需求1: 请将以上msg对象 采用对象解构的方式 只选出 data 方面后面使用渲染页面
const {data} = msg
console.log(data)
// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数
function render({data}) {
// 我们只要 data 数据
// 内部处理
console.log(data)
}
render(msg)
// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({data:myData}) {
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理
console.log(myData)
}
render(msg)
</script>
</body>
</html>
4、foreach方法(重点)
- forEach()方法用于调用数组的每个元素,并将元素传递给回调函数
- 主要使用场景:遍历数组的每个元素
语法:
例如:
注意:
- forEach主要是遍历数组
- 参数当前数组元素是必须要写的,索引号可选
5、渲染商品列表
核心思路:有多少条数据,就渲染多少模块,然后生成对应的html结构标签,赋值给list标签即可
- 利用forEach遍历数据里面的数据
- 拿到数据,利用字符串拼接生成结构添加到页面中
- 注意:传递参数的时候,可以使用对象解构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商品渲染</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.list {
width: 990px;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
padding-top: 100px;
}
.item {
width: 240px;
margin-left: 10px;
padding: 20px 30px;
transition: all .5s;
margin-bottom: 20px;
}
.item:nth-child(4n) {
margin-left: 0;
}
.item:hover {
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
transform: translate3d(0, -4px, 0);
cursor: pointer;
}
.item img {
width: 100%;
}
.item .name {
font-size: 18px;
margin-bottom: 10px;
color: #666;
}
.item .price {
font-size: 22px;
color: firebrick;
}
.item .price::before {
content: "¥";
font-size: 14px;
}
</style>
</head>
<body>
<div class="list">
<!-- <div class="item">
<img src="" alt="">
<p class="name"></p>
<p class="price"></p>
</div> -->
</div>
<script>
const goodsList = [
{
id: '4001172',
name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
price: '289.00',
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
},
{
id: '4001594',
name: '日式黑陶功夫茶组双侧把茶具礼盒装',
price: '288.00',
picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: '109.00',
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: '488.00',
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
},
{
id: '3997185',
name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
price: '108.00',
picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
},
{
id: '3997403',
name: '手工吹制更厚实白酒杯壶套装6壶6杯',
price: '99.00',
picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
},
{
id: '3998274',
name: '德国百年工艺高端水晶玻璃红酒杯2支装',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
},
]
// 1.声明一个字符串变量
let str = ''
// 2. 遍历数据
goodsList.forEach(item =>{
// console.log(item) // 可以得到每一个数组元素 对象
// const {id} = item
const {name,price,picture} = item
str += `
<div class="item">
<img src=${picture} alt="">
<p class="name">${name}</p>
<p class="price">${price}</p>
</div>
`
})
document.querySelector('.list').innerHTML = str
</script>
</body>
</html>
6、 筛选数组filter方法(重点)
filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
主要使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组
语法:
参数:currentValue必须写,index可选
因为返回新数组,所以不会影响原数组
例如:
7、综合案例
需求:
- 渲染数据列表 分析:
- 根据filter选择不同条件显示不同商品
- 渲染页面利用forEach遍历数据里面的数据,并渲染数据列表
商品列表价格筛选
- 根据选择不同条件显示不同商品
- 点击采取事件委托方式.filter
- 利用过滤函数filter筛选出符合条件的数据,因为生成的是一个数组,传递给渲染函数即可
- 筛选条件是根据点击的data-index来判断
- 可以使用对象解构,把事件对象解构
- 因为全部区间不需要筛选,直接把goodList渲染即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商品渲染</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.list {
width: 990px;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
.item {
width: 240px;
margin-left: 10px;
padding: 20px 30px;
transition: all .5s;
margin-bottom: 20px;
}
.item:nth-child(4n) {
margin-left: 0;
}
.item:hover {
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
transform: translate3d(0, -4px, 0);
cursor: pointer;
}
.item img {
width: 100%;
}
.item .name {
font-size: 18px;
margin-bottom: 10px;
color: #666;
}
.item .price {
font-size: 22px;
color: firebrick;
}
.item .price::before {
content: "¥";
font-size: 14px;
}
.filter {
display: flex;
width: 990px;
margin: 0 auto;
padding: 50px 30px;
}
.filter a {
padding: 10px 20px;
background: #f5f5f5;
color: #666;
text-decoration: none;
margin-right: 20px;
}
.filter a:active,
.filter a:focus {
background: #05943c;
color: #fff;
}
</style>
</head>
<body>
<div class="filter">
<a data-index="1" href="javascript:;">0-100元</a>
<a data-index="2" href="javascript:;">100-300元</a>
<a data-index="3" href="javascript:;">300元以上</a>
<a href="javascript:;">全部区间</a>
</div>
<div class="list">
<!-- <div class="item">
<img src="" alt="">
<p class="name"></p>
<p class="price"></p>
</div> -->
</div>
<script>
// 2. 初始化数据
const goodsList = [
{
id: '4001172',
name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
price: '289.00',
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
},
{
id: '4001594',
name: '日式黑陶功夫茶组双侧把茶具礼盒装',
price: '288.00',
picture: 'https://yanxuan-item.nosdn.127.net/3346b7b92f9563c7a7e24c7ead883f18.jpg',
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: '109.00',
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: '488.00',
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
},
{
id: '3997185',
name: '与众不同的口感汝瓷白酒杯套组1壶4杯',
price: '108.00',
picture: 'https://yanxuan-item.nosdn.127.net/8e21c794dfd3a4e8573273ddae50bce2.jpg',
},
{
id: '3997403',
name: '手工吹制更厚实白酒杯壶套装6壶6杯',
price: '99.00',
picture: 'https://yanxuan-item.nosdn.127.net/af2371a65f60bce152a61fc22745ff3f.jpg',
},
{
id: '3998274',
name: '德国百年工艺高端水晶玻璃红酒杯2支装',
price: '139.00',
picture: 'https://yanxuan-item.nosdn.127.net/8896b897b3ec6639bbd1134d66b9715c.jpg',
},
]
// 1. 渲染函数 封装
function render(arr){
// 声明空字符串
let str = ''
// 遍历数组
arr.forEach(item =>{
// 解构
const {name,picture,price} = item
str += `
<div class="item">
<img src=${picture} alt="">
<p class="name">${name}</p>
<p class="price">${price}</p>
</div>
`
})
// 追加给list
document.querySelector('.list').innerHTML = str
}
render(goodsList) // 页面一打开就需要渲染
// 2. 过滤筛选
document.querySelector('.filter').addEventListener('click',e =>{
// e.target.dataset.id
const {tagName,dataset} = e.target
// 判断
if(e.target.tagName === 'A'){
// console.log(11)
// arr返回的新数组
let arr = goodsList
if(dataset.index === '1'){
arr = goodsList.filter(item => item.price > 0 && item.price <= 100)
} else if(dataset.index === '2'){
arr = goodsList.filter(item => item.price >= 100 && item.price <= 300)
} else if(dataset.index === '3'){
arr = goodsList.filter(item => item.price >= 300)
}
// 渲染函数
render(arr)
}
})
</script>
</body>
</html>