2024.5.27
正则表达式
用于匹配字符串中字符组合的模式
作用
- 表单验证
- 过滤敏感词
- 字符串中提取我们想要的部分
test() 检验是否有匹配项
exec() 返回数组
元字符
一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能
^ 以谁开始
$ 以谁结束
/^哈$/ 以ha开头 ha结尾
量词 用来规定某个模式出现得次数
- *重复0次或更多次
- +重复1次或更多次
- ?重复零次或一次
- {4}只能出现4次
- {4,}出现次数大于等于4
- {4,6}重复n到m次
- [] 匹配字符集合 包含里面的任意一个字符
- [a-zA-Z]
- [^a-z] 取反 匹配除了小写字母以外的字符
. 除了换行符之外的任何符号
字符类 预定义
- \d 相当于[0-9]
- \D 相当于[^0-9]
- \w 相当于[a-zA-Z0-9_]
- \W 相当于[^a-zA-Z0-9_]
- \s 匹配空格相当于[\t\r\n\v\f]
- \S 相当于[^\t\r\n\v\f]
- i 不区分大小写 eg/^java$/i
- g 匹配所有满足正则表达式的结果
replace
replace(/正则表达式/,'被替换的文本')
str.replace(/java/ig,'前端')
change事件 内容发了变化
classList.contain() //判断是否包括……属性
今日案例
案例一:用户验证案例 考察reg+className+blur事件
<script>
const input = document.querySelector('input')
const span = document.querySelector('span')
//正则表达式
const reg = /^[a-zA-Z0-9-_]{6,16}$/
//当聚焦离开后触发
input.addEventListener('blur', function () {
if (reg.test(this.value)) {
span.innerHTML = '输入正确'
//增添right类
span.className('right')
} else {
span.innerHTML = '输入错误'
//增添error类
span.className('error')
}
})
</script>
案例二:过滤敏感词 考察replace+reg
<script>
const input = document.querySelector('textarea')
const button = document.querySelector('button')
const div = document.querySelector('div')
const reg = /fuck|shit/ig
//拿到textarea里面的value
button.addEventListener('click', function () {
div.innerHTML = input.value.replace(reg, '**')
})
</script>
案例三:综合案例
register.html 考察reg+setInterval
1.短信验证码部分
//1,发送短信验证码
const code = document.querySelector('.code')
let flag = true
code.addEventListener('click', function () {
if (flag) {
flag = false
let i = 5
code.innerHTML = `0${i}秒后重新获取`
let timeid = setInterval(function () {
i--
code.innerHTML = `0${i}秒后重新获取`
if (i === 0) {
clearInterval(timeid)
code.innerHTML = `重新获取`
flag = true
}
}, 1000)
}
})
2.表单验证(ps 后续手机号、密码等验证相似)考察change事件 当内容发生改变时触发
//表单验证
const username = document.querySelector('[name=username]')
username.addEventListener('change', verifyName)
function verifyName() {
const span = username.nextElementSibling
const reg = /^[a-zA-Z0-9-_]{6,10}$/
if (!reg.test(username.value)) {
span.innerHTML = '输入不合法,请输入6~10位'
return false
}
span.innerHTML = ''
return true
}
3.我同意模块 考察点:toggle
const agree = document.querySelector('.icon-queren')
agree.addEventListener('click', function () {
this.classList.toggle('icon-queren2')
})
4.提交模块 考察点:submit contains preventDefault
const form = document.querySelector('form')
form.addEventListener('submit', function (e) {
//我同意是否勾选
if (!agree.classList.contains('icon-queren2')) {
alert('请勾选同意协议')
e.preventDefault()}
if (!verifyName()) e.preventDefault()
if (!verifyCode()) e.preventDefault()
if (!verifyConfirm()) e.preventDefault()
if (!verifyPassword()) e.preventDefault()
if (!verifyPhone()) e.preventDefault()
})
login.html localStorage.setItem
<script>
//tab栏切换
const tab_nav = document.querySelector('.tab-nav')
const pane = document.querySelectorAll('.tab-pane')
tab_nav.addEventListener('click', function (e) {
if (e.target.tagName === 'A') {
tab_nav.querySelector('.active').classList.remove('active')
e.target.classList.add('active')
for (let i = 0; i < pane.length; i++) {
pane[i].style.display = 'none'
}
pane[e.target.dataset.id].style.display = 'block'
}
})
//点击提交模块
const form = document.querySelector('form')
const username = document.querySelector('[name=username]')
const agree = document.querySelector('[name=agree]')
form.addEventListener('submit', function (e) {
e.preventDefault()
if (!agree.checked) {
return alert('请勾选同意协议')
}
//记录用户名
localStorage.setItem('xtx-uname', username.value)
location.href = './index.html'
})
</script>
index.html
<script>
const li1 = document.querySelector('.xtx_navs li:first-child')
const li2 = li1.nextElementSibling
//做个渲染函数
function render() {
const uname = localStorage.getItem('xtx-uname')
if (uname) {
li1.innerHTML = `<a href="javascript:;"><i class="iconfont icon-user">${uname}</i></a>`
li2.innerHTML = `<a href="javascript:;">退出登录</a>`
} else {
li1.innerHTML = `<a href="./login.html">请先登录</a>`
li2.innerHTML = `<a href="./register.html">免费注册</a></a>`
}
}
render()
li2.addEventListener('click', function () {
localStorage.removeItem('xtx-uname')
//重新渲染
render()
})
</script>
2024.5.28
综合案例:图片切换模块
<script>
//获得三个盒子
const small = document.querySelector('.small')
const middle = document.querySelector('.middle')
const large = document.querySelector('.large')
small.addEventListener('mouseover', function (e) {
if (e.target.tagName === 'IMG') {
this.querySelector('.active').classList.remove('active')
e.target.parentNode.classList.add('active')
middle.querySelector('img').src = e.target.src
large.style.backgroundImage = `url(${e.target.src})`
}
})
//3.鼠标经过中等盒子,显示隐藏大盒子
middle.addEventListener('mouseover', show)
middle.addEventListener('mouseleave', hide)
let timeId = null //0
function show() {
clearInterval(timeId)
large.style.display = 'block'
}
function hide() {
timeId = setInterval(function () {
large.style.display = 'none'
}, 200)
}
large.addEventListener('mouseover', show)
large.addEventListener('mouseleave', hide)
//黑色遮罩
//找到黑色盒子得坐标 变化
//鼠标经过中等盒子,显示遮罩
const layer = document.querySelector('.layer')
middle.addEventListener('mouseenter', function () {
layer.style.display = 'block'
})
middle.addEventListener('mouseleave', function () {
layer.style.display = 'none'
})
//移动黑色遮罩盒子
middle.addEventListener('mousemove', function (e) {
//e.pageX
//middle.getBoundingClientRect().left
let x = e.pageX - middle.getBoundingClientRect().left
let y = e.pageY - middle.getBoundingClientRect().top - document.documentElement.scrollTop
if (x >= 0 && x <= 400 && y >= 0 && y <= 400) {
//黑色盒子移动变量
let mx = 0, my = 0
if (x < 100) mx = 0
if (x >= 100 && x <= 300) mx = x - 100
if (x > 300) mx = 200
if (y < 100) my = 0
if (y >= 100 && y <= 300) my = y - 100
if (y > 300) my = 200
layer.style.left = mx + 'px'
layer.style.top = my + 'px'
//大盒子背景图片跟随中等移动
large.style.backgroundPositionX = -2 * mx + 'px'
large.style.backgroundPositionY = -2 * my + 'px'
}
})
</script>
2024.5.29
作用域
局部作用域
- 函数作用域:在函数内部声明的变量,只能在函数内部被访问
- 块作用域:在js种使用{}包裹的代码,代码块内部声明的变量外部将【有可能】无法被访问(这里指的var声明的可以被访问)
全局作用域
script内部声明+.js文件
作用域链
优先查找当前函数作用域,查找不到就依次逐级查找父级作用域直到全局作用域
本质上是最底层的变量查找机
嵌套关系的作用域串联起来形成了作用域链
相同作用域链中按着从小到大的规则查找变量
子作用域能够访问父作用域 反之不行
垃圾回收机制(garbage collection GC)
js种内存的分配和回收都是自动完成的,内存在不使用时会被垃圾回收器自动回收
生命周期:内存分配-内存使用-内存回收
全局变量一般不回收,一般情况下局部变量的值不用了会被自动回收
内存泄漏:程序种分配的内存由于某种原因未释放或无法释放
算法说明:
- 1 栈:由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈中
- 2 堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放在堆里
引用计数法
跟踪记录被引用的次数
被引用一次就++ 没被引用就--
致命问题:嵌套使用 两个对象互相引用,尽管都不使用,GC不会回收 导致内存泄漏
标记清除法
优先使用
将不再使用的对象定义为无法到达的对象
从根部扫描对象,能查找到的就是使用的,查找不到的就是要回收的
闭包 closure(关闭的)
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
但可能会存在内存泄漏
作用:封装数据,实现数据私有,外部也可以访问函数内部的变量
很有用,因为它允许将函数与其所操作的某些数据关联起来
怎么理解闭包:内层函数+外部函数的变量
变量提升
仅存在于var声明变量,是js中比较奇怪的现象,它允许在变量声明之前被访问
函数提升
能够使函数的声明调用更灵活
函数参数
动态参数
arguments使函数内部内置的伪数组变量
arguments是一个数组
剩余参数
...arr
更加灵活
展开运算符
将一个数组进行展开:求数组最大最小值,合并数组
eg: consolo.log(Math.max(...arr))
箭头函数
基本语法
const fun = () =>{}
只有一个形参时,可以省略小括号
只有一行代码的时候,可以省略大括号
eg: const fun = x => consolo.log(x)
箭头函数里面没有arguments 但是有...args
解构赋值
数组解构 将数组的单元值快速批量赋值给一系列变量的简洁语法
const [max,min,avg] = [100,60,80]
必须添加分号:①立即执行函数 ②数组解构
对象解构
const {uname,age} = {uname:'pink',age:18}
改名字 const {uname:username,age}
forEach方法
遍历数组的每个元素
不返回数组,map会返回数组
fliter筛选
筛选数值
今日案例:价格筛选
<script>
//1.渲染函数
function render(arr) {
let str = ''
arr.forEach(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
})
}
render(goodsList)
//2.过滤筛选
document.querySelector('.filter').addEventListener('click', e => {
if (e.target.tagName === 'A') {
let newarr = goodsList
if (e.target.dataset.index === '1') {
newarr = goodsList.filter(item => item.price > 0 && item.price <= 100)
} else if (e.target.dataset.index === '2') {
newarr = goodsList.filter(item => item.price > 100 && item.price <= 300)
} else if (e.target.dataset.index === '3') {
newarr = goodsList.filter(item => item.price > 300)
}
render(newarr)
}
})
</script>
2024.5.30
Array
- join 返回字符串 ,数组元素拼接
- find 查找元素
- every 检验数组所有元素是否都符合指定条件
- some 检验数组中的元素是否满足
- concat 合并数组
- sort 排序
- splice 删除元素
- reverse 反转
- fondIndex 查找元素的索引值
- map 返回数组
- filter 过滤数组
- fill 填充
把伪数组转换成真数组
- Array.from(lis)
- [...list]也可以
String
- length string长度
- split 分割字符串为数组
- substring 截取字符串
- startsWith 判断是不是以某个字符开头
- includes 是否包含
- toUpperCase 转换为大写
- toLowerCase 转换为小写
- indexOf
- endWith 判断是不是以某个字符结束
- replace 替代
- match 匹配
今日案例:购物车模块
<script>
const list = document.querySelector('.list')
const amount = document.querySelector('.amount')
//1.渲染页面
list.innerHTML = goodsList.map(item => {
const { picture, name, spec, price, count, gift } = item
//二元选择器
const str = gift ? gift.split(',').map(function (item) {
return `
<span class="tag">【赠品】 ${item}</span>`
}).join('') : ''
return `
<div class="item">
<img src="${picture}" alt="">
<p class="name">${name} ${str}</p>
<p class="spec">${Object.values(spec).join('/')}</p>
<p class="price">${price.toFixed(2)}</p>
<p class="count">${count}</p>
<p class="sub-total">${((price * 100 * count) / 100).toFixed(2)}</p>
</div>
`
}).join('')
acount.innerHTML = goodsList.reduce(function (prev, current) {
return prev + current.count * current.price
}, 0).toFixed(2)
</script>
2024.5.31
两种编程思想
面向过程编程
分析出解决问题所需要的步骤,然后利用函数把这些步骤一步步实现,使用的时候再依次调用
性能更高
面向对象编程OOP
把事物分解成 一个个对象,由对象之间分工与合作
更加灵活,适合多人合作的大型软件项目
特性:继承、封装、多态
构造函数体现了封装的特性、很好用但是存在浪费内存的问题
原型 prototype
本质上也是个对象,用于解决构造函数浪费内存的问题
可以把不变的方法,直接定义在prototype对象上面,这样所有对象的实例就可以共享这些方法
构造函数和原型对象
constructor属性
每个原型对象里面都有个constructor属性
该属性指向该原型对象的构造函数,就是指向我的爸爸
对象原型
在实例对象里面指向原型对象
对象都会有一个属性__proto__指向构造函数prototype的原型对象,之所以我们对象可以构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型继承
原型链
Person.prototype.__proto__===Object.prototype (true)
基于原型对象的继承使得不同构造函数的原型对象关联在一起,
是一个查找规则 先查找当前身上有没有该属性,没有就依次往上查找指到找到object对象为止
intanceof
用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
深浅拷贝
浅拷贝
拷贝的是地址 不会影响原来的
- 1.拷贝对象 object.assign()
- 2.拷贝数组 array.prototype.concat or [...arr]
直接赋值 赋值的是地址 只要是对象都会互相影响
浅拷贝 如果是一层对象,不相互影响,如果出现多层对象拷贝还会互相影响
深拷贝
拷贝的是对象不是地址
- 通过递归 利用递归函数实现setTimeout模拟setInterval效果
- lodash/cloneDeep
- JSON.stringify()
深拷贝
1.新对象不会影响旧对象,用到函数递归
2.遇到普通元素直接拷贝就行
3.遇到数组元素再次运行这个拷贝函数就行了
4.遇到对象形式,再次利用递归解决 先array后对象
lodash
- 先引入lodash
- 在调用 _.cloneDeep(obj)
JSON.stringify()
异常处理
可以提高代码的健壮性
throw抛异常
会终止程序
throw new error ''
try/catch 捕获错误信息
try{}
catch(err){
consolo.log(err.message)
}
finally {
//不管怎么样都会执行的代码
}
debugger
this
箭头函数没有this
在开发中使用箭头函数前需要考虑函数中this的值,因此DOM事件回调函数如果里面需要DOM对象的this,则不推荐使用箭头函数
原型对象也不推荐使用箭头函数
适用于需要使用上层this的地方
改变this
call()
fun.call(thisArg,arg1,arg2) thisArg是指的fun的this指向,后面是参数传递
apply()
fun.apply(thisArg,[argsArray])
参数必须以数组形式传递进入
可以用与求最大值
const max = Math.max.apply(Math,[1,2,3])
bind()
fun.bind(thisArg,arg1,arg2)
bind() 不会调用函数
防抖 debounce
单位时间内,频繁触发事件 只执行最后一次
-.debounce(func,wait=0,options=)
手写防抖函数
核心是利用setTimeout定时器实现的
1.声明定时器变量
2.每次鼠标移动的时候都要先判断是否有定时器,如果有就先清楚以前的定时器
3.如果没有定时器,就开启定时器,存入到定时器变量里面
4.定时器里面写函数调用
节流 throttle
在单位时间内,频繁触发事件,只执行一次