Web API
一、名词解释:
webAPI:
- 使用js来操作html和浏览器
DOM : document object model 文档对象模型.
===> 把一个页面当做一个对象来看待. 对象有属性和方法.
是用来呈现以及与任意HTML或XML文档交互的API;
通过DOM提供给我们的属性和方法, 操作网页内容.
BOM: browser object model 浏览器对象模型
====> 把浏览器当做一个对象来看待. 浏览器的顶级对象就是window
通过浏览器提供给我们的方法, 操作浏览器的功能.
对象:
- 描述事物的特征和行为 具有属性和方法
文档:
- 一个页面就是一个文档. document
元素:
- 页面上所有的标签都叫元素. element
节点:
- 页面上所有的内容 都叫节点(元素节点 文本节点 属性节点 注释节点)
DOM对象(DOM元素):
- 把页面上的内容(元素/节点)当成对象来看待
DOM树:
- 描述了元素节点之间的一种层级关系 父子关系或者兄弟关系
document对象:
- 页面上的顶级对象
元素和节点有什么区别:
DOM节点:
- DOM 文档是由节点层次结构组成。每个节点可以有父级或子级节点
<!DOCTYPE html> <html> <head> <title>My Page</title> </head> <body> <!-- Page Body --> <h2>My Page</h2> <p id="content">Thank you for visiting my web page!</p> </body> </html>
const paragraph = document.querySelector('p'); console.log(paragraph.nodeType); // 1 ==> Node.ELEMENT_NODE paragraph instanceof Node; // => true paragraph instanceof HTMLElement; // => true /* paragraph 既是节点 也是元素 */ const firstChild = paragraph.childNodes[0]; console.log(firstChild.nodeType); // 3 ==> Node.TEXT_NODE console.log(document.nodeType); // 9 ==> Node.DOCUMENT_NODE
DOM元素:
- 元素是特定类型的节点,是使用 HTML 文档中的标记(标签)编写的节点
了解一些常见的节点类型:
1. DocumentType类型 —— 文档类型声明节点
文档类型声明节点即文档最顶部的类型声明,比如,`。
文档类型声明节点的
nodeType
==10
。文档类型声明节点的
nodeName
等于文档的类型名。文档类型声明节点没有子节点。
2. Document类型 —— 文档节点
在浏览器中,
document
对象是HTMLDocument
类型的一个实例,而HTMLDocument
继承自Document
类型。
docuemnt
对象作为文档的顶层节点,我们称其为文档节点。文档节点的
nodeType
等于9
。文档节点的
nodeName
等于'#document'
。
3. Element类型 —— 元素节点
元素节点的
nodeType
等于1
元素节点的
nodeName
等于元素的标签名所有HTML元素都由
HTMLElement
或其子类型表示,HTMLElement
类型继承自Element
类型。
HTMLElement
类型设定了id
、className
、title
等基本属性,所有HTML元素都支持这些属性
4. Text类型 —— 文本节点
文本节点的
nodeType
等于3
.文本节点的
nodeName
等于'#text'
文本节点的
nodeValue
即为包含的文本内容文本节点的父节点是元素节点,没有子节点
文本节点拥有一个
length
属性,表示该文本中的字符数空格也会生成文本节点
文本节点的创建:
document.createTextNode('文本内容')
。默认情况下,一个元素节点下最多只能有一个文本节点。
5. Attr类型 —— 属性节点
一般不将其视作DOM树的一部分
从HTML的角度来看,属性即元素标签内的特性;从JS的角度看,属性就是元素节点对象实现和提供的相关节点
属性节点的
nodeType
等于2
.属性节点的
nodeName
等于属性名属性节点在HTML中没有子节点
在实际开发中,对于属性的操作我们一般都使用
getAttribute()
、setAttribute()
、removeAttribute()
这几个更加好用的方法。
二、获取DOM对象
1. 获取元素的方式
document.querySelector('选择器')
// 作用:返回指定选择器的第一个DOM对象(元素)
document.querySelectorAll('选择器')
// 作用:返回匹配的所有元素的集合
// 返回值:是一个伪数组 一堆集合
// 伪数组:有length 有索引 没有数组的pop,shift等方法
document.getElementById('id名')
document.getElementsByClassName('类名')
document.getElementsByTagName('div')
/* 特殊元素的获取 */
body ==> document.body
html ==> document.documentElement
console.dir();打印一个对象的所有属性和方法
2. 属性选择器
<input type="text" value="123" data-id="0" data-name="andy">
<input type="password">
const input1 = document.querySelector('input[value]')
// console.log(input)
console.log(input.dataset) // 自定义属性集合
console.log(input.dataset.name) // 自定义属性集合
console.log(input.dataset.id) // 自定义属性集合
// 获取元素
// document.querySelector('选择器')
// 1. [] 方括号立马写上属性名
const input2 = document.querySelector('input[type="password"]')
// 2. 方括号里键值对 引号可以省略
const input3 = document.querySelector('input[type=password]')
// 3. 可以直接写属性也能获取到
const input4 = document.querySelector('[type=password]')
const input5 = document.querySelector('[value="123"]')
const input6 = document.querySelector('[data-id]')
console.log(input)
3. 操作元素内容
innerHTML 识别html标签
innerText 不识别html标签
4. 修改标签样式属性
-
style (权重大 1000)
-
className
className适合多个样式都需要修改的情况
也可以清空某个元素的所有类名 div.className =’ ’
-
classList
- 添加类名:box.classList.add(‘类名’)
- 删除类名:box.classList.remove(‘类名’)
- 切换类名:box.classList.toggle(‘active’)
三者的使用选择:
- style 适合修改单个或少部分样式的时候使用
- className 缺陷会覆盖原来的类名
- classList 不会覆盖原来的类名 是追加或者删除当前的这个类名
三、自定义属性
- H5 新增, 以 data - 来定义自定义属性
- dataset获取自定义属性:
- 它获取到的是一个对象, 存放了所有以**data开头**定义的自定义属性
element.dataset.属性名
element.dataset[‘属性名’] (注意:单引号)
- dataset内, 属性名从短横线变为了驼峰
<div data-id="111" data-test-id="2222"></div>
<a href="http://www.baidu.com" target="_blank" person="我也是自定义"></a>
<div data-id="0" data-test-id="222"></div>
<ul>
<li>123</li>
<li>123</li>
<li>123</li>
<li>123</li>
<li>123</li>
</ul>
// 自定义属性 ,我们自己定义的一些属性。
const div = document.querySelector('div') // 获取第一个div
const a = document.querySelector('a')
console.log(typeof div.dataset) // 'object'
// 对象 获取属性值的两种方式:
// 1. 对象.属性名
// 2. 对象['属性名']
console.log(div.dataset.id)
console.log(div.dataset['id']) // 注意:单引号
// dataset内, 属性名从短横线变为了驼峰
console.log(div.dataset.testId)
console.log(div.dataset['testId'])
// 1. 获取
// 获取自定义属性 元素.getAttribute('属性名')
console.log(a.getAttribute('person'))
// 它还可以获取元素本身自带的属性
console.log(a.getAttribute('href'))
// 2. 设置 \ 修改
// 设置元素自定义属性 元素.setAttribute('属性名', 值)
a.setAttribute('test-demo', '哈哈哈哈')。
// setAttribute() 还可以设置(修改)本身自带的属性值
a.setAttribute('href', 'http://jd.com')
console.log(a.getAttribute('href'))
// setAttribute() 用js自动设置自定义属性 更推荐以'data-'开头自定义属性
// 3. 移除属性
// el.removeAttribute('属性名')
a.removeAttribute('test-demo')
// 需求:给每一个li标签 设置自定义属性data-id 索引号从0开始
const lis = document.querySelectorAll('li')
lis.forEach(function(item, i) {
item.setAttribute('data-id', i)
})
// item.getAttribute(属性名) ==> 可以获取元素的属性
// item.setAttribute(属性名,值) ==> 可以自定义属性 还可以修改元素本身的属性
// item.removeAttribute(属性名) ==> 移除元素的某个属性
四、遍历对象获取属性
const obj = {
name: 'pink',
age: 20,
hobby: '唱歌',
key: '234'
}
for (let key in obj) {
console.log(key) //name age hobby key
console.log(obj[key]) //'pink' 20 '唱歌' '234'
console.log(obj['name']) // 'pink'
// 1. 这里的key是一个变量, 每次循环的时候,依次表示每一个属性名
// 第一次循环 key 'name'
// 第二次 key 'age'
// 第三次 key 'hobby
}
// 如果我们要直接取属性的值, 这里, 需要加引号
console.log(obj['age'])
// 如果不加引号, 表示变量, 找不到age,
console.log(obj[age]) // Error 报错
console.log(obj[key]) // Error 报错
console.log(obj['key']) //234
console.log(obj.key) //234
// ===> 最后记住一句话: obj[key]
// 如果不加引号, 表示变量 ,
// 如果加了引号, 表示对象本身存在的某个属性名
五、计时器setInterval
1. setInterval使用
// 第一种:直接把函数写在setInterval()里面
// 语法: window.setInterval(fn, wait)
// window提供的方法 window可以省略
window.setInterval(function() {
console.log('定时器在走');
}, 1000)
// 第二种:具名函数
function fn() {
console.log('午饭吃什么');
}
const foo = function() {
console.log('好好努力');
}
setInterval(foo, 1000)
思考:
var a = 3; //在顶层函数中声明变量a
function f() {
const a = 2; //在函数体内声明局部变量a
return new Function("return a*a;"); //无法捕获局部作用域
}
console.log(f()()); // 9
2. 定义函数有几种方式
// 1. 声明式
function foo(){
console.log(1)
}
btn.addEventListener('click', foo)
// 2. 表达式
const bar = function(){
console.log(2)
}
btn.addEventListener('click', bar)
3. 清除计时器
let timer1 = setInterval(function() {
console.log('22222');
}, 1000)
console.log(timer1);
// 定时器有一个返回值 是一个数字 是当前定时器的唯一标识
// 这个number标记 从1开始
clearInterval(timer1) //清除计时器
六、延时器 setTimeout
setTimeout(function() {
console.log('大家吃早饭了嘛? ')
}, 1000)
// 1.2 写函数名
function cb() {
console.log('饿了吗')
}
const foo = function() {
console.log('真的不饿吗')
}
// 注意:函数名后面一定不要写小括号, 写小括号表示调用
let timer1 = setTimeout(cb, 2000)
let timer2 = setTimeout(foo, 5000)
window.clearTimeout(timerId) // 清除延时器
// window可以省略
setTimeout 延时时间到了,只调用一次
setInterval 每隔这个延时时间,都会回调一次,会调用很多次,直到清除定时器
七、事件监听
事件三要素:
事件源:谁 谁触发了事件
事件类型:什么事件(行为)
事件处理程序:我们要干嘛(事件)
1. 随机点名案例分析(多种方法解析)
<h2>随机点名</h2>
<div class="box">
<span>名字是:</span>
<div class="qs">这里显示姓名</div>
</div>
<div class="btns">
<button class="start">开始</button>
<button class="end">结束</button>
</div>
<!-- 多次重复点击会开启多个计时器 速度会随着点击的次数增加而加快 且后台会冗余大量的计时器 -->
// 解决方法一:
const arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
const start = document.querySelector('.start')
const end = document.querySelector('.end')
const qs = document.querySelector('.qs')
let i
let timerId
start.addEventListener('click', function() {
clearInterval(timerId)
// 在每次点击事件之前 先进行清除计时器 这样确保每次点击开始之后 只有一个计时器在进行
// 缺点:后台会冗余大量已经清除的计时器
timerId = setInterval(function() {
i = Math.floor(Math.random() * arr.length)
qs.innerHTML = arr[i]
}, 50)
if (arr.length === 1) {
start.disabled = end.disabled = true
}
})
end.addEventListener('click', function() {
clearInterval(timerId)
arr.splice(i, 1)
})
// 解决方法二:
const arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
const start = document.querySelector('.start')
const end = document.querySelector('.end')
const qs = document.querySelector('.qs')
let i
let timerId
let flag = true
// 声明flag 运用开关思想 来控制后台启动计时器的数量
start.addEventListener('click', function() {
if (flag === false) return
flag = false
timerId = setInterval(function() {
i = Math.floor(Math.random() * arr.length)
qs.innerHTML = arr[i]
}, 50)
if (arr.length === 1) {
start.disabled = end.disabled = true
}
})
end.addEventListener('click', function() {
clearInterval(timerId)
// 对flag进行判断 是为了防止多次点击“结束” 导致数组里面的元素全部删除清空 出现undefined
if (flag) {
arr.splice(i, 1)
}
flag = true
})
// 解决方法三:
const arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
const start = document.querySelector('.start')
const end = document.querySelector('.end')
const qs = document.querySelector('.qs')
let i
let timerId = 0
start.addEventListener('click', function() {
if (timerId) return
// 就地取材 直接利用现有的timerId变量 运用开关思想 进行判断
timerId++
timerId = setInterval(function() {
i = Math.floor(Math.random() * arr.length)
qs.innerHTML = arr[i]
}, 50)
if (arr.length === 1) {
start.disabled = end.disabled = true
}
})
end.addEventListener('click', function() {
clearInterval(timerId)
if (timerId) {
// 为了防止多次点击“结束” 导致数组里面的元素全部删除清空 出现undefined
arr.splice(i, 1)
}
timerId = 0
})
2. 轮播图
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="toggle">
<button class="prev"><</button>
<button class="next">></button>
</div>
</div>
</div>
// 1. 初始数据
const arr = [{
url: './images/slider01.jpg',
title: '对人类来说会不会太超前了?',
color: 'rgb(100, 67, 68)'
}, {
url: './images/slider02.jpg',
title: '开启剑与雪的黑暗传说!',
color: 'rgb(43, 35, 26)'
}, {
url: './images/slider03.jpg',
title: '真正的jo厨出现了!',
color: 'rgb(36, 31, 33)'
}, {
url: './images/slider04.jpg',
title: '李玉刚:让世界通过B站看到东方大国文化',
color: 'rgb(139, 98, 66)'
}, {
url: './images/slider05.jpg',
title: '快来分享你的寒假日常吧~',
color: 'rgb(67, 90, 92)'
}, {
url: './images/slider06.jpg',
title: '哔哩哔哩小年YEAH',
color: 'rgb(166, 131, 143)'
}, {
url: './images/slider07.jpg',
title: '一站式解决你的电脑配置问题!!!',
color: 'rgb(53, 29, 25)'
}, {
url: './images/slider08.jpg',
title: '谁不想和小猫咪贴贴呢!',
color: 'rgb(99, 72, 114)'
}, ]
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const lis = document.querySelectorAll('.slider-indicator li')
const footer = document.querySelector('.slider-footer')
const prev = document.querySelector('.prev')
const next = document.querySelector('.next')
let i = 0
next.addEventListener('click', function() {
i++
if (i >= 8) {
i = 0
}
render()
})
prev.addEventListener('click', function() {
i--
if (i <= -1) {
i = arr.length - 1
}
render()
})
function render() {
img.src = arr[i].url
p.innerHTML = arr[i].title
footer.style.backgroundColor = arr[i].color
document.querySelector('.slider-indicator .active').classList.remove('active')
lis[i].classList.add('active')
}
/* ================== 计时器 ================== */
const slider = document.querySelector('.slider')
let timer = setInterval(function() {
next.click()
}, 1000)
slider.addEventListener('mouseenter', function() {
clearInterval(timer)
})
slider.addEventListener('mouseleave', function() {
timer = setInterval(function() {
next.click()
}, 1000)
})
3. 全选按钮案例
const checkAll = document.querySelector('#checkAll')
const cks = document.querySelectorAll('.ck')
// 最外层是循环
cks.forEach(function(item) {
// 点击全选按钮
checkAll.addEventListener('click', function() {
item.checked = checkAll.checked
})
// 点击单个按钮
item.addEventListener('click', function() {
const arr = document.querySelectorAll('.ck:checked')
if (arr.length === cks.length) {
checkAll.checked = true
} else {
checkAll.checked = false
}
})
3. 键盘事件(input\change\blur)
- input事件: 在输入框输入的时候会实时响应并触发;
- change事件:在输入框失去焦点,并且输入框的值发生变化的时候才会触发
- blur事件:blur事件是每次失去焦点时触发,不管输入框数据有没有变化;
八、DOM事件对象
事件对象: 当事件发生的时候, 和这个事件相关的所有信息, 都存在这个对象中.
在 DOM 中发生事件时,所有相关信息都会被收集并存储在一个名为 event 的对象中。这个对象包 含了一些基本信息,比如导致事件的元素、发生的事件类型,以及可能与特定事件相关的任何其他数据。 例如,鼠标操作导致的事件会生成鼠标位置信息,而键盘操作导致的事件会生成与被按下的键有关的信 息。
这个对象就叫做事件对象. event, 简写ev、evt ===> e
event 对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁
1. 事件对象常见的公共属性与方法
属性/方法 | 读/写 | 说 明 |
---|---|---|
currentTarget 元素 | 只读 | 当前事件处理程序所在的元素 |
preventDefault() 函数 | 只读 | 用于取消事件的默认行为 |
stopImmediatePropagation() 函数 | 只读 | 用于取消所有后续事件捕获或事件冒泡, 并阻止调用任何后续事件处理程序(DOM3 Events新增) |
stopPropagation() 函数 | 只读 | 用于取消所有后续事件捕获或事件冒泡 |
target 元素 | 只读 | 事件目标 |
type 字符串 | 只读 | 被触发的事件类型 |
e.target ==> 触发事件的元素 点击的是谁 他就指向谁
this ==> 绑定事件的元素
e.currentTarget ==> 绑定事件的元素 等于this
type 属性多用于在一个处理程序处理多个事件
const btn = document.querySelector('button') let handler = function(event) { switch (event.type) { case "click": alert("Clicked"); break; case "mouseover": event.target.style.backgroundColor = "red"; break; case "mouseout": event.target.style.backgroundColor = ""; break; } }; btn.onclick = handler; btn.onmouseover = handler; btn.onmouseout = handler; // 在这个例子中,函数 handler 被用于处理 3 种不同的事件:click、mouseover 和 mouseout // 当按钮被点击时,会在控制台打印一条消息 // 这个函数使用 event.type 属性确定了事件类型,从而可以做出不同的响应。
preventDefault()与stopPropagation()的区别:
preventDefault()方法用于取消默认行为的事件
比如,链接的默认行为就是在被单击时导 航到 href 属性指定的 URL,如果想阻止这个导航行为,可以在 onclick 事件处理程序中取消;以及submit默认提交行为
stopPropagation()方法用于立即阻止事件流在 DOM 结构中传播,取消后续的事件捕获或冒泡
例如,按钮的事件处理程序中调用 stopPropagation(),可以阻止 document.body 上注册的事件处理程序执行
阻止的是事件的传播 不会阻止函数内代码的执行
九、函数内部特殊对象(arguments、this)
1. arguments动态参数
-
arguments 是函数内部内置的伪数组变量,它包含了函数调用时传入的所有实参
- 动态参数,只存在于函数里 是伪数组
- arguments的作用是动态获取函数传递过来的所有实参
- 可以通过for循环一次性得到传递过来的实参
function box() { let sum = 0; if (arguments.length == 0) return sum; //如果没有参数,退出 for(var i = 0;i < arguments.length; i++) { //如果有,就累加 sum = sum + arguments[i]; } return sum; //返回累加结果 } alert(box(5,9,12));
2. this
- this就是一个关键字, 是一个变量. 它的值存的是一个对象.
this在标准函数和箭头函数中有不同的行为。
- 在标准函数中,this 引用的是把函数当成方法调用的上下文对象
- 在箭头函数中,this引用的是定义箭头函数的上下文
// 全局环境中 this指向window (指向 ===> 理解为 等于)
console.log(this); // window
函数内部 this这个对象的值取决于函数被调用的方式
// 函数内部 this这个对象的值取决于函数被调用的方式
// ===> 粗略的规则:谁调用 this指向谁
// 1. 普通函数的调用
function fn() {
console.log(this);
}
fn() // ===> 相当于window.fn()
window.fn()
// 2. 函数作为对象的方法调用
const obj = {
name: 'zjl',
fn: function() {
console.log(this);
}
}
obj.fn() // obj{}
// 2.3 事件绑定中 this指向注册(绑定)事件元素
const btn = document.querySelector('button')
btn.addEventListener('click', function() {
console.log(this); // <button>
this.style.color = 'orange'
console.log(btn === this); // true
})
btn.fn() ==>可以理解为 btn调用了这个函数
十、回调函数
本质上: 就是函数, 只是作为函数的参数来进行使用.
作为函数的参数传入
一开始不执行, 当满足某些触发条件的时候, 才执行.回头再调用
使用场景:事件绑定 / 事件注册 / 事件监听
const btn = document.querySelector('button')
const callback = function() {
console.log('我是回调函数');
}
btn.addEventListener('click', callback)
// 定时器
const cb = function() {
console.log('我也是回调函数');
}
setInterval(cb, 1000)
// 为什么计时器会重新启动?
十一、事件
1. 事件流:
事件在执行过程中的流动路径(元素在页面中接收事件的顺序)
DOM事件流 三个阶段:
1. 捕获阶段 从外到内
2. 处于目标阶段
3. 事件冒泡阶段 从内到外
2. 事件捕获:
是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件
document - > html - > body - > father - > son(从外到内)
addEventListener(type, listener, true) 第三参数为 true
3. 事件冒泡:
事件从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)
son - > father - > body - > html - > document(从内到外)
addEventListener(type, listener, false)
4. 事件解绑
// 第一种:传统方式解绑
const btn = document.querySelector('button')
btn.onclick = function() {
console.log('传统方式解绑');
btn.onclick = null
}
// 第二种:事件监听方式的解绑
const fn = function() {
console.log('事件监听方式的解绑');
btn.removeEventListener('click', fn)
}
btn.addEventListener('click', fn)
// 我们解绑的时候, 需要传入一个函数名字, 表示解绑谁
btn.removeEventListener('click', 没有函数名)
// 建议: 不管是表达式,还是声明式, 都写在事件注册的前面.
// 先声明, 在使用. 函数的定义, 写在调用的前面
// 事件监听, 如果直接写匿名函数, 无法解绑
btn.addEventListener('click', function() {
console.log('你动不了我')
})
5. 事件委托
利用事件冒泡 将事件监听绑定在父元素上 通过父元素来监听子元素的事件
- 当我们使用事件委托的时候,使用**e.target.**tagName来判断我们点击的是否是想要的标签
- 事件委托的好处(作用):减少事件注册的次数 提高程序性能
6. 页面加载事件
- window ==> load 页面上所有的资源加载完执行 包括css、图片
- document ==> DOMContentLoaded 只要DOM元素加载完 就执行回调
1. window ==> load
// 等待页面所有资源都加载完毕后, 才执行回调函数里面的代码。(DOM元素加载完,CSS, 图片等都加载完)
window.addEventListener('load', function(){
// js代码内容
})
2. DOMContentLoaded
// 当DOM元素加载完成时, 就会执行回调函数 不包含图片, 样式等。
document.addEventListener('DOMContentLoaded', function(){
// js代码内容
})
7. 滚动事件
// 被卷去的头部
document.documentElement.scrollTop === window.pageYOffset
// scrollTop 可读写
被检测的元素.offsetTop
// offsetTop 仅可读 被检测元素距离页面顶部的位置
8. 立即执行函数
Immediately-Invoked Function Expression (IIFE) 立即执行函数(自执行函数)
作用:
立即执行函数会形成一个单独的作用域,我们可以封装一些临时变量或者局部变量,避免污染全局变量
// 1. 普通函数, 先声明, 后调用
function fn() {
console.log('我是普通函数')
}
fn() // 函数名加() 调用
// 2. 立即执行函数 : 不需要调用,立马执行的函数
// 以() / [] 开头, 再前面需要加分号!
;(function() { /* code */ })()
;(function() { /* code */ }())
// 3. 多个立即执行函数之间, 要用分号隔开
例题:
;(function() {
// 如果没写关键字, 会变为全局变量 成为window的属性
num = 20
console.log(num)
})()
console.log(num) // 20
九、处理字符串的属性与方法
1. 去除字符串空格trim()
- trim() 去除字符串左右两边的空格
- 语法: str.trim()
- 返回一个新的字符串,不会改变原来的字符串
const str = ' im a teacher '
console.log(str.trim()) // 'im a teacher'
2.数组转换为字符串
toString()
作用: 返回字符串。
join()
- arr.join(分割符)
- 作用: 把数组的所有元素连接成一个字符串, 并返回这个字符串。
- 参数:可以传一个分割符, 也可以省略
// 1. 如果参数省略, 默认使用逗号分割
const arr = ['red', 'pink', 'orange']
console.log(arr.join())
// 2. 传空字符串 直接将数组元素无间隙的连在一起
const res = arr.join('')
// 3. 用分割符来拼接 (分割符可以自己定义)
const res2 = arr.join('*')
3. 字符转换为数组split()
let str2 = 'red, blue, purple';
console.log(str2.split(',')); // [red, blue, purple]
4. 返回指定位置的字符charAt()
var str = "HELLO WORLD";
var n = str.charAt(2) // L
// 遍历字符串里面所有字符
for (let i = 0; i < str1.length; i++) {
console.log(str1.charAt(i));
}
// 查找字符串'addjfirfcjasxaodofcjoao'中所有o出现的位置以及次数
// 因为indexOf 只能查找到第一个 所以后面的查找 一定是当前索引号+1 从而继续查找
let str = 'addjfirfcjasxaodofcjoao';
let index = str.indexOf('o');
let num = 0;
while (index !== -1) {
console.log(index);
num++;
index = str.indexOf('o', index + 1);
// 因为indexOf 只能查找到第一个 所以后面查找 一定是当前索引号+1 从而继续查找
}
console.log('o出现的次数是:' + num);
// 判断字符串 出现次数最多的字符 并统计其次数
// obj.a=1
// obj.b=1
// obj.c=1
let obj = {};
for (let i = 0; i < str.length; i++) {
let chars = str.charAt(i);
if (obj[chars]) { //o[chars]得到的是属性值
obj[chars]++;
} else {
obj[chars] = 1;
}
}
console.log(o);
5. 返回字符串首次出现位置indexOf()
- indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置
- 如果没有找到匹配的字符串则返回 -1
- indexOf 从前往后寻找字符串位置
const str = "hello world"
document.write(str.indexOf("lo") + "<br>"); // 3
6. 切片
substring()
- 提取字符串中介于两个指定下标之间的字符
- 返回字符串:[开始处的字符,结束处的字符)
const str = "hello world"
document.write(str.substring(3)+"<br>"); // lo world
document.write(str.substring(3,7)); // lo w
slice(start,end)
- 可提取字符串的某个部分,并以新的字符串返回被提取的部分
- 返回字符串:[start,end)
- start 参数字符串中第一个字符位置为 0, 第二个字符位置为 1, 以此类推
- end 参数如果为负数,-1 指字符串的最后一个字符的位置,-2 指倒数第二个字符,以此类推
substr()
let str1 = '改革春风吹满地';
console.log(str1.substr(2, 3)); // 春风吹
7. 替换字符replace()
let str = 'addjfirfcjasxaodofcjoao';
// 把字符里面所有的‘f’替换为‘*’
while (str.indexOf('f') !== -1) {
str = str.replace('f', '*')
}
console.log(str);
8. 将 Unicode 编码转为一个字符fromCharCode()
fromCharCode()
- 将 Unicode 编码转为一个字符
const n = String.fromCharCode(65); // A
9. 字符串样式
let str = "hello world"
// length big small fontcolor fontsize bold italics strike删除线 sub下 sup上
// toUpperCase大写 link charAt concat连接字符串 indexOf lastindexOf slice
document.write(str.length + "<br>");
document.write(str.big().big().italics() + "2".sub() + "<br>");
document.write(str.fontcolor('pink') + "<br>");
document.write(str.link("http://www.baidu.com") + "<br>");
十、Date 对象
1. 实例化
- **实例化:**通过构造函数生成实例对象的过程
- **实例:**实例的例子 具体的某一个对象
// 构造函数
function Star(name, age) {
this.name = name
this.age = age
}
const ldh = new Star('刘德华', 20)
console.log(ldh);
通过Star这个构造函数 创建出了刘德华这个具体的明星 这个过程叫做实例化
2. 常见的方法
let date = new Date()
date.getFullYear() // 年
date.getMonth() // 月(0~11)
date.getDate() // 日(1~31)
date.getDay() // 星期(0~6)
date.getHours() //时
date.getMinutes() //分
date.getSeconds() //秒
date.getTime() 、date.now() // 时间戳 后者性能更好
date.toString());
> "Fri Sep 09 2022 01:04:24 GMT+0800 (中国标准时间)"
date.toDateString());
> "Fri Sep 09 2022"
date.toLocaleDateString(undefined, options);
> "2022年9月9日星期五"
date.toLocaleDateString();
> "2022/9/9"
date.toLocaleTimeString('en-US');
> "8:47:13 AM"
date.toLocaleTimeString();
> "08:47:13"
3. 倒计时
<div class="countdown">
<!-- <p class="next">今天是2222年2月22日</p> -->
<p class="title">倒计时</p>
<p class="clock">
<span id="day">00</span>
<i>:</i>
<span id="hour">00</span>
<i>:</i>
<span id="minutes">25</span>
<i>:</i>
<span id="scond">20</span>
</p>
<!-- <p class="tips">18:30:00下课</p> -->
</div>
const getCountTime = function() {
// 得到当前的时间戳
const now = +new Date()
const future = +new Date('2022-9-8 09:00:00')
const time = (future - now) / 1000
// 除以1000是为了得到秒数
let d = parseInt(time / 60 / 60 / 24)
let h = parseInt(time / 60 / 60 % 24)
let m = parseInt(time / 60 % 60)
let s = parseInt(time % 60)
// 补 0
d = d < 10 ? '0' + d : d
h = h < 10 ? '0' + h : h
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
// 将天、时、分、秒放入盒子
const arr = [d, h, m, s]
const spans = document.querySelectorAll('span')
arr.forEach(function(item, index) {
spans[index].innerHTML = item
})
}
getCountTime()
setInterval(getCountTime, 1000)
十一、节点操作
1. 父节点
文档: document 一个页面就是一个文档 window ==> document ==> html
元素: element 页面上所有的标签都叫元素
节点: node 页面上所有的内容都叫节点 : 元素节点 属性节点 文本节点 注释节点
父节点: node.parentNode 返回最近一级的父元素(节点) 如果找不到就返回null
2. 子节点
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
// 1. 子节点 (元素/文本/属性/注释节点)
// 语法: node.childNodes node表示父元素
const ul = document.querySelector('ul')
console.log(ul.childNodes)
// 返回值, 是一个伪数组: 有length, 有索引, 但是没有数组的pop()/push()等方法
// 2. 子元素节点
// 语法:node.children
// 获取的是夫元素下的子元素节点(亲儿子)
// 返回值:也是一个伪数组
// 获取第一个子元素节点
console.log(ul.children[0]);
// 获取最后一个子元素节点
console.log(ul.children[ul.children.length - 1]);
3.兄弟节点
node.parentNode 父节点
node.children 子元素节点(伪数组)
node.previousElementSibling 前一个兄弟元素节点
node.nextElementSibling 后一个兄弟元素节点
4. 增加节点
先创建(元素)节点/标签,再追加给父元素( 先创建 再追加)
- 父元素.appendChild(要添加的元素)
- 父元素.insertBefore(插入的元素,放在哪个元素前面)
- insertBefore 与 appendChild不能同时对一个克隆对象进行操作
const ul = document.querySelector('ul')
const li = document.createElement('li')
li.innerHTML = '哈哈哈哈哈'
ul.appendChild(li)
// appendChild 是在父元素的最后追加
const li2 = document.createElement('li')
li2.innerHTML = '嘿嘿嘿嘿'
ul.insertBefore(li2, ul.children[0])
// insertBefore 是在指定元素的前面追加
5. 克隆节点
const copy_li1 = li1.cloneNode(true) // 参数为true 为深拷贝
面试题:深拷贝与浅拷贝
共同点:复制
1. 浅拷贝:只复制引用,而未复制真正的值。
var arr1 = ['a','b','c','d'];
var arr2 = arr1;
var obj1 = {a:1,b:2}
var obj2 = Object.assign(obj1);
2. 深拷贝:是复制真正的值 (不同引用)
var obj3 = {
a:1,
b:2
}
var obj4 = JSON.parse(JSON.stringify( obj3 ));
6. 删除节点
- node(父元素).removeChild(子元素)
- 只能删除父节点下面最近一层的子节点 不能跨级删除
十二、BOM
- BOM 浏览器对象模型, 把浏览器(顶级对象 window)当做一个对象
console.log(document === window.document) // true
- DOM 文档对象模型, 把页面文档当做一个对象
- 在全局作用域中, 用var声明的变量, 会成为window的属性;用const let 声明的变量,则不会成为window的属性
- 全局作用域中用function声明的函数,会成为window对象的方法;用const声明的函数不会成为window对象的方法
1. location对象
属性:
location.href 记录了URL的完整地址. 可读写 , 所以还可以设置==>可以跳转
e.preventDefault() 阻止默认提交 阻止表单的action跳转 但是不会阻止代码的执行 form.action='http://baidu.com'
location.search 获取"?xxxxx"
location.hash 获取的是"#xxxx"
方法
location.reload() 方法 重新加载, 页面刷新
location.reload(true) 表示强制刷新。 清空缓存
2. navigator对象
检测 userAgent(浏览器信息)
userAgent记录了浏览器配置信息, 可以检测是Android 还是 IOS, 是移动端,还是PC
;(function() {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}
})();
3. history对象
返回上一页面
history.back(-1):直接返回当前页的上一页,数据全部消息,是个新页面 history.go(-1):也是返回当前页的上一页,不过表单里的数据全部还在 history.back(-1) history.back(); //返回
前进一页面
history.back(1) 前进 history.go(1); //前进 history.forward(); //前进
4. localStorage 本地存储
面试题:
localStorage 和 sessionStorage 有什么区别?
1. 生命周期 2. 数据共享 3.localStorage 和 sessionStorage的存储大小, 都是5M
生命周期: 生命周期永久生效(数据一直存在), 除非手动删除, 否则关闭页面也会存在
- 生命周期: 一个对象的创建到清除的过程, 数据存在的周期
数据共享: 可以多窗口(页面)共享 , 同一个浏览器,同一个域名(地址)下
数据以键值对的形式存储使用 key value, 存的是字符串的形式
- 存数据 localStorage.setItem(key, value)
localStorage.setItem('user_name', '刘德华') // 如果key存在, 就是覆盖修改, 不存在, 就是新增一条数据
- 取数据 localStorage.getItem(key), 返回值就是我们存的value
const res = localStorage.getItem('user_name')
- 删数据 localStorage.removeItem(key)
localStorage.removeItem('user_name')
- 清空数据 localStorage.clear()
5. 本地存储复杂数据类型
复杂数据类型 在存入 localStorage / sessionStorage之前, 必须先转为字符串
JSON.stringify() : 将对象转换为JSON格式的字符串
localStorage.setItem('test_obj', JSON.stringify(obj))
JSON.parse() : 将JSON格式的字符串, 转换为对象形式。
const result = JSON.parse(localStorage.getItem('test_obj'))
6. sessionStorage 会话存储
生命周期: 关闭浏览器窗口, 数据清除
数据共享: 在同一个窗口(页面)下数据可以共享 (前提:同一个域名下)
数据以键值对的形式存储使用 key value, 存的是字符串的形式
- 存数据 sessionStorage.setItem(key, value)
sessionStorage.setItem('user_name', '刘德华') // 如果key存在, 就是覆盖修改, 不存在, 就是新增一条数据
- 取数据 sessionStorage.getItem(key), 返回值就是我们存的value
const res = sessionStorage.getItem('user_name')
- 删数据 sessionStorage.removeItem(key)
sessionStorage.removeItem('user_name')
- 清空数据 sessionStorage.clear()
十三、事件循环
1. JS是**单线程**的语言:
也就是同一时间只能做一件事件(同步), 所有的工作的顺序执行
- 代码是从上到下,依次执行的, 逻辑是一步一步完成
why JS是单线程?
- JS设计之初, 就是为了做网页交互, 那么就会操作DOM元素
2. 线程和进程
进程是资源分配的最小单位, 线程是CPU调度的最小单位
进程(process) : 一个进程就是一个正在运行的程序(打开任务管理器)
线程(thread) : 一个进程内执行着的每一个任务 (录屏程序: 录声音,录画面)
- 线程是允许应用程序并发执行多个任务的一种机制
- 进程 ==> 火车 , 线程 ==> 车厢
两者联系:
- 线程必须依附于进程存在 (单独的车厢无法运行)
- 一个进程可以包含多个线程 (一个火车可以有多个车厢)
3. 同步和异步
同步: 同一时间只能执行一个任务, 上一个任务执行完, 才能执行下一个任务
异步: 可以同时执行多个任务,提高了程序的执行效率同步任务: 同步任务都在主线程上执行, 形成一个执行栈. 同步任务是依次执行的.
**异步任务:**JS的异步任务一般是通过回调函数来实现的, 在做某个任务的同时还可以处理其他任务
异步任务-常见的三种类型:
1. 事件: click, resize 监听 回调函数
2. 定时器: setTimeout / setInterval 回调
3. 资源加载: load DOMContentLoaded 回调
window.addEventListener('load', function(){})
我们把执行的任务又分为 : 宏任务 macroTask, 微任务 microTask
宏任务:
1. 整个script代码块
2. setTimeout
3. setInterval
4. setImmediate
微任务:
1. promise的回调 ==> promise.then() promise.catch()
2. async await
3. mutationObserver (vue源码)
4. process.nextTick (node)
每执行完一个宏任务, 就会清空当前的所有微任务队列.
JS执行机制, 事件循环EventLoop
4. JS执行机制
-
首先判断JS任务是同步任务, 还是异步任务. 同步任务会在主线程的执行栈中依次执行.
-
异步任务会提交给异步进程处理(setTimeout/ click事件等,此时异步进程也在执行相关的逻辑判断).
满足触发条件后, 异步进程会将异步任务(回调函数)放到任务队列(临时进行存放的一个空间)里面去
- 当主线程执行完所有的同步任务后, 会去任务队列中查看是否有可以执行的异步任务.
如果有, 就拿到主线程中执行(立即性). 执行完之后再去任务队列里面查找, 依次循环.
十四、正则表达式
1. 定义与作用
正则表达式是什么?
- 是用于匹配字符串中字符组合的模式
正则表达式有什么作用?
- 表单验证(匹配)
- 过滤敏感词(替换)
- 字符串中提取我们想要的部分(提取)
2. 判断字符串是否符合规则:test()
用来查看正则表达式与指定的字符串是否匹配
reg.test(str) // 返回一个布尔值
3. 检索符合规则的字符串:exec()
- 在一个指定字符串中执行一个搜索匹配
- 如果匹配成功,exec() 方法返回一个数组,否则返回null
4. 元字符
- 是一些具有特殊含义的字符
- 可以极大提高了灵活性和 强大的匹配功能
1. 边界符
- 提示字符所处的位置
边界符 说明 ^ 匹配行首的文本(以谁开始) $ 匹配行尾的文本(以谁结束) 如果 ^ 和 $ 在一起,表示必须是精确匹配
2. 量词
设定某个模式出现的次数
量词 说明 * >=0 + >=1 ? 1或者0 {n} =n {n,} >=n {n,m} >=n && <=m
3. 字符类
[ ] 匹配字符集合
后面的字符串只要包含 abc 中任意一个字符,都返回 true
/[abc]/.test('baby') // true /[abc]/.test('cry') // true /[abc]/.test('die') // flase
[ ]加上 - 连字符:表示一个范围
/[abc]/.test('abc') // true /^[abc]$/.test('a') // true /^[abc]$/.test('abc') // false
[ ] 里面加上 ^ 取反符号:匹配除了小写字母以外的字符
==> [^...] 里面写^表示取反, 不能是里面的任意一个字符. ==> /^xxx/ 如果写到正则的最前面, 表示边界符, 以什么开头
. 匹配除换行符之外的任何单个字符
/n./.test('an') // false /n./.test('na') // true /.n/.test('an') // true /.n/.test('ab+n') // true
预定义:某些常见模式的简写方式
预定类 说明 \d ==> [0-9] \D ==> [ ^ 0-9] \w [A-Za-z0-9_] \W [^A-Za-z0-9_] \s 匹配空格 \S 匹配非空格的字符
5. 修饰符
- i,正则匹配时字母不区分大小写
- g,匹配所有满足正则表达式的结果
- replace 替换
str.replace(/正则表达式/,'替换的文本')