作用域和ES6+新特性

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

作用域

作用域scope规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,作用域分为局部作用域和全局作用域。

局部作用域

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

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

块作用域

在Javascript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问。

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

全局作用域

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

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

作用域链

作用域链本质上是底层的变量查找机制,简单讲是:就近原则。

在函数被执行时,会优先查找当前函数作用域中查找变量
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域

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

垃圾回收机制

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

内存的生命周期

JS环境中分配的内存,一般有如下生命周期:

  1. 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
  2. 内存使用:即读写内存,也就是使用变量、函数等
  3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存

程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏

算法说明

堆栈空间分配区别:

栈:由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。

堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。

下面介绍两种常见的浏览器垃圾回收算法引用计数法 和 标记清除法

引用计数

IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象

跟踪记录被引用的次数

如果被引用了一次,那么就记录次数1,多次引用会累加++

如果减少一个引用就减1--

如果引用次数是0,则释放内存

js嵌套引用

但它却存在一个致命的问题:嵌套引用(循环引用)。
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。
因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露

标记清除法

现代的浏览器已经不再使用引用计数算法了,大多是基于标记清除算法的某些改进算法,总体思想都是一致的。

① 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。

② 从根部(全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。

③ 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

闭包

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

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

function outer() {
  let a = 10
  function fn() {
    console.log(a)
  }
  fn()
}
outer()

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

// 常见的闭包的形式 外部可以访问使用函数内部的变量
function outer() {
  let a = 10
  function fn() {
    console.log(a)
  }
  return fn // 返回fn
}
outer()  //outer()  === fn  === function fn(){}
const fun = outer()
fun() // 调用函数

// 外面使用这个10

闭包应用:实现数据的私有,比如我们要做个统计函数调用次数,函数调用一次就++

// 闭包的形式
function count() {
  let i = 0
  function fn() {
    i++
    console.log(`函数被调用了${i}`)
  }
  return fn
}
const fun = count()

fun() //函数被调用了 1
fun()// 函数被调用了 2
fun()// 函数被调用了 3
fun()// 函数被调用了 4

// 统计函数调用的次数
// let i = 0
// function fn() {
//   i++
//   console.log(`函数被调用了${i}`)
// }
// 因为 i 是全局变量,容易被修改

变量提升

变量提升是Javascript 中比较奇怪的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)

  1. 变量在未声明即被访问时会报语法错误
  2. 变量在var声明之前即被访问,变量的值为undefined
  3. let const声明的变量不存在变量提升
  4. 变量提升出现在相同作用域当中
  5. 实际开发中推荐先声明再访问变量
// 1. 把所有var声明的变量提升到 当前作用域的最前面
// 2. 只提升声明,不提升赋值
var num
console.log(num + '件')
var num = 10     // undefined件

var num1
console.log(num1 + '件') // undefined件
num1 = 10
console.log(num1) // 10

function fn() {
  console.log(num2)
  var num2 = 10
}
fn() // undefined

JS初学者经常花很多时间才能习惯变量提升,还经常出现一些意想不到的bug,正因为如此,ES6引入了块级作用域,用let或者const声明变量,让代码写法更加规范和人性化。

函数进阶

函数提升

需要知道函数参数默认值、动态参数、剩余参数的使用细节,提升函数应用的灵活度,知道箭头函数的语法及与普通函数的差异。

  1. 函数提升能够使函数的声明调用更灵活
  2. 函数表达式不存在提升的现象
  3. 函数提升出现在相同作用域当中
// 1. 会把所有函数声明提升到当前作用域的最前面
// 2. 只提升函数声明,不提升函数调用
fn() // 函数提升
function fn() {
  console.log('函数提升')
}

// 函数表达式,必须先声明后报错
// var fun = function () {
//   console.log('函数提升')
// }
// fun()

函数参数

函数参数的使用细节,能够提升函数应用的灵活度。

动态参数

arguments是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。

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

  1. arguments是一个伪数组,只存在于函数中
  2. arguments的作用是动态获取函数的实参
  3. 可以通过for循环依次得到传递过来的实参
function getSum() {
  let sum = 0
  // arguments动态参数,只存在于函数里面
  console.log(arguments)  // 伪数组
  for (let i = 0; i < arguments.length; i++) {
    sum = sum + arguments[i]
  }
  console.log(sum) // 9
}
getSum(2, 3, 4)
getSum(1, 5, 2, 3, 4) // 15

剩余参数

剩余参数允许我们将一个不定数量的参数表示为一个数组,开发中,还是提倡多使用 剩余参数。

  1. ...是语法符号,置于最末函数形参之前,用于获取多余的实参
  2. 借助...获取的剩余实参,是个真数组
  3. 只能函数里面用
function getSum(a, b, ...arr) {
  console.log(arr)
}
getSum(2, 3)     // 空
getSum(1, 2, 3)  // 3
getSum(1, 2, 3, 4, 5)  // [3,4,5]

展开运算符

展开运算符(...),将一个数组进行展开,展开运算符和剩余参数很相似,对象和数组也能用。

不会修改原数组,典型运用场景:求数组最大值(最小值)、合并数组等。

