前言:本系列主要是前端高阶课程的笔记整理,所以在讲述知识点的同时会延伸映射一些面试题,内容之间有点跳跃,但是全文的知识点还是循序渐进的。阅读本文你将了解认识在浏览器运行态下的JS,包括
-
BOM
-
浏览器原理
一、 认识在浏览器运行态下的JS
在开始之前笔者先帮大家整理了相应的知识点概念逻辑,方便各位理解
BOM:Browser Object Model 是浏览器对象模型,浏览器对象模型提供了独立与内容的、可以与浏览器窗口进行互动的对象结构,BOM由多个对象构成,其中代表浏览器窗口的window对象是BOM的顶层对象,其他对象都是该对象的子对象。
DOM:当网页被加载时,浏览器会创建页面的文档对象模型
(function(context, undefined){
const _class = ['js', 'browser', 'vue']
// 向全局中挂载
window.classArr = _class.map(item => item)
// 获取当前页面地址
const _url = location.href
// 设置tab标题
document.title = 'zhaowa class'
// 获取渲染节点
document.getElementById('app')
})(this)
window是我们浏览器的进程中唯一挂载的一个窗口
location是我们用户当前存在的一个位置,或者说是地址document指我们可视范围内的文本通过上面的代码可以看到,不光有执行环境,还有我们跟用户的交互,和文本的交互,
其实可以简述成下面的面试题
面试题: 了解浏览器JS的执行态
Q: // 简述:
// ECMAScript - 基础逻辑、数据处理
// DOM - 对于浏览器视窗内,文本的相应操作
// BOM - 对于浏览器本身区域能力的处理
今天主要的是讲一下BOM
BOM
先在控制台打印一下我们的location,了解一下我们的location有哪些东西
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uo23TbWU-1652062986396)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01cbf167e548461e83ef77afd8f4716b~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
location.href => 'https://www.baidu.com/search?class=browser#comments' => 路径栏所有
.orgin => 'https://www.baidu.com'
.protocol => 'https:'
.host => 'www.zhaowa.com'
.port => ''
.pathname => '/search/'
.search => '?class=browser&id=2' //不包含哈希
.hash => '#comments'

以上是location的一些API
除此之外,location还有一些方法
.assign('') // 跳转到指定path => 替换pathname
.replace('') // 同上,同时替换浏览历史
.reload() //重新载入当前文档
.toString() // 产出当前地址字符串
面试方向:
*** location本身api操作 – 提取相关信息、api间对比 => assign vs replace
- *路由相关: 跳转、参数、操作 => 场景:面试官问你,从列表页面进到详情页面,再点击详情页面的某个模块的详情页,然后在改模块点击返回,但是此时返回到的不是第一个详情页,而是列表页面,请问,为什么会出现这种页面返回不对的情况** 记住:凡是出现页面返回不对的问题,就是跟history有关,要么就是第二次跳转调用了replace()直接清空了, 要么就是直接整个路由重置了
**避免跳转异常方法:**是否刷新(hash)=> replace替换assign、携带参数
- url处理 - 正则 or 手写js处理
- URI & URL: uniform resource identifier / locator
Q: URI是统一资源标识符。标识资源详细名称。
URL是统一资源定位器。定位资源的网络位置。
资源:可以通过浏览器访问的信息统称为资源。(图片、文本、HTML、CSS等等。。。)
URI标识资源的详细名称。包含资源名。即文件id
URL定位资源的网络位置。包含http:。即路径位置
可以说URL是URI(URL是URI的子集),但URI永远不能是URL。
hash和history的区别
-
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;hash路由一个明显的标志是带有
**#**,我们主要是通过监听url中的hash变化来进行路由跳转。hash的优势就是兼容性更好,在老版IE中都有运行,问题在于url中一直存在#不够美观,而且hash路由更像是Hack而非标准,相信随着发展更加标准化的History API会逐步蚕食掉hash路由的市场。 -
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
-
history会出现后台解析不了的情况,因为他跟真实的路由是一样,所以我们需要给后台一个默认指向
navigator
我们试着在控制台打印一下

