JS高级-1

204 阅读9分钟

作用域

作用域对程序执行的影响及作用域链的查找机制,使得闭包函数创建隔离作用域避免全局变量污染。

作用域:规定了变量能够被访问的“范围”,离开了这个范围,变量便不能被访问。

作用域可以被分为:局部作用域、全局作用域

局部作用域

局部作用域分为:函数作用域和块作用域

函数作用域

在函数内部声明的变量,只能在函数内部被访问,外部无法直接访问。

function getSum(){
    //函数内部是函数作用域、属于局部变量
    const num = 10
}
console.log(num)//报错,超出作用域范围。

总结:
1.函数内部声明的变量,在函数外部无法被访问
2.函数的参数也是函数内部的局部变量
3.不同函数内部声明的变量无法互相访问
4.函数执行完毕后,函数内部的变量实际上被清空了

块作用域

在JavaScript中,使用{}包裹的代码称为“代码块”,代码块内部声明的变量,外部将【有可能(用var可以、var没有块级作用域)】无法被访问

for(let i = 1;i<=6;i++){
    //i只能在该代码块中被访问
    console.log(i)//正常
}
//超出i的作用域
console.log(i)//报错

如果使用var声明的变量,外部是可以被访问的

for(var i = 1;i<=6;i++){
    console.log(i)//正常
}
console.log(i)//可以被访问

总结: 1.let声明的变量,会产生块级作用域。而var不会产生块级作用域
2.const声明的常量也会产生作用域
3.不同代码块之间的变量无法互相访问
4.推荐使用let或const

全局作用域

<script>标签和.js文件的【最外层】就是所谓的全局作用域,再次声明的变量在函数内部也可以被访问。

全局作用域中声明的变量,任何其他作用域都可以被访问。

<script>
    //此处全局作用域
    //全局作用域下声明了num变量
    const num = 10
    function fun(){
        //函数内部可以使用全局作用域的变量
        console.log(num)
    }
    //此处全局作用域
</script>

注意:
1.为window对象动态添加的属性,默认也是去全局的,不推荐!window.a = 10
2.函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
3.尽可能少的声明全局变量,防止全局变量被污染。

作用域链

作用域链的本质:是底层的【变量查找机制】
1.在函数被执行时,会优先查找【当前函数作用域】中查找变量
2.如果【当前作用域】查找不到,则会依次逐层查找父级作用域,直到全局作用域。

<script>
    //全局作用域
    let a = 1
    let b = 2
    
    //局部作用域
    function f(){
        let a = 1
        //局部作用域
        function g(){
            a = 2
            console.log(a)
        }
        g()//调用g
    }
    f()//调用f
</script>

输出结果:2 2

总结:
1.嵌套关系的作用域,串联起来,形成了作用域链
2.相同作用域链中,按着从小到大的规则查找变量
3.子作用域能够访问父作用域,父作用域无法访问子级作用域。

image.png

JS垃圾回收机制

垃圾回收机制:(Garbage Collection)GC,JS中内存的分配和回收都是自动完成的,内存在不使用的时候就会被垃圾回收器自动回收。

正因为垃圾回收器的存在,许多人认为JS不用太关心内存管理的问题。

但如果不了解JS的内存管理机制,我们统一容易造成内存泄漏(内存无法被回收)的情况。

不再用到的内存,没有及时释放,就叫做内存泄漏

内存的生命周期:
1.内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
2.内存使用:即读写内存,也就是使用变量、函数等
3.内存回收:使用完毕,由垃圾回收,自动回收不再使用的内存
4.全局变量一般不会被回收
5.一般情况下局部变量的值,不用了,会被自动回收掉

image.png

所谓垃圾回收,核心思想就是:如何判断内存是否已经不会再被使用了,如果是,就是为了垃圾,释放。

有常见的两种方法:引用计数法标记清除法

引用计数法

引用计数法:就是看一个对象,是否有指向他的引用。

算法:
1.跟踪记录每个值被引用的次数
2.如果这个值,被用了一次,那么就记录次数1
3.多次引用就会累加
4.如果减少一个引用,就减1
5.如果引用次数是0,则释放内存

image.png

image.png

下方这个是一个块级作用域,按道理,块结束后,会将里面的内存释放掉。但是由于o1.a=o2o2.a=o1他们相互指向,引用次数就会0+1=1,此时引用次数如果不是0,就不会被释放掉。因此在嵌套中,使用引用计数法,容易存在内存泄漏的问题。

