作用域
局部作用域
- 作用域规定了变量能够被访问的范围,离开了这个范围变量就不能被访问
- 作用域分为:局部作用域和全局作用域
局部作用域分为函数作用域和块作用域
1.函数作用域:
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
总结:
- 函数内部声明的变量,在函数外部无法直接访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
2.块作用域:
在JavaScript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问
- let声明的变量会产生块级作用域,var不会产生块作用域
- const声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用let或const
全局作用域
<script>标签和.js文件的最外层就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问
全局作用域中声明的变量,任何其他作用域都可以被访问
注意:
- 为window对象动态添加的属性默认也是全局的,不推荐!
- 函数中未使用任何关键字声明的变量为全局变量,不推荐!
- 尽可能少的声明全局变量,防止全局变量被污染
作用域链
作用域链本质上是底层的变量查找机制
在函数被执行时,会优先查找当前函数作用域中查找变量
如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
总结:
- 嵌套关系的作用域串联起来形成了作用域链
- 相同作用域链中按照从小到大的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域
JS垃圾回收机制
- 垃圾回收机制(Garbage Collection)简称GC
- JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收
JS环境中分配的内存,一般有如下生命周期:
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
- 内存使用:即读写内存,也就是使用变量,函数等
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
说明:
- 全局变量一般不会回收(关闭页面回收)
- 一般情况下局部变量的值,不用了,会被自动回收掉
- 内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
JS垃圾回收机制算法说明
堆栈空间分配区别:
1.栈(操作系统):由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈里面。
2.堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,复杂数据类型放到堆里面
引用计数
IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象·
算法:
- 跟踪记录被引用的次数
- 如果引用了一次,那么就记录次数1,多次引用会累加++
- 如果减少一个引用就减1 --
- 如果引用次数是0,则释放内存
function fn(){
let o1={}
let o2={}
o1.a=o2
o2.a=o1
return '引用计数无法回收'
}
但引用计数存在一个致命的问题:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已不再使用,垃圾回收机制不会进行回收,导致内存泄漏
因为他们的引用次数永远不会为0,这样的相互引用如果说很大量的存在就会导致大量的内存泄漏
标记清除法:
现代的浏览器已经不再使用引用计数算法了
现代浏览器通用的大多是基于标记清除法算法的某些改进算法,总体思想都是一致的。
核心:
- 标记清除算法将“不再使用的对象”定义为“无法达到的对象”
- 就是从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的
- 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
闭包
概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到外层函数的作用域
简单理解:闭包=内层函数+外层函数的变量(里层函数用到了外层函数的变量,二者缺一不可)
闭包作用:封闭数据,实现数据私有,外部也可以访问函数内部的变量,它允许将函数与其所操作的某些数据(环境)关联起来
闭包的基本格式:
// 简单的闭包形式
// function outer(){
// const a=1
// function f(){
// console.log(a)
// }
// f()
// }
// outer()
// 常见的闭包样式 外部可以访问函数内部的变量
function outer(){
let a=10
function fn(){
console.log(a)
}
return fn
}
// outer() ===fn ===function fn(){}
// const fun=function fn(){}
const fun=outer()
fun()//调用函数
闭包应用: 实现数据的私有
比如,做个统计函数调用次数,函数调用一次,就++
let i=0
function fn(){
i++
console.log(`函数被调用了${i}次`)
}
此处的i是全局变量,很容易遭到篡改
// 闭包形式 统计函数调用的次数
function count(){
let i=0
function fn(){
i++
console.log(i)
}
return fn
}
const fun=count()
这样就实现了数据私有,无法直接修改i,且文件不关闭,i不会被回收,因为fun为全局变量,fun会一直调用函数,故虽然i为局部变量,但调用完之后不会立刻销毁,由于fn使得i这个该回收的变量并未回收,这也是内存泄漏现象
变量提升
变量提升是JavaScript中比较”奇怪“的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)
流程:先把var变量提升到当前作用域最前面,只提升变量,不提升赋值,然后依次执行代码
注意:
- 变量在未声明即被访问时会报语法错误
- 变量在var声明之前即被访问,变量的值为undefined
- let/const声明的变量不存在变量提升
- 变量提升出现在相同作用域中
- 实际开发中推荐先声明再访问变量
// 1.把所有var声明的变量提升到 当前作用域的最前面
// 2.只提升声明 不提升赋值
// var num
// console.log(num+'件')//undefined件
// var num=10
function fn(){
console.log(num)//undefined
var num=10
}
fn()
函数进阶
函数提升
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
// 1.会把所有函数声明提升到当前作用域的最前面
//2.只提升函数声明 不提升函数调用
fn()
function fn(){
console.log('函数提升')
}
//错误
fun()//var变量提升 但赋值不会提升
var fun=function(){//相当于给变量赋值
console.log('函数表达式')
}//函数表达式必须先生命和赋值 后调用 否则 报错
总结:
- 函数提升能够使函数的声明更灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
函数参数
动态参数
例如:写一个求和函数
不管用户传入几个实参,都要把和求出来
function getSum(){
//arguments 动态参数 只存在于函数里面
//1.是伪数组
// console.log(arguments)
let sum=0
for(let i=0;i<arguments.length;i++){
sum+=arguments[i]
}
console.log(sum)
}
getSum(2,3,4)
getSum(1,2,3,4,2,2,3,4)
剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组
- ...是语法符号,置于最末函数形参之前,用于获取多余的实参
- 借助...获取的剩余实参,是个真数组
function getSum(a,b,...arr){//用户至少输入两个数据
console.log(arr)
}
getSum(2,3)//2传给a,3传给b
getSum(1,2,3)//3传给arr,1,2分别传给a,b
提倡多使用剩余参数
展开运算符
展开运动符(...),将一个数组进行展开
const arr=[1,2,3]
console.log(...arr)
说明:
不会修改原数组
典型运用场景(作用):求数组最大值(最小值)、合并数组等
// 1.求数组最大值
// ...arr===1,2,3
console.log(Math.max(...arr))
// 2.合并数组
const arr2=[3,4,5]
const arr3=[...arr,...arr2]
console.log(arr3)
展开运算符和剩余参数区别
剩余参数:函数参数使用,得到真数组
展开运算符:数组中展开,数组展开
箭头参数
目的:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
使用场景:箭头函数更适用于那些本来需要匿名函数的地方
基本语法
//1.箭头函数
// const fn=()=>{
// console.log(123)
// }
// 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)
// const fn=x=>{
// return x+x
// }
// 4.只有一行代码的时候 可以省略return
const fn=x=>x+x
console.log(fn(1))//2
// 5.箭头函数可以直接返回一个对象
const fn=()=>({name:uname})//加小括号区分对象与函数的大括号
console.log(fn('刘德华'))
- 箭头函数属于表达式函数,因此不存在函数提升
- 箭头函数只有一个参数时可以省略圆括号()
- 箭头函数函数体只有一行代码时可以省略花括号{},并自动作为返回值被返回
- 加括号的函数体返回对象字面量表达式
箭头函数的参数
1.普通函数有arguments动态参数
2.箭头函数没有arguments动态参数,但是有剩余参数...args
// 1.利用箭头函数来求和
const getSum=(...arr)=>{
let sum=0
for(let i=0;i<arr.length;i++){
sum+=arr[i]
}
return sum
}
const result=getSum(2,3,4)
console.log(result)
箭头函数this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
//1.以前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()
// 2.箭头函数的this 是上一次作用域的this 指向
const fn=()=>{
console.log(this)//window
}
fn()
// 对象方法的箭头函数 this
// const obj={
// uname:'pink老师',
// sayHi:()=>{
// console.log(this)//window
// }
// }
const obj={
uname:'pink老师',
sayHi:function(){
let i=10
const count=()=>{
console.log(this)//obj
}
count()
}
}
obj.sayHi()
在开发中怕【使用箭头函数前需要考虑函数中this的值】,事件回调函数使用箭头函数时,this为全局的window,因此DOM事件回调函数为了简便,还是不太推荐使用箭头函数
解构赋值
数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法
基本语法:
- 赋值运算符=左侧的[]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
- 变量的顺序对应数组单元值的位置依次进行赋值操作
const arr=[100,60,80]
//数组解构赋值
// const max=arr[0]
// const min=arr[1]
// const avg=arr[2]
// const [max,min,avg]=arr
const [max,min,avg]=[100,60,80]
console.log(max)//100
console.log(min)//60
console.log(avg)//80
交换两个变量案例
let a=1
let b=2;//注意加分号
[b,a]=[a,b]
console.log(a,b)
js前面必须加分号情况
1.立即执行函数
(function t(){})();
//或者
;(function t(){})()
2.数组解构
// 数组开头的 特别是前面有语句的一定注意加分号
;[b,a]=[a,b]
const arr=[1,2,3]
const str='pink';
[1,2,3].map(function(item){
console.log(item)
})
独立完成数组解构赋值
需求1:有个数组:const pc=['海尔','联想','小米','方正']
解构为变量:hr,lx,mi,fz
需求2:请将最大值和最小值函数返回解构max和min两个变量
变量多 单元值少的情况:
const[a,b,c,d]=[1,2,3]
console.log(a)//1
console.log(b)//2
console.log(c)//3
console.log(d)//undefined
利用剩余参数解决变量少 单元值多的情况:
const [a,b,...c]=[1,2,3,4]
console.log(a)//1
console.log(b)//2
console.log(c)//[3,4]
防止undefined传递单元值的情况 可以设置默认值
const[a=0,b=0]=[1,2]
console.log(a)//1
console.log(b)//2
按需导入赋值
const [a,b, ,d]=[1,2,3,4]
console.log(a)//1
console.log(b)//2
console.log(d)//4
支持多维数组的解构:
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
对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
基本语法:
- 赋值运算符=左侧的{}用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
- 对象属性的值将被赋值给与属性名相同的变量
- 注意解构的变量名不要和外面的变量名冲突否则报错
- 对象中找不到与变量名一致的属性时变量值为undefined
//对象解构
const obj={
uname:'pink老师',
age:18
}
// 解构的语法
// const uname=' red老师'
// const {uname,age}={uname:'pink老师',age:18}
// //等价于 const uname=obj.uname
// //要求属性名和变量名必须一致才可以
// console.log(uname)
// console.log(age)
// 1.对象解构的变量名 可以重新改名 旧变量名:新变量名
// const {uname:username,age}={uname:'pink老师',age:18}
// console.log(username)
// console.log(age)
// 2.解构数组对象
const pig=[{
uname:'佩奇'
,age:6
}]
const [{uname,age}]=pig
console.log(uname)
console.log(age)
多级对象解构:
const pig={
name:'佩奇',
family:{
mother:'猪妈妈',
father:'猪爸爸',
sister:'乔治'
},
age:6
}
// 多级对象解构
// const {name,family:{mother,father,sister}}=pig
// console.log(name)
// console.log(mother)
// console.log(father)
// console.log(sister)
const person=[{
name:'佩奇',
family:{
mother:'猪妈妈',
father:'猪爸爸',
sister:'乔治'
},
age:6
}]
const [{name,family:{mother,father,sister}}]=person
console.log(name)
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
// function render({data}) {
// // 我们只要 data 数据
// // const {data}=arr
// // 内部处理
// console.log(data)
// }
// render(msg)//大对象 传参的时候解构data
// 需求3, 为了防止msg里面的data名字混淆,要求渲染函数里面的数据名改为 myData
function render({data:mydata}) {
// 要求将 获取过来的 data数据 更名为 myData
// 内部处理
console.log(mydata)
}
render(msg)
遍历数组forEach方法(重点)
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数
// 被遍历的数组.forEach(function(当前数组元素,当前元素索引号){
// //函数体
// })
//forEach就是遍历 加强版的for循环 适合于遍历数组对象
const arr=['red','green','pink']
const result=arr.forEach(function(item,index){//index可省 item 不可省
console.log(item)//数组元素 red ...
console.log(index)//索引号
})
console.log(result)//undefined
map返回数组,forEach只遍历不返回
主要使用场景:遍历数组的每个元素
注意:
- forEach主要是遍历数组
- 参数当前数组元素是必须要写的,索引号可选
渲染商品列表
核心思路:有多少条数据,就渲染多少模块,然后生成对应的html结构标签,赋值给list标签即可
- 利用forEach遍历数据里面的数据
- 拿到数据,利用字符串拼接生成结构添加到页面当中
- 注意:传递参数的时候,可以使用对象解构
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
综合案例
筛选数组filter方法(重点)
filter()方法创建一个新的数组,新数组中的元素通过检查指定数组中符合条件的所有元素
主要使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组
const arr=[10,20,30] const newArr=arr.filter(function(item,index){ // console.log(item) // console.log(index) return item>=20 }) console.log(newArr)
返回值:返回数组,包含了符合条件的所有元素,如果没有符合条件的元素则返回空数组
参数:currentValue必须写,index可选
因为返回新数组,所以不会影响原数组
商品列表价格筛选
需求:
1.渲染数据列表
- 渲染页面 利用forEach遍历数组里面的数据,并渲染数据列表
- 根据filter选择不同条件显示不同商品
2.根据选择不同条件显示不同商品
- 点击采取事件委托方式 .filter
- 利用过滤函数filter筛选出符合条件的数据,因为生成的是一个新数组,传递给渲染函数即可
- 筛选条件是根据点击的data-index来判断
- 可以使用对象解构,把事件对象解构
- 因为全部区间不需要筛选,直接把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: '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)//页面一打开就需要渲染
document.querySelector('.filter').addEventListener('click',e=>{
// e.target.dataset.index
const {tagName,dataset}=e.target
if(tagName==='A'){
let arr=goodsList//arr返回的新数组
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)
}
})
深入对象
创建对象的三种方式
1.利用对象字面量创建对象
const o={
name:'佩奇'
}
2.利用new Object创建对象
// const obj=new Object()
// obj.uname='pink老师'
const obj=new Object({uname:'pink'})
console.log(obj)
3.利用构造函数创建对象
构造函数
构造函数:是一种特殊的函数,主要用来初始化对象
使用场景:常规的{...}语法允许创建一个对象,比如我们创建了佩奇的对象,继续创建乔治的对象还需要重新写一遍,此时可以通过构造函数来快速创建多个类似的对象。
构造函数在技术上是常规函数。
但有两个约定:
- 他们的命名以大写字母开头
- 他们只能由"new"操作符来执行
构造函数语法:大写字母开头的函数
创建构造函数:
说明:
- 使用new关键字调用函数的行为被称为实例化
- 实例化构造函数时没有参数时可省略()
- 构造函数内部无需写return,返回值即为新创建的对象
- 构造函数内部的return返回的值无效,所以不要写return
- new Objiect() new Date()也是实例化构造函数
function Pig(uname,age){
this.uname=uname
this.age=age
}
// console.log(new Pig('佩奇',6))
const p=new Pig('佩奇',6)
console.log(p)
利用构造函数创建多个对象
需求:
- 写一个Goods构造函数
- 里面包含属性uname 商品名称 price价格 count 库存数量
- 实例化多个商品对象,并打印控制台
function Goods(name,price,count){
this.name=name
this.price=price
this.count=count
}
const mi=new Goods('小米',1999,20)
console.log(mi)
const hw=new Goods('华为',3999,59)
console.log(hw)
const date=new Date('2022-4-8')
实例化执行过程
说明:
- 创建新空对象
- 构造函数this指向新对象
- 执行构造函数代码,修改this,添加新的属性
- 返回新对象
实例成员&静态成员
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)
说明:
1.为构造函数传入参数,创建结构相同但值不同的对象
2,构造函数创建的实例对象彼此独立互不影响
// 1.实例成员:实例对象上的属性和方法属于实例成员
function Pig(name){
this.name=name
}
const peiqi=new Pig('佩奇')
const qiaozhi=new Pig('乔治')
peiqi.name='小猪佩奇'//实例属性
peiqi.sayHi=()=>{//实例方法
console.log('hi~~~')
}
console.log(peiqi)//name 以及sayhi方法
console.log(qiaozhi)
console.log(peiqi===qiaozhi)//false
静态成员:构造函数的属性和方法被称为静态成员(静态属性和静态方法)
说明:
1.静态成员只能构造函数来访问
2.静态方法中的this指向构造函数
比如:Date.now() Math.PI Math.random()
// 静态成员
function Pig(name){
this.name=name
}
Pig.eyes=2//静态属性
Pig.sayHi=function(){ //静态方法
console.log(this)
}
Pig.sayHi()
console.log(Pig.eyes)//2
内置构造函数
在JavaScript中最主要的数据类型有6种:
基本数据类型:
字符串,数值,布尔,undefined,null
引用类型:
对象
但是我们发现基本数据类型也有方法,比如求字符串的长度,将数值保留两位小数等
其实字符串,数值,布尔,等基本类型也都有专门的构造函数,这些我们成为包装类型。
JS中几乎所有的数据都可以基于构成函数创建
JS提供的引用数据类型:Object Array RegExp(正则表达式),Date等
包装类型:String,Number,Boolean
Object
内置的构造函数,用于创建普通对象
推荐使用字面量方式声明对象,而不是Object构造函数
const user=new Object({name:'小明',age:15})
三个常用的静态方法(静态方法就是只有构造函数Object可以调用的)
作用:
- Object.keys 静态方法获取对象中所有属性(键)
- Object.values 静态方法获取对象中所有属性值
- Object.assign静态方法常用于对象拷贝,经常使用的场景是给对象添加属性
语法:
const o={name:'佩奇',age:6}
// 获取对象的所有键(属性名) 并且返回是一个数组
const arr=Object.keys(o)
console.log(arr)//['name','age']
Array
内置的构造函数,用于创建数组
创建数组建议使用字面量创建,不用Array构造函数创建
1.数组常见实例方法-核心方法
方法 | 作用 | 说明 |
---|---|---|
forEach | 遍历数组 | 不返回新数组,经常用于查找遍历数组元素 |
filter | 过滤数组 | 返回新数组,返回的是筛选满足条件的数组元素 |
map | 迭代数组 | 返回新数组,返回的是处理之后的数组元素,想要使用返回的新数组 |
reduce | 累计器 | 返回累计处理的结果,经常用于求和等 |
reduce作用:返回累计处理的结果,经常用于求和等
基本语法:
// 数组reduce方法
// arr.reduce(function(上一次值,当前值){},初始值)
const arr=[1,5,8]
//1.没有初始值
// const total=arr.reduce(function(prev,current){
// return prev+current
// })
// console.log(total)//14
//2.有初始值
const total=arr.reduce(function(prev,current){
return prev+current
},10)
console.log(total)//24
// 3.箭头函数的写法
const total1=arr.reduce((prev,current)=>prev+current,10)
console.log(total1)
参数:如果有起始值,则把初始值累加到里面
reduce执行过程:
1.如果没有起始值,则上一次值为数组的第一个数组元素的值
2.每一次循环,把返回值给作为下一次循环的上一次值
3.如果有起始值,则起始值做为上一次值
计算薪资案例
需求:
根据数据计算当月工资
const arr=[{
name:'张三',
salary:10000
},{
name:'里斯',
salary:10000
},{
name:'王五',
salary:10000
}
]
//计算薪资案例
// const total=arr.reduce((pre,current)=>{
// return prev+current.salary
// },0)//一定要将初始值设为0
// console.log(total)
// 需求:每个人涨薪30% 当月支出多少薪资
const total=arr.reduce((prev,current)=>prev+current.salary*1.3,0)
console.log(total)
数组的常见方法-其他方法
- 实例方法:join数组元素拼接为字符串,返回字符串(重点)
- 实例方法:find 查找元素,返回符合测试条件的第一个数组元素值,如果没有符合条件的则返回undefined(重点)
- 实例方法:every 检测数组所有元素是否都符合指定条件,如果所有元素都通过检测返回true,否则返回false(重点)
- 实例方法:some 检测数组中的元素是否满足指定条件 如果数组中有元素满足条件返回true,否则返回false
- 实例方法:concat 合并两个数组,返回生成新数组
- 实例方法:sort 对原数组单元值排序
- 实例方法:splice 删除或者替换原数组单元
- 实例方法:reverse 反转数组
- 实例方法:findIndex查找元素的索引值
// find方法
// const arr=['red','blue','green']
// const re=arr.find(function(item){
// return item==='blue'
// })
// console.log(re)
// const arr=[
// {
// name:'小米',
// price:1999
// },
// {
// name:'华为',
// price:3999
// }
// ]
// const mi=arr.find(function(item){
// return item.name==='小米'
// })
// const mi=arr.find(item=>item.name==='小米')
// console.log(mi)//返回有name为小米的数组对象
// every 每一个是否都符合条件,如果都符合返回true,否则返回false
//some只要有一个符合条件就返回true,否则返回false
const arr=[10,20,30]
const flag=arr1.every(item=>item>=10)
console.log(flag)//true
请完成以下需求
const spec={size:'40cm*40cm',color:'黑色'}
请将size和color里面的值拼接为字符串之后,写到div标签里面,展示内容如下:
40cm*40cm/黑色
思路:获得所有的属性值,然后拼接字符串就可以了
1.获得所有属性值是:Object.values() 返回的是数组
2.拼接数组是join('')这样就可以转换为字符串了
const spec={size:'40cm*40cm',color:'黑色'}
// 1.所有的属性值获取过来
// console.log(Object.values(spec))
// 2.转化为字符串
document.querySelector('div').innerHTML=Object.values(spec).join('/')
数组常见方法-伪数组转换为真数组
静态方法 Array.from()
const lis=document.querySelector('ul li')
// console.log(lis)
// lis.pop()
const liss=Array.from(lis)//把伪数组转换为真数组
liss.pop()
console.log(liss)
String
常见实例方法
1.实例属性 length 用来获取字符串的长度(重点)
2.实例方法 split('分隔符')用来将字符串拆分成数组(重点)
3.实例方法 substring(需要截取的第一个字符的索引[,结束的索引号])用于字符串截取(重点)
4.实例方法 startsWith(检测字符串[,检测位置索引号])检测是否以某字符开头,返回true或者false(重点)
5.实例方法 includes(搜索的字符串[,检测位置索引号]) 判断一个字符串是否包含在另一个字符串中,根据情况返回true和false(重点)
6.实例方法 toUpperCase 用于将字母转成大写
7.实例方法 toLowerCase 用于将字母转成小写
8.实例方法 indexOf 检测是否包含某字符
9.实例方法 endsWith 检测是否以某字符结尾
10.实例方法 replace用于替换字符串,支持正则匹配
11.实例方法 match 用于查找字符串 支持正则匹配
// 将字符串转化为数组
// const str='pink,red'
// const arr=str.split(',')
// console.log(arr)
// 字符串的截取 substring(开始的索引号[,结束的索引号])
// 如果省略了结束的索引号 默认渠取到最后
// const str='今天又要做核酸了'
// console.log(str.substring(5,7))//不包括7 核酸
// 3.startWith 判断是不是以某个字符开头
// const str='pink老师上课中'
// console.log(str.startsWith('ink',1))//true 从索引号为1开始 看ink是否从索引号1开始开头
// console.log(str.startsWith('pink'))//true 以pink开头
// 4. includes 判断某个字符是不是包含在一个字符串里面
// const str='我是pink'
// console.log(str.includes('pink'))//true
显示赠品案例
请将下面字符串渲染到准备好的p标签内部,结构如下:
const gift='50g茶叶,清洗球'
<p class="name">
<span class="tag">【赠品】50g茶叶</span>
<span class="tag">【赠品】清洗球</span>
</p>
思路:
1.把字符串拆分为数组,这样两个赠品就分开了,split(',')
2.利用map遍历数组,同时把数组元素生成到span里面,并且返回数组
3.因为返回的是数组,所以需要转换为字符串,join('')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div></div>
<script>
const gift='50g茶叶,清洗球'
// 1.把字符串拆分为数组
console.log(gift.split(','))
// 2.根据数组元素的个数,生成对应span标签
const str=gift.split(',').map(function(item){
return `<span>【赠品】${item}</span>`
}).join('')
console.log(str)
const div=document.querySelector('div')
div.innerHTML=str
//简写
// document.querySelector('div').innerHTML=gift.split(',').map(item=>`<span>【赠品】${item}</span>`).join('')
</script>
</body>
</html>
Number
内置的构造函数,用于创建数值
常用方法:
toFixed()设置保留小数位的长度
// toFixed方法可以使小数保留相对应的小数位数
const num=10.123
console.log(num.toFixed())//10 toFixed里面什么都没写即为整数的四舍五入
console.log(num.toFixed(2))//10.12 四舍五入两位小数
综合案例
购物车展示
- 渲染图片,标题,颜色,价格,赠品等数据
- 单价和小计模块
- 总价模块
分析业务模块:
- 把整体的结构直接生成遍历然后渲染到大盒子.list里面
- 遍历数组的同时还有返回值 map方法
- 最后计算总价模块,reduce对数组累加求和
1.先利用map来遍历,有多少条数据,渲染多少相同商品
- 可以先写死的数据
- 注意map返回值是数组,我们需要用join转换为字符串
- 把返回的字符串赋值给list大盒子的innerHTML
2.更换数据
- 先更换不需要处理的数据,图片,商品名称,单价,数量
- 采取对象解构的方式
- 注意 单价要保留2位小数, 489.00 toFixed(2)
更换数据-处理规格文字模块
- 获取每个对象里面的spec,上面对象解构添加spec
- 获取所有属性值是:Object.values() 返回的是数组
- 拼接数组是join('') 这样就可以转换为字符串了
更换数据-处理赠品模块
获取每个对象里的gift,上面对象解构添加gift
思路:
- 把字符拆分为数组,这样两个赠品就拆分开了。split(',')
- 利用map遍历数组,同时把数组元素生成到span里面,并且返回
- 因为返回的是数组,所以需要转换为字符串。join('')
更换数据-处理小计模块
- 小计=单价*数量
- 小计名可为:subTotal=price*count
- 注意保留两位小数
注意要判断是否有gift属性,没有的话不需要渲染
利用变成的字符串然后写到p.name里面
计算合计模块
- 求和用到数组reduce方法 累加器
- 根据数组里面的数量和单价累加和即可
- 注意 reduce方法有2个参数,第一个是回调函数,第二个是初始值,这里写0
<!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>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.list {
width: 990px;
margin: 100px auto 0;
}
.item {
padding: 15px;
transition: all .5s;
display: flex;
border-top: 1px solid #e4e4e4;
}
.item:nth-child(4n) {
margin-left: 0;
}
.item:hover {
cursor: pointer;
background-color: #f5f5f5;
}
.item img {
width: 80px;
height: 80px;
margin-right: 10px;
}
.item .name {
font-size: 18px;
margin-right: 10px;
color: #333;
flex: 2;
}
.item .name .tag {
display: block;
padding: 2px;
font-size: 12px;
color: #999;
}
.item .price,
.item .sub-total {
font-size: 18px;
color: firebrick;
flex: 1;
}
.item .price::before,
.item .sub-total::before,
.amount::before {
content: "¥";
font-size: 12px;
}
.item .spec {
flex: 2;
color: #888;
font-size: 14px;
}
.item .count {
flex: 1;
color: #aaa;
}
.total {
width: 990px;
margin: 0 auto;
display: flex;
justify-content: flex-end;
border-top: 1px solid #e4e4e4;
padding: 20px;
}
.total .amount {
font-size: 18px;
color: firebrick;
font-weight: bold;
margin-right: 50px;
}
</style>
</head>
<body>
<div class="list">
<!-- <div class="item">
<img src="https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg" alt="">
<p class="name">称心如意手摇咖啡磨豆机咖啡豆研磨机 <span class="tag">【赠品】10优惠券</span></p>
<p class="spec">白色/10寸</p>
<p class="price">289.90</p>
<p class="count">x2</p>
<p class="sub-total">579.80</p>
</div> -->
</div>
<div class="total">
<div>合计:<span class="amount">1000.00</span></div>
</div>
<script>
const goodsList = [
{
id: '4001172',
name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
price: 289.9,
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
count: 2,
spec: { color: '白色' }
},
{
id: '4001009',
name: '竹制干泡茶盘正方形沥水茶台品茶盘',
price: 109.8,
picture: 'https://yanxuan-item.nosdn.127.net/2d942d6bc94f1e230763e1a5a3b379e1.png',
count: 3,
spec: { size: '40cm*40cm', color: '黑色' }
},
{
id: '4001874',
name: '古法温酒汝瓷酒具套装白酒杯莲花温酒器',
price: 488,
picture: 'https://yanxuan-item.nosdn.127.net/44e51622800e4fceb6bee8e616da85fd.png',
count: 1,
spec: { color: '青色', sum: '一大四小' }
},
{
id: '4001649',
name: '大师监制龙泉青瓷茶叶罐',
price: 139,
picture: 'https://yanxuan-item.nosdn.127.net/4356c9fc150753775fe56b465314f1eb.png',
count: 1,
spec: { size: '小号', color: '紫色' },
gift: '50g茶叶,清洗球'
}
]
// 1.根据数据渲染页面
document.querySelector('.list').innerHTML=goodsList.map(item=>{
// 对象解构
const {picture,name,count,price,spec,gift}=item
// 规格文字模块处理
const text=Object.values(spec).join('/')
// 计算小计模块 单价*数量
const subTotal=((price*100*count)/100).toFixed(2)//避免浮点数精度问题
// 处理赠品模块
const str=gift?gift.split(',').map(item=>`<span class="tag">【赠品】${item}</span>`).join(''):''
return `
<div class="item">
<img src="https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg" alt="">
<p class="name">${name} ${str}</p>
<p class="spec">${text}</p>
<p class="price">${price.toFixed(2)}</p>
<p class="count">x${count}</p>
<p class="sub-total">${subTotal}</p>
</div>
`
}).join('')
// 3.合计模块
const total=goodsList.reduce((prev,item)=>prev+(item.price*100*item.count)/100,0)
document.querySelector('.amount').innerHTML=total.toFixed(2)
</script>
</body>
</html>
数值型转化为字符型方法:num.String()或者num.toString()
上述均为学习哔站pink老师视频所得!