变量声明
- 优先使用const
- 当需要改变对象时再将其改为let
- 因为实际开发中大多使用Object变量,其在栈中存储的值一直不会改变,而是改变堆中的数据,因此这些都能使用const进行定义
WebAPI
- 作用:使用JS操作html和浏览器
- 分类:
- DOM(文档对象模型)用来呈现以及与任意HTML或XML文档交互的API,即操作网页内容
- BOM(浏览器对象模型)
DOM
- 文件对象模型,用来呈现以及与任意HTML或XML文档交互的API
- 作用:开发网页内容特效和实现用户交互
DOM树
- 将HTML以树型结构直观地表现出来,我们称之为文档树或DOM树
- 描述网页内容关系的名词
- 作用:文档树直观的体现了标签与标签之间的关系
DOM对象
- DOM对象指浏览器根据html标签生成的JS对象
- 所有标签都可以在这个对象上找到
- 修改这个对象的属性会自动映射到标签身上
- DOM的核心思想
- 把网页内容当作对象处理
- document对象
- 是DOM提供的一个对象
- 提供的属性和方法都是用来访问和操作网页内容的
- 网页的所有内容都在document中
获取DOM
- 利用CSS选择器获取DOM
document.querySelector('css选择器')
选择第一个css匹配的元素document.querySelectorAll('css选择器')
选择多个匹配的元素,得到对象伪数组,但这个数组只有长度,索引,但并没有pop()和push()方法
document.getElementById('nav')
根据ID获取,获取单个document.getElementByTagName('div')
根据标签名获取获取伪数组document.getElementByClassName('w')
根据类名获取伪数组
操作内容
.innerText
属性- 获取文字内容
- 显示为纯文本,不解析HTML
.innerHTML
属性- 将文本内容添加/更新到任意标签位置
- 会解析HTML,多标签建议使用模板字符
操作元素属性
操作常用属性
obj.attr = newValue
操作样式属性
- 通过style属性操作CSS
obj.style.width = '200px'
- 属性中的短横线属性的属性名将自动被转换为小驼峰命名
- 通过className属性操作CSS
obj.className = 'nav'
- 之一操作将会直接覆盖以前的类名
- 通过classList操作CSS
- 这一操作可以实现追加、删除类名
box.classList.add('active')
box.classList.remove('active')
box.classList.toggle('active')
可以实现如果有active
就删掉,如果没有就加上的效果
于是,使用className和classList的区别在于:
- classList修改大量样式的时候更方便
- className修改不多样式的时候更方便
- classList追加和删除不影响原本有的类名
操作表单元素属性
获取DOM:
DOMObj.attr
设置DOM:
DOMObj.attr = newValue
获取表单对象中的值:
DOMObj.value
- 但
<button></button>
双标签的内容获取比较特殊,需要使用innerHTML
获取
表单中使用bool为表单元素添加状态例如:
- disable
input.checked = true
- 或者使用
input.checked = 'true'
,此处进行了隐式类型转换,因为'true'
转化为boolean的值也为true
- checked
- selected
- disable
操作自定义属性
自定义属性是html5中推出的新特性,以data-
开头,在DOM对象上一律以dataset对象方式获取
JS通过dataset
方式访问:
1 | const one = document.querySelect('div') |
该方式返回所有自定义属性的集合,之后按自定义属性名data-attr
后的attr
访问即可得到值:
1 | console.log(one.dataset.attr) |
定时器和间歇函数
间歇函数
可以使用定时器函数重复执行代码
- 开定时器
setInterval(函数名, 间隔时间)
setInterval('函数名()', 间隔时间)
(极少使用- 每隔间隔时间调用函数
- 间隔时间单位为毫秒
- 该set函数返回一个id数字表示定时器编号
- 关闭定时器
clearInterval(定时器id)
事件监听
事件:系统内发生的动作或者发生的事情
事件监听:让程序检测是否有事件产生,一旦事件出发就立即调用函数做出响应
添加事件监听:
obj.addEventListener('事件类型',要执行的函数)
事件监听三要素:
- 事件源:哪个DOM元素的事件被触发了,要获取DOM元素
- 事件类型:用什么方式触发的,例如click、mouseover等
- 事件调用的函数:做什么
事件监听版本
- DOM L0
- 是DOM的第一个版本
事件源.on事件 = function(){}
- 这种写法实际上是通过
onclick
属性进行绑定,因此当绑定多个相同事件类型的事件时,后面的事件会覆盖前面的事件 - 因此直接赋值null即可解绑
- 都是冒泡阶段执行(即这一写法只有冒泡,没有捕获
- DOM L1
- 1998年10月1日成为W3C推荐标准
- DOM L2
- 添加
addEnventListener
注册事件 事件源.addEnventListener(事件, 事件处理函数, 是否使用捕获(否则冒泡)
- 事件不会出现发生覆盖
- 可以做事件冒泡,可以做事件捕获
- 可以通过第三个参数确定实在冒泡阶段执行还是捕获阶段执行
- 必须使用
removeEventListener
- 匿名函数无法被解绑
- 添加
- DOM L3
- 在DOM L2事件的基础上重新定义了这些事件,并添加了一些新的事件类型
事件类型
- 鼠标事件
- click
- mouseenter 鼠标经过(无冒泡 推荐
- mouseleave 鼠标离开(无冒泡 推荐
- mouseover 鼠标经过(有冒泡
- mouseout 鼠标离开(有冒泡
- 焦点事件
- focus 获得焦点
- blur 失去焦点
- 键盘事件
- Keydown 键盘按下触发
- Keyup 键盘抬起触发
- 文本事件
- input 用户输入事件
需要注意鼠标事件中的mouseover和mouseout两个事件:
1 | <html> |
上述案例中,鼠标先经过dad,会触发一次mouseover
事件,打印鼠标经过,
接着鼠标从dad移动到son时会触发一次mouseout
事件,打印鼠标离开,
最后由于鼠标经过son,触发son的mouseover
事件并冒泡到dad节点,因此还会触发dad的mouseover
事件,打印一次鼠标经过
事件对象
该对象包含事件触发时的相关信息
- 获取
- 事件绑定的回调函数的第一个参数
- 一般命名为event、ev、e
obj.addEventListener('click', function(e){})
- 部分常用属性
- type 当前事件类型
- clientX/clientY 光标相互对于浏览器可见窗口左上角的坐标
- offsetX/offsetY 光标相对于当前DOM元素左上角的位置
- key
- 用户按下的键盘的值
- 现在不提倡使用KeyCode属性(已经废弃,被code属性代替,但code很多浏览器还未实现(2023-3-29
环境对象
指函数内部特殊的变量this,它代表当前函数运行时所处的环境
每个函数都有this环境对象
在普通函数中,this指向window
1 | function fn() { |
事实上this指向的是函数的调用者,普通函数的调用者均为window
对象
因此事件函数中的this指向的是事件源
但箭头函数没有this,他会继承外部的this
并且this的指向可以通过call
,apply
改变指向
回调函数
定义:如果将函数A作为参数传递给函数B时,称函数A为回调函数
事件流
事件流指事件完整执行过程中的流动路径
事件流分为两个阶段:
- 捕获阶段
- DOM会从根节点
Document
开始逐级向下捕获事件
- DOM会从根节点
- 冒泡阶段
- DOM查找到触发事件的元素后,该元素会逐级向上将事件传递到根节点
Document
,这一过程称为事件冒泡
- DOM查找到触发事件的元素后,该元素会逐级向上将事件传递到根节点
实际工作中大部分是使用事件冒泡
事件捕获
从DOM的根元素开始去执行对应的事件(从外到里)
事件捕获需要在事件绑定阶段添加第三个参数查看:
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
传入true则查看事件捕获(默认为false
传入false代表冒泡阶段触发
DOM L0只有冒泡没有捕获(IE浏览器不支持捕获
事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素,这一过程称为冒泡
- 事件冒泡默认存在
- L2监听事件的第三个参数是false,或者默认冒泡
阻止冒泡
由于冒泡的默认存在,则容易影响父级元素
则可以是用如下语法组织之间冒泡
eventObj.stopPropagation()
1 | let DOMObj = document.querySelect('.div') |
注意:此方法可以阻断事件的流动传播,不光会阻止冒泡,在驳货阶段也有效
接触绑事件
DOM L0级的事件由于存在覆盖现象,因此直接使用:
btn.onclick = null
即可
而DOM L2级事件需要使用新的方法removeEventListener
1 | target.removeEventListener(type, listener, [options]) |
注意:匿名函数无法被解绑
事件委托
事件委托是一种利用事件流的特性解决一些开发需求的技巧
- 优点:减少注册次数,提高程序性能
- 原理:利用事件冒泡的特性
- 给父元素注册事件,则触发子元素的事件后,会冒泡到父元素身上,变为触发父元素的事件
- 父元素可以使用事件对象中的
e.target
来获取到事件的具体触发对象 - 还可以通过
e.traget.tagName
获取到对象的标签名
组织默认行为
使用e.preventDefault()
方法来组织默认行为
其他事件
页面加载事件
页面完全加载完成后,会触发load
事件
- 作用:加载外部资源(如图片、外联CSS、JS)加载完毕时触发的事件
- 原因:
- 即有时需要等待页面资源全部加载完成再处理一些事情
- 老代码喜欢把JS写在head中,这是直接找DOM元素是找不到的
- 操作:
- 给window添加load事件以实现
window.addEventListener('load', function(){})
- 还可以针对某些个体元素添加
此外还有DOMContentLoaded
事件,无需等待样式表、图像等完全加载,只需要DOM加载完成即可触发
- 作用:监听页面DOM加载完毕
- 给
document
添加这一事件 document.addEventListener('DOMContentLoaded', function(){})
- 给
元素滚动事件
滚动条滚动的时候触发的事件
- 监听整个页面的滚动:
window.addEventListener('scroll', function(){})
- 也可以给某个元素添加
- 如果要确定滚动发生了多少,则可以使用如下属性:
DOMObj.scrollLeft
、DOMObj.scrollTop
- 选取被卷去的大小
- 获取元素内容往左往上滚出去看不到的距离
- 两个属性均可读写
- 获取的指为Number类型
- 需要注意的是,如果需要确定整个页面滚动了多少,则需要访问
html
标签的scrollTop
,此时应该通过:document.documentElement
来获取到html
标签
页面滚动除了给scrollTop
赋值外,还可以使用scrollTo()
方法
elementObj.scrollTo(x,y)
- 其中x,y分别代表x轴和y轴的滚动距离单位为px
页面尺寸事件
- 在窗口大小改变时候触发的事件:
resize
window.addEventListener('resize', function(){})
- 检测屏幕尺寸
clientWidth
,clientHeight
let w = document.documentElement.clientWidth
- 这一方法用于获取可见部分的高度,包含padding(不包含border、margin、滚动条等
offsetWidth
,offsetHeight
- 获取元素的自身高度,包含自身设置的高度、padding、border
- 如果盒子是隐藏的,则结果为0
- 获取元素距离自己定位父级元素的左上距离
- 是只读属性
- 获取元素位置
element.getBoundingClientRect()
- 该方法返回元素的大小及其相对于视口的位置
- 相对于视口意味着页面滑动的时候位置会发生改变
属性 | 作用 | 说明 |
---|---|---|
scrollTop和scrollLeft | 被卷去的头部和左侧 | 配合页面滚动使用,可读写 |
clientWidth和clientHeight | 获得元素宽度和高度 | 不包含border、margin、滚动条。用于js获取元素大小 |
offsetWidth和offsetHeight | 获取元素宽度和高度 | 包含border、padding、滚动条。只读 |
offsetLeft和offestTop | 获取元素距离自己定位父级元素的左、上距离 | 获取元素位置的时候使用。只读 |
getBoundingClientRect | 获取元素相对于视口左上角的坐标 |
日期对象
用来表示事件的对象,可以得到当前系统时间
实例化
使用new
关键字来创建对象的方法叫做实例化
例如获取当前时间:
const date = new Date()
获取指定时间
const date = new Date('2008-8-8 08:30:00')
事件对象方法
通常日期对象返回的数据无法直接使用,因此需要一些方法来获取格式更为简单的时间信息:
方法 | 使用 | 说明 |
---|---|---|
getFullYear() | 获得年份 | 2023 |
getMonth() | 获得月份 | 0~11 |
getDate() | 获得日期 | 不同月份取值不同(0~31) |
getDay() | 获得星期 | 0~6(周日是0) |
getHours() | 获得小时 | 0~23 |
getMinutes() | 获得分钟 | 0~59 |
getSeconds() | 获得秒 | 0~59 |
时间戳
时间戳是一个毫秒数,是从1970年1月1日起到目前为止的毫秒数,是一种特殊的时间计量方式,即时间的存档点
这一方式的出现是为了更方便的对时间进行计算,由于时间中包含年月日时分秒,这些量的进制和单位并不相同,于是时间戳就将他们统一单位以便计算。
于是得到以下计算方式:
- 将来时间戳 - 现在时间戳 = 剩余毫秒数
- 剩余毫秒数转换为剩余时间的时分秒就是倒计时
获得时间戳的三种方法:
getTime()
- 必须先实例化Date对象
+new Date()
::star:- 本质就是将Date对象转化为数字
+new Date('2022-4-1 18:30:00')
Date.now()
- 直接调用类方法,无需实例化
- 但该方法只能得到当前时间戳,而之前的方法可以返回指定时间的时间戳
关于秒数转时间:
d = parseInt(总秒数 / 60 / 60 / 24);
计算天数h = parseInt(总秒数 / 60 / 60 % 24);
计算小时m = parseInt(总秒数 / 60 % 60);
计算分钟s = parseInt(总秒数 % 60);
计算秒数
节点操作
DOM节点
DOM树上的每一个节点
节点类型:
- 元素节点:star:
- 所有标签
- html为根节点
- 属性节点
- 所有属性,例如href
- 文本节点
- 所有文本
- 其他
查找节点
父亲节点查找
使用parentNode属性查找当前节点的最近一级的父节点:
element.parenNode
子节点查找:star:
childNodes
- 获取所有子节点、包括文本节点、注释节点
chidlren
:star:- 返回所有元素节点
- 返回值为一个伪数组
parentElement.children
兄弟节点查找
nextElementSibling
- 下一个兄弟节点
previousElementSibling
- 上一个兄弟节点
增加节点
- 创建节点
document.createElement('标签名')
- 追加节点
parentElement.appendChild(element)
- 插入一个子元素在父节点的最后
parentElement.insertBefore(插入元素, 哪个元素之前)
- 插入一个子元素到父节点中某个子元素的前面
- 复制一个原有节点
element.cloneNode(bool)
- bool = true,则克隆包含后代节点(深拷贝
- bool = false,则克隆时不包含后代节点(浅拷贝
- 默认为false
删除节点
DOM中删除元素必须通过父节点删除
parentElement.removeChild(要删除的节点)
- 如果不存在父子关系(亲父亲子)则删除不成功
- 删除节点和隐藏节点(display:none)有区别:隐藏的节点在DOM中仍然存在,但删除节点会从html中删除
移动端事件
移动端常见的事件有:
touch
事件
touch事件 | 说明 |
---|---|
touchstart | 当手指触摸到一个DOM时触发 |
touchmove | 手指在一个DOM元素上滑动时触发 |
touched | 手指从一个DOM元素上移开时触发 |
JS插件
swiper插件
BOM
什么是BOM
浏览器对象模型
BOM即window对象
BOM和DOM的关系如下:
1 | -window |
- window对象是一个全局对象,是JS中最顶级的对象
- document、alert()、console.log()都是window的属性,基本BOM的属性和方法都是window对象的
- window对象下的属性和方法在调用时可以省去window
- 所有通过var定义的全局作用域的变量、函数都会变成window对象的属性和方法
定时器-延时函数
- JS中内置了一个能让代码延时执行的函数
setTimeout
setTimeout(回调函数, 等待毫秒数)
- 仅执行一次,可以理解为把一段代码延时执行,和
setInterval
函数一样都属于BOM,因此调用时可以省略window - 清除延时函数
clearTimeout(定时器ID)
- 延时函数需要等待,所以后面的代码会先执行
- 每一次调用延时函数都会产生一个新的延时器
JS执行机制
浏览器两个引擎:
- 渲染引擎
- JS解释器(谷歌:V8引擎
经典面试题:
1 | console.log(1111) |
JS的一大特点是:单线程
因为JS诞生时是为了操作DOM,对于某个DOM的进行添加和删除不能同事进行
所以所有任务都需要排队,前一个任务结束,才会执行后一个任务,这导致如果JS执行事件过长,会造成页面渲染不连贯,表现为页面渲染加载阻塞
因此JS出现了同步和异步,两者的区别在于在整条流水线上各个流程的执行顺序不同
同步
前一个任务结束后才能执行后一个任务
同步任务均在主线程上执行,形成一个执行栈
1 | -执行栈 |
异步
做一个任务的同事可以同事处理其他事情
异步任务,JS的异步任务通过回调函数实现,通常有以下三种:
- 普通事件,如click、resize
- 资源加载,如load、error
- 定时器,如setInterval、setTimeout
异步任务将被添加到任务队列(消息队列)中
1 | -任务队列 |
事件循环
- 先执行执行栈中的同步任务
- 异步任务放入任务队列中
- 一旦执行栈中的所有同步任务执行完毕,系统将依次读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈开始执行
思路就是:虽然JS只能处理单线程,但是JS的宿主(浏览器或Node)可以执行多线程,于是JS就将多线程任务委托给宿主执行
由于主线程不断重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)
location
location对象拆分并保存了URL地址的各个组成部分
常用属性和方法:
- href属性获取完整的URL地址,对其赋值时用于地址的跳转
location.href = 'http://www.baidu.com'
- search属性获取地址中携带的参数,即符号?后面的部分
location.search
- hash属性获取地址中的哈希值,符号#后面的部分
location.hash
- vue路由也使用这一的方法,经常用于不刷新页面,来显示不同的页面内容
- reload方法,用来刷新当前页面,传入参数true表示强制刷新
location.reload(true)
navigator
navigator对象记录了浏览器的相关信息
常用属性和方法:
- userAgent
- 检测浏览器的版本及平台
navigator.userAgent.match(/(Android);?[¥s¥/]+ ([¥d.]+)?/)
来判断设备是否为安卓平台navigator.userAgent.match(/(iPhone¥sOS)¥s([¥d_]+)/)
来判断设备是否为苹果平台
history
history对象主要管理历史记录,该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等等
常用属性和方法:
- back()
- 后退
- forward()
- 前进
- go(参数)
- 1前进,-1后退
一般再实际开发中很少使用,但会在一些OA办公系统中见到
本地存储
- 该对象将数据存储在用户浏览器中
- 设置、读取方便、刷新页面不会丢失
- 容量较大,sessionStorage和localStorage约5M左右
localStorage
将数据永久存在浏览器中,除非手动删除,否则关闭页面也会存在
特性:
- 可以多窗口共享(同一浏览器可共享)(涉及到跨域时无法访问
- 生命周期到手动删除为止
- 以键值对的形式存储使用
语法:
- 增
localStorage.setItem(key, value)
- 查
localStorage.getItem(key)
- 删
localStorage.removeItem(key)
- 改
localStoragte.setItem(key, value)
- 放key已经存在localStorage中时,调用set方法将会在原值上进行修改
注意:本地存储只能保存字符串
sessionStorage
特性:
- 生命周期为关闭浏览器窗口
- 在同一个窗口(页面)下数据可共享
- 以键值对的形式存储使用
- 用法与localStorage相同
存储复杂数据类型
由于本地存储只能存储字符串类型,无法直接存储复杂数据类型
因此需要将复杂数据类型转化为JSON字符串,再存储:
JSON.stringify(data)
取出JSON字符串后,可以使用如下方法转化为JS对象:
JSON.parse(JSONString)
map()和join()
map
可以遍历数组的同时处理数据,并返回新数组
const newArr = arr.map(function(ele, index) {console.log(ele, index)//ele 为数组元素,index为索引号})
map也称为映射,与for...each
不同的是,map会返回新数组
join
该方法将数组中的所有元素转换为一个字符串
const string = arr.join('')
参数为一个字符串,表示分隔符,默认为‘,’
正则表达式
用于匹配字符串中字符组合的模式,在JS中也是对象
语法
定义正则的方式有两种:
const regObj = /表达式/
匹配语法:
- 是否匹配
regObj.test(被检测的字符串)
- 匹配返回true,不匹配返回false
- 查找匹配
regObj.exec(被检测字符串)
- 匹配成果返回一个数组,否则返回null
元字符
正则语法中将字符分为两类:
- 普通字符
- 这些字符仅能描述他们本身例如字母和数字
- 即匹配时只能匹配与他们相同的字符
- 元字符
- 具有特殊含义的字符,具有更强大的的匹配功能
- 例如匹配所有小写字母的
[a-z]
- 提高了正则的灵活性
- 提供更强大的匹配功能
元字符本身也有分类:
- 边界符(表示位置,开头和结尾
- 量词(表示重复次数
- 字符类(比如\d 表示0~9,与[0-9]一致
边界符
主要有两个边界符:
边界符 | 说明 | 例子 |
---|---|---|
^ | 表示匹配行首的文本(以谁开始 | /^哈/.test('哈1') |
$ | 表示匹配行尾的文本(以谁结束 | /哈$/.test('2哈') |
但当两个符号同时使用时/^哈$/
代表精确匹配,只有‘哈’
为true,其余均为false,即必须以一个哈开头一个哈结尾
量词
设定某个模式出现的次数
量词 | 说明 | 例子 |
---|---|---|
* | 0 or more | /^哈*$/.test('哈哈') |
+ | 1 or more | /^哈+$/.test('哈哈') |
? | 0 or 1 | /^哈?$/.test('') |
{n} | n | /^哈{4}$/.test('哈哈哈哈') |
{n,} | n or more | /^哈{4,}$/.test('哈哈哈哈哈') |
{n, m} | n ~ m | /^哈{4,6}$/.test('哈哈哈哈哈哈') |
注意:
{n,m}
的逗号左右不能有空格
字符类
[]
匹配字符集合- 例如
/[abc]/
- 匹配串中只要包含abc中任意一个字符,即可返回true
- 但只能匹配一个,即
/^[abc]$/
匹配ab的结果将时false - 此时可以使用量词限制,让他可以匹配两次
/^[abc]{2}$/
- 还可以用一个连字符表示范围
/[a-z]/
- 其中可以放很多范围比如
/[a-zA-Z0-9]/
,中间没有空格 [^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]
匹配非空格其中空格包含换行符、制表符、空格符
例如日期匹配:
/^\d{4}-/d{1,2}-/d{1,2}/
修饰符
约束正则执行的某些细节行为,比如是否区分大小写,是否支持多行匹配等
写法:/表达式/修饰符
i
表示ignore,不区分大小写g
表示global,匹配所有满足的结果- 写的顺序不影响结果
replace
replace提供了一个使用正则匹配后将匹配到的字符串替换的方法:
stringObj.replace(/regObj/, 'replaceText')