const arr = [1, 2, 3]
// 展开运算符 可以展开数组

// 1. 求数组的最大值和最小值
// ...arr === 1, 2, 3
console.log(...arr) // 1 2 3
console.log(Math.max(...arr)) // 3
console.log(Math.min(...arr)) // 1

// 2. 合并数组
const arr2 = [3, 4, 5]
const arr3 = [...arr, ...arr2]
console.log(arr3) // [1, 2, 3, 3, 4, 5]

剩余参数:函数参数使用,得到真数组;展开运算符:数组中使用,数组展开

箭头函数

学习目的:箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方

箭头函数属于表达式函数,因此不存在函数提升

箭头函数只有一个参数时可以省略圆括号()

箭头函数函数体只有一行代码时可以省略花括号{},并自动做为返回值被返回

加括号的函数体返回对象字面量表达式

基本语法:

// 箭头函数
// 声明变量 = 匿名函数{}
const fn = () => {
  console.log(123)
}
fn() // 123

// 等同
const fn = function fn() {
  console.log(123)
}
// 只有一个形参的时候,可以省略小括号
const fn = x => {
  console.log(x)
}
fun(1) // 1

// 只有一行代码的时候,我们可以省略{}据号
const fn = x => console.log(x)
funn(1) // 1
// 箭头函数可以省略 return
const fn = x => x + x
console.log(fn(1)) // 2

// 等同
const fn = x => {
  return x + x
}
console.log(fn(1))
// 箭头函数可以省略 return
const fn = x => x + x
console.log(fn(1)) // 2

// 等同
const fn = x => {
  return x + x
}
console.log(fn(1))
const form = document.querySelector('form')
// 简写的阻止默认行为
form.addEventListener('click', e => e.preventDefault())

如果函数体只有一行代码,可以写到一行上,并且无需写return直接返回值。

// 箭头函数可以直接返回一个对象
// 声明函数变量 = 实参 =>({属性名:属性值})
const fn = (uname) => ({ uname: uname })
console.log(fn('刘德华'))


// 等同于这个,放在一行的话,外层花括号换成小括号
const fn = (uname) => {
  // 一个对象
  return { uname: uname }
}
console.log(fn('刘德华')) 
// 还可以进一步省略:直接给属性值
const fn = (uname) => ({ uname })
console.log(fn('刘德华'))

加括号的函数体返回对象字面量表达式

箭头函数参数

  • 普通函数有arguments动态参数
  • 箭头函数没有arguments动态参数,但是有剩余参数..args
// 利用箭头函数去求和
const gerSum = (...arr) => {
  let sum = 0
  for (let i = 0; i < arr.length; i++) {
    sum = sum + arr[i]
  }
  return sum
}
const result = gerSum(2, 3)
console.log(result) // 5

箭头函数this

在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值,非常令人讨厌。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this

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

// 箭头函数的this 是上一层作用域的this指向
const fn = () => {
  console.log(this) // window
}
fn()

// 对象方法的箭头函数this
const obj = {
  uname: '霍欣标',
  sayHi: () => {
    console.log(this)  // window
  }
}
obj.sayHi()

// 对象方法的箭头函数套普通函数
const obj = {
  uname: '霍欣标',
  sayHi: function () {
    console.log(this) // obj
    let i = 10
    const count = () => {
      console.log(this)  // obj
    }
    count()
  }
}
obj.sayHi()
// 以前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.sayHi()  // 指向obj

解构赋值

解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值。

数组解构

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

  1. 赋值运算符=左侧的[]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
  2. 变量的顺序对应数组单元值的位置依次进行赋值操作
const [max, min, avg] = [100, 60, 80]
console.log(max) // 100

// const arr = [100, 60, 80]
// const [max, min, avg] = arr

基本语法:典型应用交互2个变量

两个以上的立即执行函数和数组开头的时候会报错,所以要加一个分号; 表示结束。

// 交换2个变量的值
let a = 1
let b = 2;     // 这里必须有分号
[b, a] = [a, b]
console.log(a, b) // 2 1
// 1. 立即执行函数要加分号
(function () { })();
(function () { })();

// 2. 使用数组的时加分号
// const arr = [1, 2, 3]
const str = '霍欣标';
// 遍历数组
[1, 2, 3].map(function (item) {
  console.log(item)
  // 1
  // 2
  // 3
})

练习:数组结构

// 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)
// 1. 变量多, 单元值少 , undefined
const [a, b, c, d] = [1, 2, 3]
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
console.log(d) // undefined

// 2. 变量少, 单元值多
const [a, b] = [1, 2, 3]
console.log(a) // 1
console.log(b) // 2

// 3.  剩余参数 变量少, 单元值多
const [a, b, ...c] = [1, 2, 3, 4]
console.log(a) // 1
console.log(b) // 2
console.log(c) // [3, 4]  真数组

// 4.  防止 undefined 传递
const [a = 0, b = 0] = [1, 2]
const [a = 0, b = 0] = []  // 返回的都是0
console.log(a) // 1
console.log(b) // 2

// 5.  按需导入赋值 
// 忽略归忽略,位置还要保留
const [a, b, , d] = [1, 2, 3, 4]
console.log(a) // 1
console.log(b) // 2
console.log(d) // 4