image.png

标记清除法

标记清除法:现在的浏览器已经不再使用引用计数法了,而是基于标记清除法的算法,进行改进、但是总体思想都是一致的。

核心:
1.标记清除法:将“不再使用的对象”定义为“无法达到的对象”
2.就是从根部(JS全局对象)出发,定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的。
3.那些无法由根部触发触及到的对象、被标记为不再使用,稍后进行回收。

function fn(){
    let o1 = {}
    let o2 = {}
    o1.a = o2
    o2.a = o1
}
fn()

global从最外层往里面找,找到o1o2,找到o1.a、找到o2.a,函数结束后,将o1o2进行正常回收清除,但是o1.ao2.a由于存在引用,没有被清除掉,但是在标记清除法中,global再次从外部来查找的时候,o1o2已经被回收掉了,因此里面的o1.ao2.a根本就找不到,因此给他进行标记,稍后进行回收。

image.png

image.png

闭包

闭包:一个函数对周围状态的引用,捆绑在一起。内层函数中访问到外层函数的作用域。

闭包 = 内层函数 + 外层函数的变量

function outer(){
    let a = 1
    function f(){
    //里面的函数,使用外层函数的变量
        console.log(a)
    }
    f()
}
outer()

闭包的作用:封闭数据,提供操作、外部也可以访问函数内部的变量。

基本格式:

function outer(){
    let i = 1
    function fn(){
        console.log(i)
    }
    
    //将函数返回出去,不用写小括号,写小括号表示:调用
    return fn
}
//outer() 返回者,等价于fn
const fun = outer()
fun() //1
//外层函数使用内部函数的变量

因为正常情况下,函数内部的值,我们是用不了的。

第二种常见的写法:既然我fn是一个函数,那我可以直接return:

function outer(){
    let i = 1
    return function(){
        console.log(i)
    }
}
const fun = outer()
fun()

第三种写法:

function outer(){
    let i = 1
    return function(){
        return i
    }
}
//outer() 接到了匿名函数
const fun = outer()
console.log(fun())

闭包的应用

闭包应用:实现数据的私有

比如,我们要做个统计函数调用次数,函数调用一次,就++

let i = 0
function fn(){
    i++
    console.log(i)
}

这样可以实现功能,但是我的i值很容易被篡改,这个时候通过闭包就可以实现数据的私有化。

function fn(){
    let i = 0
    return function(){
        i++
        console.log(i)
    }
}
const test = fn()
console.log(test())

此时,外部想要在进行修改,就不会修改i的值,实现了数据的私有化。

那么此时i会被垃圾回收机制,回收掉吗?

答案是不会!因为const test = fn()是一个全局,全局作用域是不会被回收的、只有不使用的时候才会被回收。根据标记法,test会调用functionfunction又用到了fn中的i,一直再调用、一直被找到。所以不会被回收掉。

因此闭包会存在内存泄漏的风险。

image.png

变量提升

变量提升是JavaScript中比较“奇怪”的现象,它允许在变量声明之前就被访问(仅存在与var声明变量)

把所有var声明的变量,提升到 当前作用域的最前面
只提升声明,不提升赋值

console.log(num)//undefined
var num = 10

等价于=》

var num
console.log(num)//undefined
num = 10

注意:
1.变量在未声明,就被访问时,会报语法错误
2.变量在var声明之前被访问,变量的值是undefined
3.let/const声明的变量不存在变量提升
4.变量提升出现在相同作用域中
5.实际开发中,更推荐先声明,再访问变量

image.png

函数提升

会把所有函数声明,提升到当前作用域的前面
只提升函数声明,不提升函数调用

fn() //打印出来了
function fn(){
    console.log('函数提升')
}

等价于:

function fn(){
    console.log('函数提升')
}

fn()

function fn(){
    console.log('函数提升')
}

函数表达式就不可以,比较特殊

fun()//undefined
var fun = function(){
    console.log('函数表达式')
}

等价于=》

var fun
fun()//undefined
fun = function(){
    console.log('函数表达式')
}

函数表达式:必须先赋值,后调用!!!他不存在函数提升现象!!!

image.png

函数参数

写一个求和函数:
不管用户传入几个实参,都要把和求出来!

这个时候就可以使用:动态参数
arguments[函数内部内置的伪数组变量],它包含了调用函数时,传入的所有实参

