【JS】JavaScript进阶 ES6 - 黑马视频笔记
1. 作用域
作用域(scope)规定了变量能够被访问的“范围”,离开这个范围变量便不能被访问。
分为:局部作用域、全局作用域。
1.1 局部作用域
局部作用域分为:函数作用域、块作用域。
1. 函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
函数的参数也是函数内部的局部变量。函数执行完毕,函数内部的变量实际被清空了。
2. 块作用域
在JavaScript中使用 { } 大括号包裹的代码称为代码块,代码块内部声明的变量,外部有可能无法被访问。
let 声明的变量会产生块级作用域, var 不会产生块级作用域。
const 声明的常量也会产生块作用域。
不同代码块之间的变量无法互相访问。
推荐使用 let 或 const 。
for 循环声明的变量,虽然在{ }外面,但是for是一个整体,默认变量都在{}里面。还有 if while 等等。
1.2 全局作用域
<script> 标签和 .js文件的最外层都是全局作用域。全局作用域中声明的变量,任何其他作用域都可以访问。
注意:
1.为 window对象动态添加的属性默认也是全局的,不推荐,比如 a=10,其实就是 window.a=10。
2.函数中未使用任何关键字声明的变量为全局变量,不推荐。
3.尽可能少声明全局变量,防止全局变量被污染。
1.3 作用域链 【重点】
作用域链 本质上是底层的变量查找机制。
可以理解为就近原则、冒泡。
在函数被执行时,会优先查找当前函数作用域中查找变量。如果找不到,则会依次逐级查找父级作用域直到全局作用域。
1. 嵌套关系的作用域串联起来形成了作用域链
2. 相同作用域链中按着从小到大的规则查找变量
3. 子作用域可以访问父作用域,但父级作用域无法访问子作用域。
1.4 垃圾回收机制
内存的生命周期
js中分配的内存,一般有如下生命周期:
1. 内存分配:声明变量、函数、对象时,系统自动分配
2. 内存使用:读写内存,也就是使用变量、函数等
3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
声明:
全局变量一般不会回收(关闭页面回收)
一般情况下局部变量的值,不用了,会被自动回收。
js垃圾回收机制-算法说明
引用计数。
定义“内存不再使用”,看一个对象是否有指向它的引用,没有引用就回收对象。
被引用一次,就计数+1,减少一个引用就-1,为0时,就释放内存。
标记清除法。
现代浏览器已经不再使用引用计数法了。
就是从根部(全局对象)出发,能到达就还是需要使用的,无法到达的就回收。
1.5 闭包
一个函数对周围状态的引用捆绑在一起,內层函数中访问到其外层函数的作用域。
简单理解:闭包 = 内层函数 + 外层函数的变量
function A() {
const a = 1
function B() {
console.log(a)
}
B()
}
A()
内层函数 function B() 和 变量a 与调用函数B() 属于闭包。
闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量。
闭包:调用外部函数的变量和参数的函数 组成闭包
好处:外部可以访问函数内部变量 实现变量私有化
缺点:造成内存泄漏 解决方法 手动释放内存 null
1.6 变量提升
它允许在变量声明之前被访问,仅存在于 var 声明变量。
在代码执行之前,把所有 var 声明的变量提升到当前作用域的最前面,只提升声明,不提升赋值。
1. 变量在未声明即被访问时会报错
2. 变量在 var 声明之前即被访问,变量的值为 undefined。
console.log(num)
var num = 10
// 提升前 ↑ 提升后 ↓
var num // 只提升变量,不提升赋值,所以值是 undefined
console.log(num)
var num = 10
2. 函数进阶
2.1 函数提升
函数提升与变量提升类似,是指函数在声明之前即可被调用。
会把所有函数声明提升到当前作用域的最前面。
fun()
function fun(){
console.log('函数提升,只提升函数声明,不提升函数调用')
}
2.2 函数参数 arguments ...
1. 动态参数 arguments
arguments 是函数内部内置的伪数组变量,包含了调用函数时传入的所有实参。
1. 是伪数组。只存在于函数中。
2. 作用是动态获取函数的实参。
3. 可以通过 for 循环依次得到传递过来的实参。
比如:不确定要计算多少个参数:
function getSum(){
let sum = 0
for(let i = 0; i < arguments.length; i++){
sum += arguments[i]
}
return sum
}
console.log( getSum(8,6,4,2) )
2. 剩余参数 ...other
将一个不定数量的参数表示为一个数组。
1. ...
是语法符号,置于最末函数形参之前,用于获取多余的实参。
2. 借助 ... 获取的剩余实参,是个真数组,可以使用数组的方法 pop push。
在剩余参数使用的时候,不需要拿...,只需要拿后面的参数名就行。
function getSum(...shuzi){
console.log(shuzi)
}
getSum(8,6,4,2) // 得到真数组 [8, 6, 4, 2]
function getSum(a,b,...shuzi){ // 前面的参数一一对应,剩下的都给 ...剩余参数
console.log(a) // 8
console.log(b) // 6
console.log(shuzi) // [4, 2]
}
getSum(8,6,4,2)
建议多使用 剩余参数。
※2. 展开运算符 ...
展开运算符将一个数组进行展开。
语法: ...
1. 不会修改原数组
使用场景:求最大值、合并数组。
const arr = [1,2,3,4,5]
console.log(...arr) // 1 2 3 4 5
console.log(Math.max(1,2,3,4,5)) // 5 求最大值
console.log(Math.max(...arr)) // 5 本质上 ...arr === 1,2,3,4,5 打印不显示,因为打印没意义
const arr2 = [9,9,9]
console.log(...arr,...arr2) // 1 2 3 4 5 9 9 9
const arrNew = [...arr,...arr2] // 合并数组
console.log(arrNew) // [1, 2, 3, 4, 5, 9, 9, 9]
2.3 箭头函数 【重点】
箭头函数的目的是更简短的函数写法,并且不绑定this,语法比函数表达式更简洁。
使用场景:箭头函数更适用于那些本来需要匿名函数的地方。
1. 基本语法
语法: () => { }
1. 箭头函数
// 普通函数
const fn1 = function() {
console.log('123')
}
// 箭头函数
const fn2 = () => {
console.log('123')
}
2. 箭头函数 带参数
如果只有一个参数,就可以省略小括号,无参数和一个以上参数不能省略括号!
// 2. 只有一个形参时,可以省略小括号
//const fn2 = (x) => {
const fn2 = x => {
console.log(x)
}
fn2(3333) // 3333
3. 只有一行代码时,可以写到一行上,可以省略大括号,无需写return直接返回值
// 3. 只有一行代码时,可以省略大括号,可以写到一行上,无需写return直接返回值
const fn3 = x => return x + x
fn3(3333) // 6666
4. 加括号的函数体返回对象字面量表达式
// 4. 箭头函数可以直接返回一个对象,用()括号包住,不然{ {xx:xx} }就报错
const fn4 = (uname) => ( {uname: uname} )
console.log( fn4('刘德华') )
// 简写前: 只有一行代码,可以写在一行上,省略{大括号}、省略 return直接返回值
// const fn4 = (uname) => {
// return {uname: uname}
// }
结论:
1. 箭头函数属于函数表达式,无函数提升
2. 箭头函数只有一个参数时可以省略 ( )括号
3. 箭头函数函数体只有一行函数可以省略花括号 { },并自动做为返回值被返回,无需写 return
4. 加括号的函数体返回对象字面量表达式
2. 箭头函数参数
箭头函数没有 arguments 动态参数,但是有剩余参数 ...args 。
const getSum = (...my_arr) => {
let sum = 0
for(let i = 0; i < my_arr.length; i++){
sum += my_arr[i]
}
return sum
}
console.log( getSum(8,6,4) )
3. 箭头函数 this
箭头函数不会创建自己的 this ,它只会从自己的作用域链的上一层沿用 this。
大白话就是:箭头函数的 this,是【它爹的 this】。就是上一层作用域的 this。
this:谁调用的这个函数,this 就指向谁。
DOM事件回调函数为了简便,想使用this的值,不太推荐使用箭头函数。
// 普通函数 this
function fn() {
console.log(this) // this 指向 window
}
fn() // window.fn()
// 箭头函数 this 是上一层作用域的 this
function fn() {
console.log(this) // this 指向 window
}
fn() // window.fn()
// 对象方法 this
const obj = {
name: 'anni',
sayHi: function() {
console.log(this) // this 指向 obj
}
}
obj.sayHi()
// 对象方法箭头函数 this
const obj = {
name: 'anni',
sayHi: () => {
console.log(this) // this 指向 window
}
}
obj.sayHi() // 箭头函数向上一层找,也就是obj的父级,所以this 是 window
3. 结构赋值 (数组解构、对象解构)
1.数组解构
数组解构是 将数组的单元值快速批量赋值给一系列变量的简洁语法。数组解构其实就是把数组里的元素赋值给变量。
基本语法:
1. 赋值运算符 = 左侧的 [ ] 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量。
2. 变量的顺序对应数组单元值的位置依次进行赋值操作。按顺序依次赋值。
原先的方式
const arr = [1,2,3]
console.log(arr[0])
console.log(arr[1])
console.log(arr[2])
const a = arr[0]
const b = arr[1]
const c = arr[2]
console.log(a)
.....
// 上面的要么不好记忆。要么书写麻烦,此时可以使用 解构赋值 的方法来让代码更简洁:
const [a,b,c] = [1,2,3] // 或 const [a,b,c] = arr
console.log(a) // 1
console.log(b) // 2
console.log(c) // 3
典型的语法:交换两个变量的值
注意:js 前面必须加上分号。一个是立即执行函数,二是数组解构。
const a = 1
const b = 2; // 必须加上分号,不然就和后面连一起了:const b = 2[b,a] = [a,b]
[b,a] = [a,b] // 如果上面不写分号,这条语句前面就得写,如: ;[b,a] = [a,b]
console.log(a,b) // 2 1
//-----------------
const b = 2
[b,a] = [a,b]
相当于:
const b = 2[b,a] = [a,b]
// 1. 立即执行函数
(function t() {}) ();
;(function t() {}) ()
// 2. 数组解构 特别是前面有语句的一定要加分号; 不然就连一起了
;[b,a] = [a,b]
一些附加情况:
1. 变量多 单元值少的情况
const [a,b,c] = [1,2]
console.log(a) // 1
console.log(b) // 2
console.log(d) // undefined
2. 变量少 单元值多的情况
const [a,b] = [1,2,3,4]
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] 真数组
console.log(c[0]) // 3
4. 防止 undefined 传递 赋值默认值
const [a = 0,b = 0] = [1]
console.log(a) // 1
console.log(b) // 0 默认值
5. 按需导入,忽略某些返回值
const [a,b, ,d] = [1,2,3,4]
console.log(a) // 1
console.log(b) // 2
console.log(d) // 4 // 因为b后面没有变量,3传不过来
6. 支持多维数组的结构
const [a,b,c] = [1,2,[30,40],3] // 二维数组
console.log(a) // 1
console.log(b) // 2
console.log(c) // [30,40]
console.log(c[0]) // 30
console.log(c[1]) // 40
console.log(d) // 50
2. 对象解构
基本语法:
1. 赋值运算符 = 左侧的 { } 用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量。
2. 对象属性的值将被赋值给与属性名相同的变量。因为对象赋值是无序的,所以,声明的变量名字必须与属性名一致。
3. 注意解构的变量名不要和外面的变量名冲突,否则报错
4. 对象中找不到与变量名一致的属性时变量值为 undefined
语法: { } = { }
const { uname,age } = {
uname: '猫',
age: 2
}
// 也就是:
const { uname,age } = { uname: '猫',age: 2 } //声明变量名 与 属性名 必须一致
// 等价于 cosnt uname = obj.uname
console.log(uname) // '猫'
1. 给新的变量名赋值:
语法:{ old_name: new_name }
,旧值:新值,“什么值: 赋值给谁”
当解构变量名与外面的变量名冲突时,可以重新命名。
const uname = '二哈'
const { uname:username ,age } = { uname: '猫',age: 2 } // 加冒号 :重新命名
console.log(username)
2. 解构数组对象
数组里面有多个对象。
在解构的时候,先看外层是什么,是什么包一层什么,是对象包对象{ },数组包数组。
先看最外层是什么,再看內层的,如:最外层是[ ]数组,內层是{ }对象,所以就是 [ { } ]
const car = [
{
name: '铃木',
age: 2
}
]
const [{name,age}] = car
console.log(name) //铃木
console.log(age) //2
3. 多级对象解构
遇到多级对象,需要加上对象名,冒号,大括号。
const car = {
name: '铃木',
data: {
torque: 24,
date: 2023
},
age: 2
}
const { name, data: {torque,date} } = car
console.log(name) //铃木
console.log(torque) //24
console.log(date) //2023
数组里的对象的对象:
const car = [
{
name: '铃木',
data: {
torque: 24,
date: 2023
},
age: 2
}
]
const [{ name, data: {torque,date} }] = car // [{ { } }]
console.log(name) //铃木
console.log(torque) //24
console.log(date) //2023
模拟处理AJAX数据:
// 1. 解构
const { name, data: { torque, date } } = car
console.log(name) //铃木
console.log(torque) //24 Nm
console.log(date) //2023年
// 2.上面 car 是后台传过来的,把里面的 data 当参数传给 函数
/*
function getCar(arr) {
const { data } = arr // 就是 const { data } = car
console.log(arr.torque)
}
getCar(car)
*/
// 这样写不简洁
// 由于 const { data } = car
// 因为 car 传给了 {data},然后 car 和 {data} 是一样的,传递参数的时候,直接做解构,写 {data}
function getCar( {data} ) {
console.log(data.torque) // 24 Nm
}
getCar(car)
3. 遍历数组 forEach() 方法【重点】
用于调用数组的每个元素,并将元素值传回给回调函数。 加强版的 for 循环。适合于遍历数组对象。
遍历数组的每个元素。只遍历,不返回值。与map的区别是这个不返回值。
语法: 被遍历的数组.forEach(function( 当前数组元素, 当前元素索引号 ) { // 函数体 }
const car = ['铃木','2023年']
car.forEach(function(item,index) {
console.log(item)
console.log(index)
})
案例:
forEach 渲染商品
const goodsList = [
{
id:'001',
name:'美食美食美食美食美食美食美食美食美食',
price:'1000',
picture:'../../../images/bg/top_img01.jpg'
},
{
id:'002',
name:'特产特产特产特产特产特产特产特产特产特产特产',
price:'1050',
picture:'../../../images/bg/top_img02.jpg'
}
]
// 1. 声明一个字符串变量
let str = ''
// 2. 遍历数组
goodsList.forEach( item => {
console.log(item) // 可以得到每一个数组元素 对象
const {name, price, picture} = item //解构赋值 对象
str += `
<div class="item">
<img src="${picture}" width="400px">
<p class="price">¥${price}</p>
<p class="name">${name}</p>
</div>
`
})
// 3. 生成的 字符串 添加给 list
document.querySelector(".list").innerHTML = str
4. 筛选数组 filter() 【重点】
类似于 map 。
filter () 方法创建一个新的数组,新数组中的元素是通过符合指定条件的所有数组元素。
用处:筛选数组符合条件的元素,并返回筛选后元素的新数组。
4. 深入对象
1. 创建对象的三种方式
1. 利用对象字面量创建对象
const obj = {
name: '名字啊'
}
2. 利用 new Object() 创建对象
const obj = new Object()
obj.name = '佩奇'
const obj2 = new Object( {name: '佩奇'} ) // 也可以这样写
3. 利用构造函数创建对象
自定义一个构造函数。
2. 构造函数
是一种特殊的函数,主要用于初始化/创建对象的。
可以批量生成,快速创建对象,把公共的属性都封装到一个函数里。
构造函数在技术上是常规函数,有两个约定:以大写字母开头命名、只能由 new 操作符来执行。
构造函数必须 new 一下,实例化才可以。
// 1. 创建构造函数 猪类
function Pig(uname,age){
this.uname = uname
this.age = age
}
// 2. new 关键字调用函数
const qz = new Pig('乔治',3)
console.log(qz) // {uname: '佩奇', age: 5}
console.log( new Pig('佩奇',5) ) // {uname: '佩奇', age: 5}
1. 使用 new 调用函数的都叫实例化
2. 没参数时可以省略 ()
3. 函数内部无需写return,返回值是新创建的对象
4. 构造函数内部的 return 返回的值无效,所以不要写 return
5. new Object() new Date() 也是实例化构造函数
实例化执行过程:
1. 创建新的空对象
2. 构造函数 this 指向新对象
3. 执行构造函数代码,修改 this ,添加新的属性
4. 返回新对象
3. 实例成员和静态成员
1. 为构造函数传入参数,创建结构相同,但值不相同
2. 构造函数创建的实例对象彼此独立互不影响
简单来说,实例成员,是new后实例对象里的。静态成员,是构造函数里的。
实例成员:实例属性、实例方法。也就是实例对象里的属性和方法
实例对象相互独立,实例成员在当前实例对象使用
// 1. 实例成员:实例对象 身上的属性和方法
function Pig(name) {
this.name = name
}
const pq = new Pig('佩奇')
pq.name = '小猪佩奇' //实例属性
pq.sayHi = () => { //实例方法
console.log('Hi~~')
}
静态成员:静态属性、静态方法。也就是构造函数里的的属性和方法
静态成员只能构造函数访问。
// 2. 静态成员:构造函数 身上的属性和方法
function Pig(uname, age) {
this.name = name
}
Pig.age = 2 //静态属性
Pig.sayHi = function() { //静态方法
console.log('Hi~')
}
console.log(Pig.age) // 2
Pig.sayHi() // Hi~
4. 内置构造函数
在 JavaScript 中最主要的数据类型有 6 种。
基本数据类型(简单数据类型):字符串、数值、布尔、undefine、null
引用数据类型(引用数据类型):对象(数组和函数都属于对象)
const str = '小猫'
// 相当于 ↓
const str = new String('小猫') // js底层完成 把简单数据类型包装为引用数据类型
如上面例子,js底层完成 把简单数据类型包装为引用数据类型
其实字符串、数值、布尔等都有专门的构造函数,这些称为包装类型。
js中几乎所有的数据都可以基于构造函数创建。
1. Object
Object 是内置的构造函数,用于创建普通对象。
推荐使用字面量方式声明对象,而不是 Object 构造函数。因为字面量更简便一些。
常用的静态方法(静态方法就是只有构造函数Object可以调用):
Object.keys :获取对象中所有的属性(键),返回一个数组。
Object.values :获取对象中所有的属性值(值),返回一个数组。
Object.assign :用于对象拷贝。经常用于给对象添加属性和值
语法:
// 1. 获得所有的属性名(键)
console.log(Object.keys(obj)) // ['name', 'color']
// 2. 获得所有的属性值(值)
console.log(Object.values(obj)) // ['黑猫', '黑色']
// 3. 对象拷贝
const obj2 = {}
Object.assign(obj2, obj)
console.log(obj2) // {name: '黑猫', color: '黑色'}
// 追加属性
Object.assign(obj2, { like: '小鱼干' })
console.log(obj2) // {name: '黑猫', color: '黑色', like: '小鱼干'}
2. Array
Array 是内置的构造函数,用于创建数组。
推荐使用字面量方式创建数组,而不是 Array 构造函数。因为字面量更简便一些。
forEach :遍历数组。不返回数组,用于查找遍历数组元素。
filter :过滤数组。返回新数组,返回筛选满足条件的数组元素。
map :迭代数组。返回新数组,返回处理后的数组元素。
from :将伪数组转换为真数组。【重点】
find :查找元素,返回符合条件的第一个数组元素值,并返回这个对象,无符合的就返回undefined。【重点】
every :检测数组所有元素是否都符合指定条件,都符合就返回true,否则false。【重点】
reduce :累计器。返回累计处理的结果,用于求和。
reduce 语法:arr.reduce(function(){}, 起始值)
、arr.reduce(function(上一次值, 当前值){}, 初始值)
如果有起始值,则把初始值累加到里面。
const arr = [1,5,8]
// 1. 没有初始值
const total = arr.reduce(function(previous, current) {
return previous + current
})
console.log(total) // 14
// 2. 有初始值
const total = arr.reduce(function(previous, current) {
return previous + current
}, 10)
console.log(total) // 24 10+14
// 3. 箭头函数的写法
const total = arr.reduce((previous, current) => previous + current, 10)
console.log(total) // 24 10+14
reduce 执行过程:
1. 如果没有起始值,则上一次值就是数组的第一个数组元素的值
2. 每次循环,把返回值给做为下一次循环的上一次值
3. 如果有起始值,则起始值做为上一次值
简单来说,
无初始值,【上一次值】是【数组第一个元素的值】,【当前值】是【数组第二个元素的值】,【返回值】就是它俩累加的。第二次循环,【上一次值】是【返回值】,【当前值】是【数组第三个元素】,【返回值】是它俩累加的。
有初始值,【上一次值】是【初始值】,【当前值】是【数组第一个元素的值】,【返回值】就是它俩累加的。第二次循环,【上一次值】是【返回值】,【当前值】是【数组第二个元素】,【返回值】是它俩累加的。几个数就循环几次。
reduce 计算工资案例
const arr = [
{
name:'牛牛',
wages: 1000
},{
name:'加菲猫',
wages: 1050
},{
name:'汤姆猫',
wages: 6543
}
]
// 计算工资总数
const total = arr.reduce((previous, current) => {
//console.log(previous) // 返回了第一个对象,不能用来相加,所以,必须有起始值 设置为0
//return previous + current //拿着对象累加是错误的,应该要它的属性值
return previous + current.wages // 对象.工资
}, 0)
console.log(total)
// 工资上涨到130%
const total_130 = arr.reduce((previous, current) => {
// 上一次值 + 每一个工资 * 1.3
return previous + current.wages * 1.3
}, 0)
console.log(total_130)
find 方法
const arr1 = ['red','pink','blue']
const arrFind = arr1.find( item => {
return item === 'blue'
})
console.log(arrFind) // blue
// 用途
const arr = [
{
name: '一加',
price: 2700
},{
name: '小米',
price: 2710
}
]
// 找“小米”这个对象,并且返回这个对象
const xiaoMi = arr.find( item => {
//console.log(item) //每一个对象
//console.log(item.name) //每一个对象的name属性值
return item.name === '小米'
})
console.log(xiaoMi) // {name: '小米', price: 2710}
every 与 some 语法:
every 全都符合条件、some 只要有一个符合条件就行。
// 2. every 都符合就返回true
const arr2 = [10,20,30,40]
const arr2Every = arr2.every( item => item >= 10) //条件是每个都>=10
console.log(arr2Every) // true
// 3. some 只要有一个符合就返回true
const arr2Some = arr2.some( item => item >= 30) //条件是有一个>=10就行
console.log(arr2Some) // true
3. String
// 1. split() 把字符串 转换为 数组,和 join() 相反
const str1 = 'pink,red,blue'
const arr1 = str1.split(',') // 分隔符
console.log(arr1)
// 2. substring(开始的索引号 [,结束的索引号]) 截取字符串。少用substr()
// 如果省略结束的索引号,默认读取到最后
// 结束的索引号不包含想要截取的部分
const str2 = '那个地方有黑猫,旁边还有加菲猫'
console.log(str2.substring(5,7)) //黑猫 5-7但不包含7
// 3. startsWith(检测字符串 [,检测位置索引号]) 检测是否以某个字符串开头
const str3 = '加菲猫a在我旁边,黑猫在外面'
console.log( str3.startsWith('加菲猫a')) // true
console.log( str3.startsWith('加菲猫A')) // false
console.log( str3.startsWith('黑猫',8)) // true
// 4. includes(检测字符串 [,检测位置索引号]) 只要字符串包含指定内容,就返回true,否则false
const str4 = '加菲猫a在我旁边,黑猫在外面'
console.log(str4.includes('加菲猫')) //true
console.log(str4.includes('旁边,黑猫')) //true
console.log(str4.includes('我旁边',2)) //true
console.log(str4.includes('白猫')) //false
console.log(str4.includes('猫A在')) //false
显示赠品(split 把字符串转换为数组,map 再遍历,join 再转换为字符串)
显示赠品(把字符串转换为数组,再遍历,再转换为字符串)
// 案例:显示赠品
const case1 = '新版标准外语,五十音教程,PDF资料'
// 转换为数组 再遍历数组
const case1str = case1.split(',').map( item => {
return `<span>【赠品】${item}</span>`
}).join('</br>') // join把数组转成字符串 br换行
console.log(case1str) //<span>【赠品】新版标准外语</span></br><span>...
// 渲染到页面div当中
document.querySelector('div').innerHTML = case1str
4. Number
Number 是内置的构造函数,用于创建数值
常用方法:
toFixed() :设置保留小数位的长度,四舍五入
const n = 12.345
// 保留两位小数 四舍五入
console.log(n.toFixed(2)) // 12.35
const n2 = 10
console.log(n2.toFixed(2)) // 10.00
案例:购物车
案例:购物车
<div class="list">
<!-- <div class="item">
<img src="/images/bg/top_img01.jpg" alt="图片">
<p class="name">鸡排丼饭鸡排丼饭鸡排丼饭鸡排丼饭鸡排丼饭
<span class="tag">【赠品】xxx</span>
<span class="tag">【赠品】xxx</span>
</p>
<p class="spec">原味</p>
<p class="price">1500.00</p>
<p class="count">2</p>
<p class="sub-total">3000.00</p>
</div> -->
</div>
<div class="total">
<p class="total-title">合计:</p>
<p class="total-yuan">3000.00</p>
</div>
<script>
const goodsList = [
{
id: '50001001',
name: '鹿児島県産黒毛和牛100%小田牛のハンバーグステーキ',
price: 600.00,
picture: '/images/bg/top_img01.jpg',
count: 1,
spec: '120g'
},{
id: '50001002',
name: '桂花拉麺 4食 麻辣が食欲をそそります、麻辣が絶妙なハーモニーに',
price: 1596.00,
picture: '/images/bg/top_img02.jpg',
count: 4,
spec: '麻辣',
gift: '新鲜鸡蛋*4个,辣椒*1瓶'
},{
id: '50001001',
name: '鹿児島県産黒毛和牛100%小田牛のハンバーグステーキ',
price: 500.5,
picture: '/images/bg/top_img03.jpg',
count: 5,
spec: '120g'
},{
id: '50001002',
name: '桂花拉麺 4食 麻辣が食欲をそそります、麻辣が絶妙なハーモニーに',
price: 1596.00,
picture: '/images/bg/top_img04.jpg',
count: 3,
spec: '原味',
gift: '新鲜鸡蛋*4个'
},
]
// 1. 遍历数组数据,渲染页面
document.querySelector('.list').innerHTML = goodsList.map( item => {
console.log(item) //每一条对象
// 更换数据 图片文字价格等 采取对象解构方式 单价保留2位小数
// 对象解构 item.name item.price ....
const {name,price,picture,count,spec,gift} = item
// 处理 赠品 模块
// 声明变量接收赠品数据,首先判断有没有赠品,有就处理数据,没有就返回空格''
const giftStr = gift ? gift.split(',').map( item => {
return `<span class="tag">【赠品】${item}</span>`
}).join('') : ''
// 计算小计模块 单价*数量
// 小数精度问题 0.1+0.2=0.30004 ,因为保留2位小数,所以乘以100,最后除以00
const subTotal = ((price * 100 * count) / 100 ).toFixed(2)
// 渲染页面
return `
<div class="item">
<img src="${picture}" alt="图片">
<p class="name">${name} ${giftStr}</p>
<p class="spec">${spec}</p>
<p class="price">${price.toFixed(2)}</p>
<p class="count">${count}</p>
<p class="sub-total">${subTotal}</p>
</div>
`
}).join('')
// 2. 合计模块
const total = goodsList.reduce( (prev,item) => {
return prev + (item.price * 100 * item.count) / 100
}, 0)
console.log(total)
document.querySelector('div.total .total-yuan').innerHTML = total.toFixed(2)
</script>