一、浏览器运行态下的JS
- BOM(browser object model 浏览器对象模型):对浏览器本身功能区域的汇总处理
- DOM(document object model 文档对象模型):对浏览器是窗内的HTML文本进行相关操作
- ESMAScript:处理基础逻辑及数据处理
二、BOM
1. location
location.href → “www.baidu.com/search?name…
404 是一个房间,每一个 / 就像是一扇门,每扇门进入不同的资源房间
location.origin → “www.baidu.com”
location.protocol → “https”
location.host → “www.baidu.com”
location.port → “” // 8080, 8090…
location.pathname → “/search”
location.search → “?name=kyle”
location.hash → “#post” // spa 单页面切换 / iframe
location.assign(${url}) → 跳转到指定的 url ,替换 pathname
location.replace(${url}) → 效果同 assign ,同时替换浏览历史
location.href == '/';
// 执行搜索:<https://taobao.com>
location.href == '<https://taobao.com>';
// 若
location.assign("<https://baidu.com>");
// 此时,点击后退,返回至 <https://taobao.com>
// 若
location.replace("<https://baidu.com>")
// 此时,点击后退,返回至 /
location.reload() → 页面刷新
location.toString() → 打印当前地址(字符串)
面试场景
- 手写拆解路径相关题目
面试方向
- location本身的api操作
- 路由相关:跳转(push、replace)、参数(query、params)、操作(路由栈) ⇒ 场景:是否可返回、是否可刷新(vue刷新,会返回第一个页面)⇒ 框架
- url处理 → 手写处理、正则
2. history
history.state → 存错当前页面状态
history.pushState() → 跳转到指定状态页
history.replaceState() → 替换当前状态
面试方向
-
路由方向
history和hash模式的利弊分析- 两种路由方式的考察
- 路径与浏览器渲染机制的联系
3. navigator
浏览器的信息大集合
navigator.userAgent() → 获取当前用户的环境信息
面试方向
-
UA读取信息 ⇒ 浏览器兼容性
- chrome 和 firefox:事件监听方式
-
剪切板、键盘操作 ⇒ 登录 / 验证码
-
环境 ⇒ 微信 ⇒ unionId appid
4. screen
表示显示区域—屏幕
面试方向
- 区域、大小、方位判断
window 视窗判断:
-
全局入口
window.innerHeightwindow.innerWidth -
文本处获取
document.documentElement.clientHeightdocument.documentElement.clientWidthdocument.body.clientHeightdocument.body.clientWidth -
网页的size
offsetHeight=clientHeight+ 滚动条 + 边框offsetWidth=clientWidth+ 滚动条 + 边框document.documentElement.offsetHeightdocument.documentElement.offsetWidthdocument.body.offsetHeightdocument.body.offsetWidth -
定位
scrollLeft/scrollTop→ 距离常规左上的滚动距离offsetLeft/offsetTop→ 距离常规左上的相对距离let rect = el.getBoundingRect()rect.toprect.rightrect.bottomrect.leftrect.widthrect.height- 兼容性问题
IE浏览器会多出2px
三、Event 事件模型
- 事件冒泡
- 事件捕获
el.addEventListener(event, function(){}, useCapture);
// userCapture: false 默认事件冒泡
面试方向
-
如何阻止事件传播
event. stopPropagation()event.cancelBubble() // IE浏览器无论向上向下都可以阻止 ⇒ 但无法阻止默认事件的发生(譬如:a标签跳转)
-
如何阻止默认事件传播
event.preventDefault() -
如何阻止,相同节点绑定多个同类事件
event.stopImmediatePropagation()eg: input 既绑定
click事件,又绑定hover事件,点击时,会同时触发hover事件 -
手写事件绑定
-
attachEvent
-
addEventListener
区别
-
传参
attachEvent:事件名加上
on -
执行顺序
attachEvent ⇒ 后绑定先执行
addEventListener ⇒ 先绑定先执行
-
解绑
attachEvent:
detachEventaddEventListener:
removeEventListener -
阻断
attachEvent:
e.cancelBubble = trueaddEventListener:
e.stopPropagation() -
默认事件
attachEvent:
e.returnValueaddEventListener:
e.preventDefault
-
class bindEvent(){ constructor(element){ this.element = element; } // 挂载在实例上 addEventListener = (type, handler) =>{ if(this.element.addEventListener){ this.element.addEventListener(type, handler, false); }else if(this.element.attachEvent){ this.element.attachEvent('on' + type, ()=>{ handler.call(element); }) }else{ this.element['on' + type] = handler; } } removeEventListener = (type, handler)=>{ if(this.element.addEventListener){ this.element.removeEventListener(type, handler, false); }else if(this.element.attachEvent){ this.element.detachEvent('on' + type, handler) }else{ this.element['on' + type] = null; } } // 挂载在全局上 static stopPropagation(e){ if(e.stopPropagation){ e.stopPropagation(); }else{ e.cancelBubble = true; } } static preventDefault(e){ if(e.stopPropagation){ e.preventDefault(); }else{ e.returnValue = true; } } } -
-
性能优化—事件代理
事件代理本身与性能优化无关系,但确实能提升dom操作的性能
<ul class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
// 远古硬刚写法
var list = document.querySelector('.list');
var li = list.getElementsByTag('li');
for(var i = 0; i < li.length; i++){
li[i].addEventListener("click", function(){
// callback 业务逻辑
})
}
// 事件代理--利用事件冒泡
function onClick(e){
var e = e || window.event;
if(e.target.nodeName.toLowerCase() === 'li'){
const liList = this.querySelectorAll('li');
index = Array.prototype.indexOf.call(liList, target);
}
}
list.addEventListener('click', onClick, false);
四、网络层
-
实例化
const xhr = new XMLHttpRequest(); -
初始化连接
-
xhr 有一个open方法
-
open 有五个参数
- method: get/post
- url: 请求地址
- async: 是否为异步请求(默认 true)
- user:可选用户名(默认 nul)
- password:可选密码(默认 null)
xhr.open(method, url, async); -
-
send 发送请求
-
内容
- post:将请求体的参数传入
- get:可以不传,或传入nul
xhr.send(data) -
-
接收
xhr.readyStatus- 0 ⇒ 尚未调用open
- 1 ⇒ 已调用open
- 2 ⇒ 已发送请求(已调用send)
- 3 ⇒ 已接收到请求返回数据
- 4 ⇒ 请求已完成
xhr.onreadystatuschange = () =>{ if(xhr.readyStatus === 4){ if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){ console.log('当前请求成功', xhr.responseText); } } } -
设置超时时间
xhr.timeout = 1000; xhr.ontimeout = ()=>{ console.log('请求超时'); } -
封装
ajax({ url: reqUrl, method: "get", async: true, timeout: 3*1000, data: { payload: "text" } }).then( res => console.log('成功' + res); err => console.log('失败' + err); )function ajax(options){ const {url, method, async, timeout, data} = options; const xhr = new XMLHttpRequest(); if(timeout && timeout>0){ xhr.timeout = timeout; } return new Promise((resolve, reject)=>{ // 成功 xhr.onreadystatuschange = () =>{ if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){ resolve && resolve(xhr.responseText); }else{ reject && reject(); } // 超时 xhr.ontimeout = () =>{ reject && reject('超时'); } // 失败 xhr.onerror = (err) =>{ reject && reject(err); } // 2. 传参处理 let _params = []; let encodeData; if(data instanceof Object){ for(let key in data){ // 参数拼接 _params.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])) } encodeData = _params.join('&'); } // get 处理 if(method === 'get'){ const index = url.indexOf('?'); if(index === -1){ url += '?'; }else if(index !== url.length - 1){ url += '&'; } url += encodeData; } // 3. 初始化连接 xhr.open(method, url, async); // 4. 发送 if(method === 'get'){ xhr.sned(null); }else{ xhr.setRequestHeader("Content-Type", 'application/x-www-form-urlencoded;chartset=UTF-8'); xhr.send(encodeData); } }) }