function getSum(){
    //arguments 动态参数,只存在于 函数中
    console.log(arguments)//返回Arguments(2)伪数组,里面存放了2个参数值
}

getSum(2,3)

通过传入的参数,求参数和:

function getSum(){
    let sum = 
    for(let i = 0;i<arguments.length;i++){
        sum += arguments[i]
    }
}
getSum(2,3,4)
getSum(2,3,4,5,6,7)

image.png

剩余参数

还是一个求和参数,但是不知道用于到底传进来多少实参,但是都要把它们的和求出来。

那我形参咋写?除了动态参数,还有剩余参数也可以实现。

剩余参数:允许我们将一个不定数量的参数表示为一个数组

function getSum(a,b...arr){
    //使用的时候,不需要写... 直接拿就行
    console.log(arr)//[3,4,5]
}
getSum(1,2,3,4,5)

a=1b=2,剩下的3,4,5归于剩余参数

...是语法符号,置于最末函数的形参之前,用于获取多余的实参。

借助...获取的剩余实参,是一个真数组!这是比arguments优越的地方!

开发中,我们更提倡使用剩余参数

展开运算符

展开运算符(...),将一个数组进行展开

const arr = [1,5,3,8,2]
//展开一个数组
console.log(...arr) // 1,5,3,8,2

说明:
1.他不会修改原数组\

他最典型的应用场景:求数组最大值(最小值)、合并数组等

const arr = [1,2,3]

//Math.max中的参数,要求是字符串类型,因此数组被限制了
console.log(Math.max(1,2,3))

//求数组中的最大值,展开运算符返回的是字符串
console.log(Math.max(...arr))//3

console.log(Math.min(...arr))//1

合并数组:

const arr1 = [1,2,3]
const arr2 = [4,5,6]
const arr = [...arr1,...arr2]//1,2,3,4,5,6

剩余参数和展开运算符的区别:
剩余参数:【函数参数】中使用,得到真数组
展开运算符:【数组】中使用,数组展开

image.png

箭头函数

引入箭头函数,可以使用更简短的函数写法,并且不用绑定this,箭头函数语法比函数表达式更简洁

箭头函数更适用于那些本来需要匿名函数的地方

普通函数

const fn = function(){
    console.log(123)
}

箭头函数

const fn = () => {
    console.log(123)
}
fn()
const fn = (x) => {
    console.log(x)//1
}
fn(1)
  1. 当箭头函数只有一个形参的时候,可以省略小括号
const fn = x => {
    console.log(x)
}
fn(1)
  1. 当箭头函数只输出一句话的时候,可以省略大括号
const fn = x => console.log(x)
fn(1)
  1. 当箭头函数只输出一句话,并且是返回值,可以省略return
//return x+x的return被省略
const fn = x => x + x

fn(1)
  1. 更简洁的写法:e是event,事件参数 form.addEventListener('click',e => e.preventDefault())

  2. 箭头函数,可以直接返回一个对象

const fn = name => ({uname:name})

fn('刘德华')

name就是形参传过来的刘德华

因为对象是用{}来包裹的,而箭头函数中,输出语句也是用{}来包裹,为了造成问题,对象的输出需要用()来包裹

image.png

箭头函数参数

普通函数有arguments动态参数
箭头函数没有arguments动态参数,但是有剩余参数...args

利用箭头函数来求和

const getSum = (...args) => {
    let sum = 0
    for(let i =0;i<args.length;i++){
        sum += args[i]
    }
    return sum
}
const result = getSum(2,3)
console.log(result)//5

箭头函数this

this指向:谁调用的这个,this就指向谁

console.log(this)//指向window
//普通函数
function fn(){
    console.log(this)//指向window
}
因为函数:window.fn()

//对象方法中的this
const obj = {
    name: 'andy',
    sayHi:function(){
        console.log(this)//这个方法是obj调用的
    }
}
调用:obj.sayHi()

箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值,非常令人讨厌。

但是!!!箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this

const fn = () => {
    console.log(this)//window
}

这里不是因为默认window调用,箭头函数里没有this
这里是因为fn()是一个函数作用域,他的上一层是window,所以这里this指向的才是window

fn()

对象方法中的箭头函数this

const obj = {
    uname:'pink',
    sayHi:() => {
        console.log(this)
    }
}
obj.sayHi()//this指向window

sayHi()的作用域往上找,就是obj,而obj的this是由window调用的,所以这里返回window

