一、作用域
提到作用域(作用域又分为局部作用域和全局作用域),就要想到变量。因为作用域规定了变量能够被访问的范围(也就是作用域是为变量而服务的),为了避免全局变量污染这一情况,所以需要使用闭包函数创建隔离作用域。
这就是接下来我们要将作用域、作用域链、闭包,与变量联系起来的思路。
1.1 局部作用域
首先局部作用域分为函数作用域和块作用域。
函数作用域
函数作用域:就是在函数内部声明的变量在函数外部不能使用
function fn (e) {const i = 3console.log(i)console.log(e)}fn(4)//在函数外部会报错console.log(`这是${i}`)console.log(`这是${e}`)
块作用域
{ } 大花括号中的就是块作用域 比如if语句 for循环等
在块作用域中,代码块内部声明的变量外部将【有可能】无法被访问==》为什么是有可能===》因为如果是var变量,它不讲块作用域,所以外部可以访问====》但是如果是let或者const变量,外部就不可以被访问
for (var i = 1; i <= 3; i++) {console.log(i)}console.log(i)for (let j = 1; j <= 3; j++) {console.log(j)}console.log(j)
- let 声明的变量会产生块作用域,var 不会产生块作用域
- const 声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用 let 或 const
1.2 全局作用域
顾名思义,就是script标签和.js文件的最外层就是所谓的全局作用域,在这里定义的变量在函数内部可以被访问,函数内部就上上文讲的局部作用域。
尽量少使用,避免变量污染。所谓的变量污染,大家可以理解为例如有个变量,它正在配合一个任务按部就班地进行着(也就是下面的例子,调用一次函数,count++一次),忽然有一天,因为它是全局变量,别人给他更改了一个很大值,这时候原先的任务还在执行,调用结果就是在别人给他更改的值的基础上的++,这也就说明不能正常配合任务执行了,======》轻易地导致了全局变量的污染!
// count是全局变量 很容易被修改let count = 1function fn2 () {count++console.log(`函数被调用了${count}次`)}fn2()
1.3 作用域链
本质上是底层的变量查找机制
也就是函数被执行时,优先从当前函数作用域中查找变量;如果当前作用域查找不到会一次逐级查找父级作用域知道全局作用域。
总结来说就是嵌套关系的作用域串联起来形成了作用域,相同作用域链中按着从小到大的规则查找变量;子作用域能够访问父作用域,父作用域无法访问子作用域(中的变量)。
1.4 垃圾回收机制 (这部分待完善)
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。
正因为垃圾回收器的存在,所以大家在写JS不太注意内存管理的问题
但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况,所谓的内存泄漏就是:
不再用到的内存,没有及时释放
JS环境中分配的内存, 一般有如下生命周期:
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
- 说明:
全局变量一般不会回收(关闭页面回收);
一般情况下局部变量的值, 不用了, 会被自动回收掉
1.5 闭包
闭包 = 内层函数 + 外层函数的变量
两种表达方式:
function outer () {// 闭包 = 内层函数 + 外层函数的变量let a = 10表达方式一:// function fn () {/ / console.log(a)/ /}// return一个函数 就可以使用闭包了//return fn表达方式二:因为fn===function fn() 也就是return直接指向了function函数return function fn () {console.log(a)}return }// outer()===fn===function fn()const fun = outer()fun()
利用闭包思想,来避免全部变量受污染的情况 最好的方法就是不使用全局变量
<script>// count是全局变量 很容易被修改// let count = 1// function fn2 () {// count++// console.log(`函数被调用了${count}次`)// }// fn2()// 闭包应用:内部声明变量,实现数据的私有function fn () {let count = 1function fun () {count++console.log(`函数被调用${count}次`)}return fun}const result = fn()result()</script>
二、预解析(了解)
2.1 变量提升 var
<script>// var的变量提升// 所谓的变量提升就是当前变量在下面,上面使用的时候,就把所有var声明的变量提升到当前作用域的最前面// 只提升声明 不提升赋值// undefined 表示声明变量未给值// console.log(num + '件')// var num = 10// console.log(num + '件');function fn () {console.log(num)var num = 10// 相当于 var num// console.log(num)// num=10}fn()</script>
2.2 函数提升
建议:使用let或者const,遵循先声明后使用
<script>// 1.会把所有函数声明提升到当前作用域的前面// 2.提升函数声明 不提升函数调用fn()function fn () {console.log('函数提升')}// 函数表达式 必须先声明和赋值 后调用 否则报错fun()var fun = function () {console.log('函数表达式')}// 因为上面这句话是使用var定义的,根据var的变量提升,所以会有// var fun// fun()// function() {// console.log('函数表达式')// } 不可能实现声明里面没赋值就可以调用</script>
二、箭头函数
2.1 动态参数
- arguments 是一个伪数组(具有length和索引号,但是它不具有数组的push(), pop(), forEach(), map(), filter() 等方法。),只存在于函数中
- arguments 的作用是动态获取函数的实参
- 因为具有length和索引号,所以可以通过for循环依次得到传递过来的实参
- arguments的好处就是当我们传入的实参数量不固定时,也就是一会求两个数的和,一会求三个、四个…此时形参就是不固定的 此时使用arguments就非常好用 如下案例:
<script>// 每一个函数里面默认都有一个动态参数arguments作为形参,所以不用特别将arguments写进形参中// function getSum () {// console.log(arguments)// }// getSum(2, 5, 56)function getSum () {let sum = 0// arguments动态参数 只存在于函数里面for (let i = 0; i < arguments.length; i++) {sum += arguments[i]}console.log(sum)}getSum(2, 34, 5, 67, 3)</script>
2.2 this问题
查找this的方法:从当前作用域查找,如果查找不到 就按照作用域链来查找
案例一:最简单的函数调用中,this的指向问题
在这里有一个误区,因为是fn调用的,所以this指向fn 注意:fn()是函数的定义,不是调用者。fn()在调用时,全写为 window.fn(),所以调用者是window
function fn () {console.log(this)}fn()
案例二:不要看到{},就认为是作用域
不要看到{},就认为是作用域,这样会误认为obj是函数的调用者,作用域是由函数和块级语句创建的。我们判定作用域就是如果不是函数也不是块级语句,+含有{},就不是作用域。
案例:指向的是函数的调用者=>对象
const obj = {name: 'andy',sayHi: function () {console.log(this)//指向的是函数的调用者=>对象}}obj.sayHi()
案例三:全局作用域 指向window
// 全局作用域 指向windowconst obj1 = {uname: 'Pink老师',sayHi: () => {console.log(this)}}obj1.sayHi()
案例四:函数里面套箭头函数,指向obj2
因为箭头函数中没有this 所以往上一层找 通过上一层的函数指向了函数的调用者obj2
// 指向objconst obj2 = {uname: 'pink老师',sayHi: function () {let i = 10const count = () => {console.log(this)}count()}}obj2.sayHi()
剩余与展开
剩余参数的定义是 . . .数组名 是一个真数组 与arguments异曲同工
function getSum (...arr) {// console.log(arr)let sum = 0for (let i = 0; i < arr.length; i++) {sum += arr[i]}console.log(sum)}getSum(1, 2, 3)
但是,因为arguments需要在函数里面,这就带来一些不方便,所以…arr,它可以实现求最值,合并数组等功能。
<script>// const arr = [1, 2, 3]// console.log(...arr);// console.log(Math.max(...arr)) //里面放的只能是字符 不能是数字// 合并数组const arr1 = [1, 2, 3]const arr2 = [3, 4, 5]const arr3 = [...arr1, ...arr2]console.log(arr3);</script>
剩余参数的另一种应用
从前到后,形参和实参一一对应,但是如果实参过于多,后面的统一由剩余参数接收
<script>function getSum (a, b, ...arr) {console.log(arr) // 使用的时候不需要写 ...}getSum(2, 3)getSum(1, 2, 3, 4, 5)</script>
2.3 箭头函数
基本语法
<script>// const fn = function () {// console.log(123)// }// fn()// 1.箭头函数基本语法// const fn = () => {// console.log(123)// }// fn()// const fn = (x) => {// console.log(x)// }// fn(1)// 2.只有一个形参的时候 可以省略小括号// const fn = x => {// console.log(x)// }// fn(1)// 3.只有一行代码的时候 可以省略大括号// const fn = x => console.log(x)// fn(1)// 4.只有一行代码的时候 可以直接省略return// const fn = x => x + x// console.log(fn(1))// 5.箭头函数可以直接返回个对象 将大括号转化为小括号const fn = uname => ({ uname: uname })console.log(fn('刘德华'));</script>
使用箭头函数求和
<script>//箭头函数中没有arguments参数, 并且实参中传入的是字符,可以通过...转化为数组 很方便地实现数组中国数据的迭代const getSum = (...arr) => {let sum = 0for (let i = 0; i < arr.length; i++) {sum += arr[i]}return sum}const result = getSum(2, 3, 4)console.log(result);</script>
<script>// 以前this的指向 谁调用的这个函数 this就指向谁// console.log(this)// function fn () {// console.log(this)// }// window.fn()const obj = {name: 'anfy',sayHi: function () {console.log(this) //指向的是对象}}obj.sayHi()const obj1 = {uname: 'pink老师',sayHi: () => {console.log(this)// this指向window 因为箭头函数中没有this 所以要去window中找}}obj1.sayHi()// 当前区域没有this 按照作用域链往上一层找 函数里面有thisconst obj2 = {uname: 'pink老师',sayHi: function () {let i = 10const count = () => {console.log(this)}count()}}obj2.sayHi()btn.addEventListener('click', () => {console.log(this)})</script>
这里我有一个困扰 不知道大家有没有 就是我在纠结 btn.addEventListener(‘click’, () => {
console.log(this)
}) 我想的是箭头函数没有this 它可以跑到外层 外层就是add 不就可以指向函数的调用者了吗addEventListener 本身并不是一个作用域(scope),而是一个方法(method)。作用域是 JavaScript 中变量和函数可访问性的区域。主要由函数和代码块(如 { ... })创建。既然箭头函数中没有this 往外走到父级作用域 所以也就来到了全局作用域 全局作用域指向windowaddEventListener 的回调中,外层上下文并不是 addEventListener 函数本身,而是定义箭头函数的那个作用域。 箭头函数被定义在一个全局作用域或模块作用域中
<script>// 1.以前this的指向 谁调用的这个函数 this就指向谁// console.log(this)// function fn () {// console.log(this)// }// window.fn() //===fn()const obj = {name: 'anfy',sayHi: function () {console.log(this) //指向的是函数的调用者,也就是对象}}obj.sayHi()const obj1 = {uname: 'pink老师',sayHi: () => {console.log(this)// this指向window 因为箭头函数中没有this 所以要去window中找}}obj1.sayHi()// 当前区域没有this 按照作用域链往上一层找 函数里面有thisconst obj2 = {uname: 'pink老师',sayHi: function () {let i = 10const count = () => {console.log(this)}count()}}obj2.sayHi()btn.addEventListener('click', () => {console.log(this)})</script>
三、 解构赋值
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值
分为:数组解构
对象解构
3.1 数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法.
基本语法:
1.左侧的[ ]用于声明变量因为是多个,所以是批量声明变量,右侧数组的单元值将被赋值给左侧的变量中。
2.变量的顺序对应数组单元值的位置依次进行赋值操作。
// 数组解构 赋值const arr = [100, 60, 80]// const [max, min, avg] = arrconst [max, min, avg] = [100, 60, 80]console.log(max)console.log(min)console.log(avg)// 交换两个变量的值let a = 1let b = 2;[b, a] = [a, b]console.log(a, b);
必须加分号的两种情况:
// 1.立即执行函数要加分号(function () { })();(function () { })()// 2.使用数组的时候const arr = [1, 2, 3]const str = 'pink'// 真正数组前面要加上分号;[1, 2, 3].map(function (item) {console.log(item)})// 数组名不用加上分号// arr.map(function (item) {// console.log(item)// })
数组解构的一些细节:
// 1.变量多 单元值少 undefined// const [a, b, c, d] = [1, 2, 3]// console.log(a)// console.log(b)// console.log(c)// console.log(d)// 2.变量少 单元值多const [e, f] = [1, 2, 3]console.log(e)console.log(f)// 3.剩余参数 变量少 单元制多const [g, h, ...i] = [1, 2, 3, 4]console.log(g)console.log(h)console.log(i)// 4.防止有undefined传单元值 设置默认值// const [a = 0, b = 0] = []// console.log(a)// console.log(b)// const [c = '手机', d = '华为'] = ['小米']// console.log(c)// console.log(d)// 5.按需导入 忽略返回值 以逗号隔开// const [a, b, , d] = [1, 2, 3, 4]// console.log(a)// console.log(b)// console.log(d)// 6.支持多维数组结构// 一般的多维数组// const arr = [1, 2, [3, 4]] //二维// console.log(arr[0])// console.log(arr[1])// console.log(arr[2][0]);// 数组解构const [a, b, [c, d]] = [1, 2, [3, 4]] //二维console.log(a)console.log(b)console.log(c)console.log(d);
3.2 对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
对象解构的几种情况:
<script>// 1.对象解构// const obj = {// // 对象里面为属性 对象外面的为变量// uname: 'pink老师',// age: 18// }// 2.解构的语法 变量名字 属性名字相同 // const { uname, age } = {// // 对象里面为属性// uname: 'pink老师',// age: 18// }// console.log(uname)// console.log(age)// 3.对象解构的变量名 可以重新改名 旧变量名:新变量名// const { uname: username, age } = { uname: 'pink老师', age: 18 }// console.log(username)// console.log(age);// 4.数组对象解构const pig = [{uname: '佩奇',age: 18}]const [{ uname, age }] = pigconsole.log(uname);</script>
多级对象解构
// 多级对象解构 也就是对象里面套对象const pig = {name: '佩奇',family: {mother: '猪妈妈',father: '猪爸爸',sister: '乔治'},age: 7}const { name, family: { mother, father, sister } } = pigconsole.log(name)console.log(mother)console.log(father)console.log(sister);
对象解构综合案例
<script>// 1. 这是后台传递过来的数据const msg = {"code": 200,"msg": "获取新闻列表成功","data": [{"id": 1,"title": "5G商用自己,三大运用商收入下降","count": 58},{"id": 2,"title": "国际媒体头条速览","count": 56},{"id": 3,"title": "乌克兰和俄罗斯持续冲突","count": 1669},]}// 需求一,将以上msg对象 采用对象解构的方式 只选出data数据使用来作为渲染页面的数据// const { data } = msg// console.log(data)// 需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做参数传递给 函数// msg虽然很多属性 但是我们利用解构只要data值// const { data } = msg// 所以这个{}相当于在说要对那个对象进行对象解构function render ({ data }) {// const { data } = arrconsole.log(data)}render(msg)// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myDatafunction render ({ data: myData }) {console.log(myData)}render(msg)</script>
forEach方法
<script>const arr = ['red', 'green', 'pink']// item是必须要写的 // foreach就是纯遍历 加强版的for循环 适合于遍历数组对象const result = arr.forEach(function (item, index) {console.log(item)//每个数组元素console.log(index)//索引号})console.log(result)//不返回值</script>
综合案例
综合案例一:
小热身,先讲一个结合forEach方法小案例:
首先这个案例其实就是页面已经设计好了,只要我们拿到数据,渲染到页面就行了,下面的item就是需要填写的数据,这些数据如果没有js,我们填入的都是死的数据,现在有了js,数据就变成活得了,将获得的数据填写在item中,然后将item追加到list大页面中。又下面的item都是字符串,所以我们采用字符串拼接,最后追加到list中。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8aad29cd731a4d18903cda7d7f5609d0.png)
***完整代码 我贴在最后了***
```javascript//1.声明一个字符串变量let str = ''// 2.遍历数据,遍历8次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
filter 筛选数组 返回的是真数组
<script>const arr = [10, 20, 30]// const newArr = arr.filter(function (item, index) {// // 也有用item和index// // console.log(item)// // console.log(index)// return item >= 20// })// console.log(newArr);// 写成箭头函数const newArr = arr.filter(item => item >= 20)console.log(newArr);</script>
综合案例二、渲染函数——筛选(filter)
完整代码见 ## 完整代码2
// 1.渲染函数 封装function render (arr) {//声明空字符串let str = ''//遍历数组arr.forEach(item => {// 解构const { name, picture, price } = itemstr += `<div class="item"><img src=${picture} alt=""><p class="name">${name}</p><p class="price">${price}</p></div>`})//追加给listdocument.querySelector('.list').innerHTML = str}render(goodsList)//页面一打开就需要渲染// 2.过滤筛选document.querySelector('.filter').addEventListener('click', e => {const { tagName, dataset } = e.target//判断if (tagName === 'A') {let arr = goodsListif (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)}})
完整代码1
<!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.遍历数据,遍历8次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>
完整代码2
<!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>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 } = itemstr += `<div class="item"><img src=${picture} alt=""><p class="name">${name}</p><p class="price">${price}</p></div>`})//追加给listdocument.querySelector('.list').innerHTML = str}render(goodsList)//页面一打开就需要渲染// 2.过滤筛选document.querySelector('.filter').addEventListener('click', e => {const { tagName, dataset } = e.target//判断if (tagName === 'A') {let arr = goodsListif (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>