可以把他理解为浏览器系统信息大集合
navigator.userAgent //获取当前用户的环境信息
面试方向 :
-
userAgent 读取信息 => 浏览器兼容性、上报信息
-
剪切板、键盘
screen
表征显示区域 – 荧幕
面试方向(初级工程师常考) - 视图判断
全局入口处:
window.innerHeight
window.innerWidth
文本处获取:
document.documentElement.clientHeight
document.documentElement.clientWidth
document.body.clientWidth
document.body.clientWidth
网页视图的size -> offsetHeight = clientHeight + 滚动条 + 边框
document.documentElement.offsetHeight
document.documentElement.offsetWidth
document.body.offsetHeight
document.body.offsetWidth
动态定位:
scrollLeft / scrollTop - 距离常规左 / 上滚动距离
offsetLeft / offsetTop - 距离常规左 / 上距离
Element.getBoundingClientRect() //方法返回元素的大小及其相对于视口的位置
Element.getBoundingClientRect().top
Element.getBoundingClientRect().left
Element.getBoundingClientRect().bottom
Element.getBoundingClientRect().right
兼容性 - IE是会多出来2像素
相关文档 developer.mozilla.org/zh-CN/docs/…
Event事件模型
<div id="app">
<p id="dom"></p>
</div>
// 冒泡 - ms: p => div => body => HTML => document
// 捕获 - ns: document => HTML => body => div => p
el.addEventListener(event, function, useCapture) // 默认值false
// 追问:
// 1. 如何阻止时间的传播
event.stopPropgation()
// 注意:阻止传递行为 => 无法阻止默认事件
// 2. 阻止默认事件 - a
event.preventDefault()
// 3. 相同节点绑定多个同类事件
event.stopImmediatePropagation()
引申型面试核心: 兼容性 & 性能
4. 手写兼容性事件绑定
实际上面试官就是要考我们IE - attachEvent vs addEventListener两者区别
区别:
a. 传参:attachEvent 对于事件名需要加上'on'
b. 执行顺序:attachEvent - 后绑定先执行; addEventListener - 先绑定先执行
c. 解绑:detachEvent vs removeEventListener
d. 阻断:event.cancelBubble = true vs event.stopPropgation()
e. 默认事件拦截:event.returnValue = false vs event.preventDefault()
按照上面的步骤,我们就来手写一下兼容性事件绑定
兼容性事件绑定
先写出我们整体的框架,然后再去补充里面的内容
class bindEvent{
constructor(element){
this.element = element;
}
//绑定
addEventListener = (tyype,handler) =>{
}
//解绑
removeEventListener = (type,handler) =>{
}
//阻断
static stopPropgation(e) =>{}
//默认拦截
static preventDefault(e){}
}
详细版本
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.removeEventListener) {
this.element.removeEventListener(type, handler, false)
} else if(this.element.detachEvent) {
this.element.detachEvent('on' + type, () => {
handler.call(element);
})
} else {
this.element['on' + type] = null;
}
}
// 阻断
static stopPropgation(e) {
if (e.stopPropagation) {
e.stopPropagation()
} else {
e.cancelBubble = true;
}
}
// 默认拦截
static preventDefault(e) {
if(e.preventDefault) {
e.preventDefault()
} else {
e.returnValue = false;
}
}
}
性能优化部分 – 事件代理
<ul class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<div class="content"></div>
将以上代码进行事件绑定,我们先用for循环方法进行绑定
var list = document.querySelector('.list');
var li = list.getElementsByTagName('li');
// 硬碰硬
for(var n = 0; n < li.length; n++) {
li[n].addEventListener('click', function() {
// 业务逻辑
})
}
这种方法的缺点在于,会对每一个dom节点进行监听,后期如果这些节点是动态添加的,那么性能的损耗就更大了
这时,我们可以采用事件代理的方式进行优化
//代理后,利用事件传递,优化的方向在于减少对DOM的操作和减少对事件的绑定
function onClick(e) {
//兼容
var e = e || window.event;
if(e.target.nodeName.toLowCase() === 'li') {
// 业务逻辑
var liList = this.querySelectorAll('li');
// ……
}
}
list.addEventListener('click', onClick, false)
addEventListener()方法第一个参数是事件的类型 (如 "click" 或 "mousedown").第二个参数是事件触发后调用的函数。第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
addEventListener方法可参考这篇文章
网络层
如果你还不了解XMLHttpRequest,可以先看一下这篇文章
// 实例化
const xhr = new XMLHttpRequest();
//初始化建立
xhr.open(method, url, async) // get/post; 请求的地址; 是否为异步请求
// 方法的发送请求 - send
xhr.send(data) // get - 可以不传或传入null,post - encodeURIComponent编码拼接
// 接收
// xhr.readyStatus - 0 - 尚未建立open;1 - 已经调用open; 2 - 已经调用send; 3 - 已经收到请求返回; 4- 请求已经完成
xhr.onreadystatuschange = () => {
if(xhr.readyStatus === 4) {
// 判断http状态码
if(xhr.status >= 200 &&
xhr.status < 300 ||
xhr.status == 304) {
// xhr.responseText
}
}
}
// 超时时间
xhr.timeout = 30000
xhr.ontimeout = () => {
// 超时后
}
面试方向
// 1、TCP => HTTP/HTTPs
// 2、状态码 => 2xx 4xx 5xx | 3xx => 浏览器缓存 => 强缓存(Expires + cache-control) / 协商缓存(last-modified + Etag)
常问的是3开头的状态码,面试常常会引申浏览器缓存,埋个坑,下次性能优化篇章我们详细讲
强缓存(Expires + cache-control)
协商缓存(last-modified + Etag)
关于两者的区别,可参考这篇文章

