JS基础知识,只规定了语法(ECMA 262 标准)
JS Web Api,是网页操作的API(W3C标准)
前者时后者的基础,两者结合才能真正实际应用。
本文包括以下部分:
- DOM
- BOM
- 事件绑定
- Ajax
- 存储
一、DOM
题目
- DOM是哪种数据结构
- DOM操作常用的API
- attr和property的区别
- 一次性插入多个DOM节点,考虑性能
1. DOM是哪种数据结构
树型结构 DOM树
2. DOM操作常用的API
分为两类: DOM 节点操作和 DOM 结构操作
参考知识点2和3
3. attr和property的区别
- property:修改对象属性,不会体现到HTML结构中
- attribute:修改HTML属性,会改变HTML结构【一定】
- 两者都有可能引起DOM重新渲染
- 尽量使用property,因为DOM渲染是比较耗费性能的
4. 一次性插入多个DOM节点,考虑性能
const listNode = document.getElementById('list')
// 创建一个文档片段,此时还没有插入到 DOM 树中
const frag = document.createDocumentFragment()
// 执行插入
for (let x = 0; x < 10; x++) {
const li = document.createElement('li')
li.innerHTML = 'list item ' + x
frag.appendChild(li)
}
// 都完成之后,再插入到 DOM 树中
listNode.appendChild(frag)
知识点
1. DOM本质
- XML是可扩展的标记性语言,可以描述任何结构的数据
- HTML是特定的XML,规定了标签;结构和XML一致
- DOM本身是一棵树
2. DOM节点操作
获取DOM节点
- attribute
const pList = document.querySelectorAll('p')
const p = pList[0]
p.getAttribute('data-name')
p.getAttribute('data-name', 'imooc')
p.getAttribute('style')
p.getAttribute('style', 'font-size: 30px')
- property
以 js对象属性操作 的形式来操作DOM
const pList = document.querySelectorAll('p')
const p = pList[0]
p.style.width = '100px'
p.className = 'p1'
console.log(p.nodeName)
console.log(p.nodeType)
3. DOM结构操作
新增/插入节点
const div1 = document.getElementById('div1')
// 添加新节点
const p1 = document.createElement('p')
p1.innerHTML = 'this is p1'
div1.appendChild(p1) // 添加新创建的元素
// 移动已有节点
const p2 = document.getElementById('p2')
div1.appendChild(p2)
获取子元素列表,获取父元素
// 获取父元素
console.log(p1.parentNode)
// 获取子元素列表
const div1ChildNodes = div1.childNodes
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
if (child.nodeType === 1) {
return true
}
return false
})
console.log('div1ChildNodesP', div1ChildNodesP)
删除节点
const div1 = document.getElementById("div1")
const child = div1.childNodes
div1.removeChild(child[0])
4. DOM性能
DOM操作非常“昂贵”,避免频繁的DOM操作
对DOM查询做缓存
将频繁操作改为一次性操作
DOM查询做缓存
将频繁操作改为一次性操作
二、BOM
题目
- 如何识别浏览器的类型
- 分析拆解url各个部分
1. 如何识别浏览器的类型
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome)
2. 分析拆解url各个部分
location.href // 整个网址
location.protocol // 协议
location.host // 域名
location.pathname // 路径
location.search // 常用参数
location.hash // 哈希,# 后的内容
知识点
1. navigator
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome')
console.log(isChrome)
2. screen
3. location
location.href // 整个网址
location.protocol // 协议
location.host // 域名
location.pathname // 路径
location.search // 常用参数
location.hash // 哈希,# 后的内容
整个网址是由上面的信息拼接起来的
4. history
history.back() // 后退
history.forward() // 前进
三、事件
题目
- 编写一个通用的事件监听函数
- 描述事件冒泡的流程
- 无线下拉的图片列表,如何监听每个图片的点击
1. 编写一个通用的事件监听函数
注意代理问题 this问题
// 既支持普通绑定,也支持代理绑定
function bindEvent(elem, type, selector, fn) {
// 可传入3个参数,也可传入4个参数
if(fn == null){
fn = selector
selector = null
}
elem.addEventListener(type, event=>{
const target = event.target
if(selector){
// 代理绑定
if (target.matches(selector)) {
fn.call(target, event)
}
}else{
// 普通绑定
fn.call(target, event)
}
})
}
2. 描述事件冒泡的流程
- 基于 DOM 树型结构
- 事件会顺着触发元素向上冒泡
- 应用场景:代理
3. 无线下拉的图片列表,如何监听每个图片的点击
要点:
- 事件代理
- 用 e.target 获取触发元素
- 用 matches 来判断是否是触发元素
知识点
1. 事件绑定
// 普通的事件绑定
const btn = document.getElementById('btn1')
btn.addEventListener('click', event=>{
console.log('clicked')
})
// 较为通用的绑定函数
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
const a = document.getElementById('link1')
bindEvent(a, 'click', e=>{
e.preventDefault() // 阻止默认行为
alert('clicked')
})
2. 事件冒泡
3. 事件代理
事件代理参考
“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
优点:
- 代码简洁
- 减少浏览器内存占用
但是,不要滥用;可以应用于瀑布流
// html
<div id="div3">
<a href="#">a1</a><br>
<a href="#">a2</a><br>
<a href="#">a3</a><br>
<a href="#">a4</a><br>
<button>加载更多...</button>
</div>
// js
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', event=>{
event.preventDefault() // 阻止 a 的跳转的默认行为
const target = event.target
if(target.nodeName === 'A'){ // 判断是否是 a 标签
alert(target.innerHTML)
}
})
不好每个都挨个绑定事件,那么我们把事件绑定到父元素上,这就是事件代理。
四、Ajax
题目
- 手写一个Ajax,支持post get,支持404
- 跨域的常用实现方式
1. 手写一个Ajax,支持post get,支持404
2. 跨域的常用实现方式
- jsonp
- CORS(纯服务端)
知识点
1. XMLHttpRequest
XMLHttpRequest 对象用于在后台与服务器交换数据。并且可以
- 在不重新加载页面的情况下更新网页
- 在页面已加载后从服务器请求数据
- 在页面已加载后从服务器接收数据
- 在后台向服务器发送数据
所有现代的浏览器都支持XMLHttpRequest对象。
以下给出 get 请求和 post 请求。
// get 请求
const xhr = new XMLHttpRequest()
xhr.open('GET', 'data/test.json', true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
console.log(
alert(xhr.responseText)
}
}
}
xhr.send(null)
// post 请求
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if(xhr.status === 200) {
console.log(
alert(xhr.responseText)
}
}
}
const postData = {
userName: 'zhangdan',
password: 'xxx'
}
xhr.send(JSON.stringify(postData))
2. 状态码
readyState
- 0 (未初始化)还没有调用sell()方法
- 1(载入)已调用sell()方法,正在发送请求
- 2(载入完成)sell()方法执行完成,已经接收到全部响应内容
- 3(交互)正在解析响应内容
- 4(完成)响应内容解析完成,可以在客户端调用。所以能在客户端判断的readyState的值只能是4
responseStatus
- 2xx 表示成功处理请求,如200
- 3xx 需要重定向,浏览器直接跳转,如301 302 304
- 4xx 客户端请求错误,如404 403
- 5xx 服务器端错误
3. 跨域
同源策略
- ajax 请求时,浏览器要求当前网页和server必须同源(安全)
- 同源:协议、域名、端口,三者必须一致
- 前端
- 加载图片 CSS js 可以无视同源策略
<img src=跨域的图片地址> // 可用于统计打点,可使用第三方统计服务
-
<link herf=跨域的css地址>
<script src=跨域的js地址></script> // link script 可使用CDN,CDN一般都是外域 script 可实现JSONP
- 跨域
- 所有的跨域,都必须经过server端允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
跨域解决方案(待整理)
- JSONP
- CORS
五、存储
题目
- 描述cookie localStorage sessionStorage 区别
从以下三方面来进行描述:
- 容量
- API易用性
- 是否跟随HTTP请求发送出去
知识点
1. cookie
- 本身用于浏览器和server通讯
- 被“借用”到本地存储来
- 可用 document.cookie = '...' 来修改 追加的过程,不是覆盖的过程 同一个key是覆盖,不同的key是追加
cookie 的缺点
- 存储大小,最大为4KB
- http请求时需要发送到服务端,增加请求数据量
- 只能用 document.cookie = '...' 来修改,太过简陋
- 本身是来进行通信的,不是用来存储的
2. localStorage sessionStorage
- HTML5专门为存储而设计,对于每个域名来说最大可存5M
- API简单易用 setItem getItem
- 不会随着http请求被发送出去
- 强制类型转换成字符串形式
两者区别
- localStorage 数据会永久存储,除非代码或手动删除
- sessionStorage 数据只存在于当前会话,浏览器关闭则清空
- 所以,我们一般使用 localStorage