cosnt obj = {
    uname:'pink',
    sayHi:function(){
        let i = 10
        const count = () => {
            console.log(this)
        }
        count()
    }
}
obj.sayHi()//obj;
    箭头函数没有this,他的上一级就是他的this指向,
    上一级是sayHi:function(),他的this指向是obj,因此返回obj

在开发中,【使用箭头函数前,需要考虑函数中this的值】,事件回调函数使用箭头函数时,this为全局的window,因此DOM事件回调函数为了简便,还是不太推荐使用箭头函数!

btn.addEventListener('click',function(){
    console.log(this)//指向btn
})

btn.addEventListener('click',()=>{
    console.log(this)//btn的this指向,就是window
})

image.png

数组解构

const arr = [100,60,80]
console.log(arr[0])
console.log(arr[1])
console.log(arr[2])
const arr = [100,60,80]
const max = arr[0]
const min = arr[1]
const avg = arr[2]
console.log(max)
console.log(min)
console.log(avg)

以前的写法太过于麻烦,我们通过数组解构来实现代码更简洁。

数组解构:将数组的单元值,快速批量的赋值给一系列变量的简洁语法。

const [max,min,avg] = [100,60,80]
console.log(max)
console.log(min)
console.log(avg)

基本语法:
1.赋值运算符 = 左侧的[]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量。

2.变量的顺序对应数组单元值的位置,依次进行赋值操作。

const arr = [100,60,80]
//数组解构 赋值
const [max,min,avg] = arr 

数组解构的典型应用:交互2个变量;这里必须是let,因为const无法重新赋值。

let a = 1
let b = 3; //这里必须加分号
[b,a] = [a,b]
console.log(a)//3
console.log(b)//1

image.png

必须加分号的两种情况

1.立即执行函数: (function(){})();

2.使用数组的时候:

const str = 'pink';
[1,2,3].map(function(item){
    console.log(item)
})

因为不希望你用数组开头,进行书写。

let a = 1
let b = 2;
[b,a] = [a,b]

image.png

练习

image.png

const pc = ['海尔','联想','小米','方正']

const [hr,lx,mi,fz] = pc
function getValue(){
    return [100,60]
}

const [max,min] = getValue()

数组解构特殊情况

  1. 变量多,单元值少的情况 const [a,b,c,d] = [1,2,3]
    a=1、b=2、c=3、d:undefined

  2. 变量少,单元值多的情况 const [a,b] = [1,2,3]
    a=1,b=2

  3. 剩余参数 变量少,单元值多 const [a,b,...c] = [1,2,3,4] a=1,b=2,c=[3,4],c是以一个真数组的形式存储剩余参数

  4. 防止undefined传递、给个默认值就行 const [a='手机',b='华为'] = ['小米'] a=小米,b=华为

  5. 按需导入,忽略某些返回值: const [a,,c,d]=['小米','苹果','华为','格力'] a=小米,c=华为,d=格力

多维数组解构

cosnt arr = [1,2 [3,4]]

console.log(arr[2])//[3,4]
console.log(arr[2][0])//3

用解构:

const arr = [1,2[3,4]]

const [a,b,c] = [1,2[3,4]] 

console.log(a)//1
console.log(b)//2
console.log(c)//[3,4]

--------------------------------------
const [a,b[c,d]] = [1,2[3,4]]
console.log(a)//1
console.log(b)//2
console.log(c)//3
console.log(d)//4

image.png

对象解构

对象解构,是将对象属性和方法,快速批量的赋值给一系列变量的简洁语法。

基本语法:
1.赋值运算符=左侧的{}用于批量声明变量(const user),右侧对象的属性值将被赋值给左侧的变量
2.对象属性的值,将被赋值给属性名相同的变量
3.注意解构的变量名不要和外面的变量名冲突,否则报错
4.对象中找不到与变量名一致的属性时,变量值为undefined

//对象解构
const obj = {
    uname : 'pink',
    age : '18'
}

//obj.uname

//解构的语法
const {uname,age} = {
    uname:'pink',
    age:18
}
等价于:const uname = obj.uname

要求属性名变量名必须一致。

对象解构变量重命名

const uname = 'red'

const {uname,age} = {uname:'pink',age:18}

我就是想外面也有一个叫uname的不行吗?

对象解构的变量名,可以重新改名:旧变量名:新变量名