对于中高级的面试,我们就不仅仅是要了解上面的内容,往往还需要我们封装手写
封装手写
实现Ajax的封装方式
ajax({
url: 'reqUrl',
method: 'get',
async: true,
timeout: 30000,
data: {
payload: 'text'
}
}).then(
res => {}
err => {}
).catch(err => {})
实现
options为我们上面代码传入的配置
// 实现: function ajax(options) { const { url, method, async, data, timeout } = options; const xhr = new XMLHttpRequest() // 配置超时事件 if (timeout) { xhr.timeout = timeout; } return new Promise((resolve, reject) => { // 成功 xhr.onreadystatuschange = () => { if(xhr.readyStatus === 4) { // 判断http状态码 if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { // 返回拦截器 resolve(xhr.responseText) } else { reject() } } } // 失败 xhr.onerror = err => reject(err) xhr.ontimeout = () => reject('timeout') // 传参处理 let _params = [] let encodeData = '' if (data instanceof Object) { for(let key in data) { // 参数编码 _params.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])) } encodeData = _params.join('&') } // method判断连接 if (method === 'get') { const index = url.indexOf('?') if(index === -1) { url += '?' } else if(index !== url.length -1) { url += '&' } url += encodeData } // 建立连接 xhr.open(method, url, async) // 请求拦截器…… // 发送请求,区分get和post if (method === 'get') { xhr.send(null) } else { // post xhr.setRequestHeader( 'content-type','application/x-www-form-urlencoded' ) xhr.send(encodeData) } }) }
面试点: content-type => 内容类型 => 浏览器 => 兼容firefox chrome
content-type会指向我们的内容类型,
如果后台没有指定content-type,firefox会默认指定为文本类型
而chrome会默认为文件类型,所以会出现一种情况就是你在下载的时候有时候在谷歌可以下载,在火狐下载不了的情况
感谢大家看到最后,也希望大家能够多多给出意见,互相学习互相进步
xhr.setRequestHeader( 'content-type','application/x-www-form-urlencoded' ) xhr.send(encodeData) } }) }
面试点: content-type => 内容类型 => 浏览器 => 兼容firefox chrome
content-type会指向我们的内容类型,
如果后台没有指定content-type,firefox会默认指定为文本类型
而chrome会默认为文件类型,所以会出现一种情况就是你在下载的时候有时候在谷歌可以下载,在火狐下载不了的情况
感谢大家看到最后,也希望大家能够多多给出意见,互相学习互相进步
145

被折叠的 条评论
为什么被折叠?