const arr = [1, 2, [3, 4]]
console.log(arr[0])  // 1
console.log(arr[1])  // 2
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

变量的数量大于单元值数量时,多余的变量将被赋值为undefined .
变量的数量小于单元值数量时,可以通过剩余参数...获取剩余单元值,但只能置于最末位.

对象解构

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

基本语法:

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

// 1. 解构对象
// 要求属性名和变量名必须一致
const { uname, age } = { uname: '霍欣标', age: 18 }
console.log(uname) // 霍欣标
console.log(age)  // 18

// 2. 对象解构的变量名 可以重新改名  旧变量名:新变量名
const { uname: username, age } = { uname: '霍欣标', age: 18 }
console.log(username) // 霍欣标
console.log(age)  // 18

数组对象解构

// 解构数组对象
const pig = [
  {
    uname: '霍欣标',
    age: 18
  }
]

const [{ uname: username, age }] = pig
console.log(username) // 霍欣标
console.log(age)  // 18

练习

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

// 1. 多级对象解构
const { uname, family: { mother, father, sister } } = pig
console.log(uname)
console.log(mother)
console.log(father)
console.log(sister)

// 2. 多级数组对象解构
const person = [
  {
    uname: '佩奇',
    family: {
      mother: '猪妈妈',
      father: '猪爸爸',
      sister: '乔治'
    },
    age: 6
  }
]
const [{ uname, family: { mother, father, sister } }] = person
console.log(uname)
console.log(mother)
console.log(father)
console.log(sister)
// 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选出当做参数传递给 函数
// const { data } = msg
// msg 虽然很多属性,但是我们利用解构只要 data值
function render({ data }) {
  // const { data } = arr
  // 我们只要 data 数据
  // 内部处理
  console.log(data)

}
render(msg)

// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({ data: myData }) {
  // 要求将 获取过来的 data数据 更名为 myData
  // 内部处理
  console.log(myData)
}
render(msg)

forEach遍历数组

forEach()方法用于调用数组的每个元素,并将元素传递给回调函数,主要遍历数组的每个元素。

参数当前数组元素是必须要写的,索引号不需要可以不写。

const arr = ['red', 'green', 'pink']
arr.forEach(function (item, index) {
  console.log(item)  // 数组元素
  console.log(index) // 索引号
  // red
  // 0
  // green
  // 1
  // pink
  // 2
})

// 不能返回数组,无法被赋值新变量
// const result = arr.forEach(function (item) { console.log(item) })
// console.log(result) // undefined

案例:渲染商品列表

思路:多少条数据,就渲染多少模块,然后生成对应的html结构标签,赋值给list标签即可 Demo

  1. 利用forEach遍历数据里面的数据
  2. 拿到数据利用字符串拼接生成结构添加到页面中
  3. 注意传递参数的时候,可以使用对象解构
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 { name, price, picture } = item
  str += `
    <div class="item">
    <img src="${picture}" alt="">
      <p class="name">${name}</p>
      <p class="price">${price}</p>
    </div>
  `
})
// 3. 生成的字符串 添加给 List
document.querySelector('.list').innerHTML = str

筛选数组filter方法

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

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

//返回的符合条件的新数组 箭头函数
const arr = [10, 20, 30]
const new_arr = arr.filter(item => item >= 20)
console.log(new_arr) // [20, 30]

// 等同下面
const new_arr = arr.filter(function (item, index) {
  // console.log(item)  // 数组元素
  // console.log(index) // 索引号
  return item >= 20
})
console.log(new_arr) // [20, 30]

css的tab栏切换

一点击处于活动状态,并且获得光标,改变的样式。

    .filter a:active,
    .filter a:focus {
      background: #05943c;
      color: #fff;
    }

综合案例:商品列表价格筛选

需求:Demo

  1. 渲染数据列表
  2. 根据选择不同条件显示不同商品
    • 2.1 点击采取事件委托方式.filter
    • 2.2 利用过滤函数filter筛选出符合条件的数据,生成的是一个数组,传递给渲染函数即可
    • 2.3 筛选条件是根据点击的data-index来判断
    • 2.4 可以使用对象解构,把事件对象解构
    • 2.5 因为全部区间不需要筛选,直接把goodList渲染即可
// 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',
  },
]

// 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>
    `
  });
  document.querySelector('.list').innerHTML = str
}
render(goodsList)  // 页面一打开就需要渲染


// 3. 筛选的效果
document.querySelector('.filter').addEventListener('click', e => {
  // e.target.dataset.id  // 事件对象的自定义属性
  const { tagName, dataset } = e.target
  // 判断
  // arr 返回的是新数组
  let arr = goodsList
  if (tagName === 'A') {
    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)
  }
})

原创文章,作者:霍欣标,如若转载,请注明出处:https://www.bigengwu.cn/xue/146.html

霍欣标的头像霍欣标
上一篇 2024-09-05
下一篇 2024-09-10

相关推荐

博主人懒,应管局要求暂不开启站内私信和评论功能,如需帮助请发邮件。

邮箱账号:1969600480@qq.com