const uname = 'red'
const {uname:username,age} = {uname:'pink',age:18}

冒号表示:x:y = x将值赋值给y

数组对象的结构

const pig = [
    {
        uname:'佩奇',
        age:18
    }
]
const [{uname,age}] = pig

多级对象解构

const pig = {
    name:'佩奇',
    family:{
        mother:'猪妈妈',
        father:'猪爸爸',
        sister:'乔治'
    },
    age:6
}

//多级对象解构
const {name,family:{mother,father,sister},age} = pig

要加上对象的名字,进行多级对象的结构

数组对象解构:

const person = [
    {
        name:'佩奇',
        family:{
            mother:'猪妈妈',
            father:'猪爸爸',
            sister:'乔治'
        },
        age:6
    }
]
const [{name,family:{mother,father,sister},age}] = person

多级案例

JSON对象格式:"msg":"信息",是双引号

// 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

需求2: 上面msg是后台传递过来的数据,我们需要把data选出当做【参数】传递给 函数\

function render(arr){
    const {data} = arr
    //我们只要data数据,内部处理
    console.log(data)
}

render(msg)

更简洁的写法:因为arr和data是一样的

function render({data}){
    console.log(data)
}

render(msg)

需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData\

function render({data:myData}){
    console.log(myData)
}
render(msg)

forEach遍历

forEach()方法用来调用数组的每个元素,并将元素传递给回调函数。

主要使用场景:遍历数组的每个元素;只遍历、不返回数组

语法:

被遍历的数组.forEach(function(当前数组元素,当前元素索引号)){
    //函数体
}
const arr = ['red','green','pink']
arr.forEach(function(item,index){
    console.log(item)//red green pink
    console.log(index)//索引号
})

注意:
当前数组元素是必须要写的,索引号是选写
适合遍历【数组对象】

渲染商品案例

image.png

  <div class="list">
    <!-- <div class="item">
      <img src="" alt="">
      <p class="name"></p>
      <p class="price"></p>
    </div> -->
  </div>
  
    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',
      },
    ]

0.声明一个字符串变量let str = ''

1.通过forEach遍历数据里面的数据

goodsList.forEach(item=>{
    console.log(item)//可以得到每个数组元素,里面都是一个个对象
    //循环数据,循环几次,就创建多少div
    str += `
      <div class="item">
          <img src="" alt="">
          <p class="name"></p>
          <p class="price"></p>
      </div>`
})
document.querySelector('.list').innerHTML = str

image.png

2.拿到数据,利用字符串拼接,生成解构,添加到页面中

goodsList.forEach(item=>{
//只要name,就解构name
    const {name,price,picture} = item
    str += `
      <div class="item">
          <img src="picture" alt="">
          //解构前:<p class="name">${item.name}</p>
          //解构后:<p class="name">${name}</p>
          <p class="price">${price}</p>
      </div>`
}
综合案例

tab栏切换:css写法

    .filter a:active,
    .filter a:focus {
      background: #05943c;
      color: #fff;
    }
  <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: '100.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',
      },
    ]

//渲染
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(goodList)

filter筛选数组

filter()方法【创建一个新的数组】,新数组中的元素是通过检查指定数组中,符合条件的所有元素。

主要使用场景:筛选数组中符合条件的元素,并返回筛选之后元素的新数组。

语法:

const arr = [10,20,30]

//会返回一个新数组,需要接收一下
const newArr = arr.filter(item=>{
    //找出大于等于20的数组
    return item >= 20
})

简写(省略了return和大括号):const newArr = arr.filter(item=> item>=20)

filter()中item必须写,index是可以选的,因为返回的是一个新数组,所以对原数组没有影响。

image.png

//过滤筛选
document.querySelector('.filter').addEventListener('click',e=>{
    //e.target 这个里面有tagName和dataset的属性,解构出来
    const {tagName,dataset} = e.target
    //判断 (e.target.tagName === 'A')
    if(tagName === 'A'){
        //arr是返回的新数组,先将goodList赋值给arr,因为如果点4就是渲染全部,需要将goodList拿过来
        let arr = goodList
        if(dataset.index === '1'){
            arr = goodList.filter(item=>
                item.price > 0 && item.price <= 100)
        }
        else if(dataset.index === '2'){
            arr = goodList.filter(item=>
                item.price >= 100 && item.price <= 300)
        }
        //如果点的是4,4标签是渲染全部
        render(arr)
    }
})