浏览器

168 阅读13分钟

1. 浏览器运行态下的JS

JS = BOM + DOM + ECMAScript

我们来看一下他们的概念

  • DOM(Document Object Model):文档对象模型,描述了处理网页内容的方法和接口,根对象是document(window.document)
  • BOM(Browser Object Model):浏览器对象模型,描述了与浏览器进行交互的方法和接口,由location、navigator、history、screen、window五个对象组成,根对象是window

2. BOM

1. location:主要提供当前窗口中加载文档的信息以及导航功能

1. 保存URL信息

比如以这个url为例:www.juejin.cn/frontend?so…

属性值(字符串)说明
location.href完整地址
location.protocalhttps:页面使用的协议
location.hostnamewww.juejin.cn服务器名
location.pathnamefrontendURL路径与文件名
location.search?sort=newestURL查询字符串
location.hash#content哈希地址
location.port端口号

这里的location.search会获取到?之后的参数,不过是一个长长的字符串,很有可能需要我们将其处理成我们需要的格式,有一下两种方法

  • 利用URLSearchParams实例
let qs = location.search	// '?sort=newest&num=10'
let queryParams = new URLSearchParams(qs)

这个实例上暴露了:get()/set()/delete/has()/toString()等方法,也可以进行遍历,得到所有参数的key-value的数组

for(let item of queryParams){
	console.log(item)
}
//打印出两个数组
//['sort' , 'newest']
//['num' , '5']
  • 当然我们也可以自己手写一个函数
let search = '?sort=newest&num=10'
let qs = search.substring(1)    //去掉问号
let arr = qs.split('&').map(i => i.split('='))
console.log(arr);  // [ [ 'sort', 'newest' ], [ 'num', '10' ] ]

2. 操作地址

  • assign() : 跳转到新的URL,并增加一条历史记录
  • replace() : 跳转到URL,不增加历史记录
  • reload() : 重新加载当前页面;可以接收一个参数
location.reload() //重新加载当前页面,但可能从缓存加载(页面自上次请求以来没修改过时)
location.reload(true) 	//重新加载当前页面,从服务器加载

2. navigator:浏览器系统信息大集合

面试方向:

  • userAgent 读取信息=>判断浏览器兼容性、上报信息等操作
  • 剪切板、键盘等操作

3. history:存储当前页面的状态

面试方向:

  • 路由方向=>history和hash的模式利弊:

1. Hash模式

  • 定义:hash模式是一种吧前端路由的路径用#拼接在真实url后面的模式。当url发生变化时,不会重新发起请求,而是触发onhashchange事件。
  • 特点:
  1. hash的改变记录在window.history中,不利于SEO的优化。hash 只能修改 # 后面的部分,所以只能跳转到与当前 url 同文档的 url 。
  2. ash 通过 window.onhashchange 的方式,来监听 hash 的改变,借此实现无刷新跳转的功能。
  3. hash 永远不会提交到 server 端(可以理解为只在前端自生自灭)。

2. History模式

  • 先来了解几个api
  1. history.pushState(data, title [, url]) pushState主要用于往历史记录堆栈顶部添加一条记录。各参数解析如下:

①data会在onpopstate事件触发时作为参数传递过去;

②title为页面标题,当前所有浏览器都会忽略此参数;

url为页面地址,可选,缺少时表示为当前页地址

  1. history.replaceState(data, title [, url])

上述为添加,此处为更改

  1. history.state:存储data数据
  • history存在的问题

使用 history 模式时,在对当前的页面进行刷新时,此时浏览器会重新发起请求。如果 nginx 没有匹配得到当前的 url ,就会出现 404 的页面。

4. screen - 荧幕相关

面试方向 - 判断区域大小

1. window视窗判断 - 整个宽高

  • 全局入口处: window.innerHeight,window,innerWidth
  • 文本处获取: document.body(documentElement).clientHeight

2. 网页视图的size - 内容宽高

document.body(documentElement).offsettHeight

offsetHeight = clientHeight + 滚动条 + 边框

3. 整个内容的宽高(包括不在页面可滚动部分)

scrollWidth/scrollHeight

4. 判断距离

  • offsetTop : 距离常规上面的距离
  • scrollTop :距离上面滚动的距离

3. Event 事件类型

1.冒泡和捕获

<div id="app">
    <p id="dom"></p>
</div>
  • 冒泡 - ms:p => div => body => document
  • 捕获 - ns:document => body => div => p 对于冒泡和捕获,在addEventListener中有一个useCapture参数来选择。
el.addEventListener(event, function, useCapture) //默认为false - 冒泡
// 追问:1. 如何阻止事件的传播?
// 1. 阻止事件传递(可以是冒泡,也可以是捕获)
event.stopPropagation() //阻止传递行为 => 无法阻止默认事件
// 2. 阻止默认事件 - a标签这种
event.preventDefault()
// 3. 阻止事件传递,并且阻止后续再监听相同类型的事件
event.stopImmediatePropagation

2. 手写兼容IE的addEventListener

IE - attachEvent vs addEventListener 区别:

  • 传参:attachEvent 对于事件名都要加上on
  • 执行顺序:attachEvent - 后绑定先执行;addEventListener - 先绑定先执行;
  • 解绑:detachEvent VS removeEventListener()
  • 阻断:event.cancelBubble = true VS event.stopPropagation()
  • 默认事件拦截:event.returnValue = false VS event.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(this.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(this.element)
            })
        } else {
            this.element['on' + type] = null
        }
    }
    // 阻断
    static stopPropagation(e){
        if(e.stopPropagation){
            e.stopPropagation()
        } else {
            e.cancelBubble = true
        }
    }
    // 默认事件拦截
    static preventDefault(e){
        if(e.preventDefault){
            e.preventDefault()
        } else {
            e.returnValue = false
        }
    }
}

3. 性能优化 - 事件代理

<ul class="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul> 
//当ul中有多个li,我们要给li添加点击事件,要使用循环给每一个li添加点击事件,这样性能非常低。
var list = document.querySelector('list');
var li = list.getElementsByTagName('li');

for(var n = 0; n < li.length; n++) {
    li[n].addEventListener('click', function() {
        // 业务逻辑
    })
}
//所以我们可以使用事件代理,通过li冒泡到list触发点击事件
function onClick(e) {
    var e = e || window.event;

    if(e.target.nodeName.toLowCase() === 'li') {
        // 业务逻辑
        var liList = this.querySelectorAll('li');
        // ……
    }
}

list.addEventListener('click', onClick, false)

4. 网络层

1. ajax

// 1.实例化
const xhr = new XMLHttpRequest();

// 2. 初始化建立
xhr.open(method,url,async) // get/post请求;地址;是否为异步请求

// 3. 方法的发送
xhr.send(data) //get - 可以不传或传null;post - encodeURIComponent编码拼接

// 4. 接收
xhr.onreadystatuschange = () => {
    if(xhr.readyStatus === 4) {
        // 判断http状态码
        if(xhr.status >= 200 &&
            xhr.status < 300 ||
            xhr.status == 304) {
                // xhr.responseText
        } else {
            // xhr.statusText
        }
    }
};
// 超时时间
xhr.timeout = 30000
xhr.ontimeout = () => {
    // 超时后
}
// 报错
xhr.onerror = function (e){
    // xhr.statusText
}

2. XMLHttpRequest的实例属性

1. readyState - 状态

XMLHttpRequest.readyState返回一个整数,表示实例对象的当前状态。该属性只读。它可能返回以下值。

  • 0,表示 XMLHttpRequest 实例已经生成,但是实例的open()方法还没有被调用。

  • 1,表示open()方法已经调用,但是实例的send()方法还没有调用,仍然可以使用实例的setRequestHeader()方法,设定 HTTP 请求的头信息。

  • 2,表示实例的send()方法已经调用,并且服务器返回的头信息和状态码已经收到。

  • 3,表示正在接收服务器传来的数据体(body 部分)。这时,如果实例的responseType属性等于text或者空字符串,responseText属性就会包含已经收到的部分信息。

  • 4,表示服务器返回的数据已经完全接收,或者本次接收已经失败。 当readyState发生变化,就会触发readyStateChange事件

2. onreadyStateChange - 监听函数

XMLHttpRequest.onreadystatechange属性指向一个监听函数。实例的readyState属性变化,就会执行这个属性。

3. response - 返回所有数据

XMLHttpRequest.response属性表示服务器返回的数据体(即 HTTP 回应的 body 部分)。它可能是任何数据类型,比如字符串、对象、二进制对象等等,具体的类型由XMLHttpRequest.responseType属性决定。XMLHttpRequest.response属性是只读的。

4. responseType - 返回数据类型

XMLHttpRequest.responseType属性是一个字符串,表示服务器返回数据的类型。这个属性是可写的,可以在调用open()方法之后、调用send()方法之前,设置这个属性的值,告诉浏览器如何解读返回的数据。如果responseType设为空字符串,就等同于默认值text,它还可以等于以下值。

  • ""(空字符串):等同于text,表示服务器返回文本数据。

  • "arraybuffer":ArrayBuffer 对象,表示服务器返回二进制数组。

  • "blob":Blob 对象,表示服务器返回二进制对象。

  • "document":Document 对象,表示服务器返回一个文档对象。

  • "json":JSON 对象。

5. responseText,responseXML,responseURL - 接收的不同数据类型

XMLHttpRequest.responseText属性返回从服务器接收到的字符串,该属性为只读。只有 HTTP 请求完成接收以后,该属性才会包含完整的数据。

XMLHttpRequest.responseXML属性返回从服务器接收到的 HTML 或 XML 文档对象,该属性为只读。如果本次请求没有成功,或者收到的数据不能被解析为 XML 或 HTML,该属性等于null。这要求在发送请求前,XMLHttpRequest.responseType属性要设为document

XMLHttpRequest.responseURL属性是字符串,表示发送数据的服务器的网址。

6. status,statusText - http状态

XMLHttpRequest.status属性返回一个整数,表示服务器回应的 HTTP 状态码。状态码的相关内容会在下面单独开列出。

XMLHttpRequest.statusText属性返回一个字符串,表示服务器发送的状态提示。

7. timeout, ontimeout

XMLHttpRequest.timeout属性返回一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。

XMLHttpRequest.timeout属性返回一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。

3. XMLHttpRequest的监听属性

XMLHttpRequest 对象可以对以下事件指定监听函数。

  • onloadstart:HTTP 请求发出的监听函数
  • onprogress:正在发送和加载数据的监听函数
  • onabort:请求中止,比如用户调用了abort()方法的监听函数
  • onerror:请求失败的监听函数
  • onload:请求成功完成的监听函数
  • ontimeout:超时的监听函数
  • onloadend:请求完成(成功或失败)的监听函数

4. XMLHttpRequest的实例方法

1. open()

2. send()

3. setRequestHeader() - 设置请求头

XMLHttpRequest.setRequestHeader()方法用于设置浏览器发送的 HTTP 请求的头信息。该方法必须在open()之后、send()之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。

xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));

4. getResponseHeader(), getAllResponseHeaders() - 得到请求头信息

XMLHttpRequest.getResponseHeader("balabala")方法返回 HTTP 头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,返回null。该方法的参数不区分大小写。如果有多个字段同名,它们的值会被连接为一个字符串,每个字段之间使用“逗号+空格”分隔。

5. abort() - 终止请求

XMLHttpRequest.abort()方法用来终止已经发出的 HTTP 请求。调用这个方法以后,readyState属性变为4status属性变为0

5. ajax封装手写

ajax({
    url: 'reqUrl',
    method: 'get',
    async: true,
    timeout: 30000,
    data: {info:'lkh'}
}).then(
    res => {},
    err => {}
).catch(err => {})
function ajax (options){
    let {url,method,async,timeout,data} = options
    const xhr = new XMLHttpRequest()
    // 判断是否有timeout
    if(timeout){
        xhr.timeout = timeout
    }
    return new Promise((resolve,reject) => {
        xhr.onreadystatechange = () => {
            if(xhr.readyStatus === 4){
                if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
                    resolve(xhr.responseText)
                } else {
                    reject(xhr.statusText)
                }
            }
        }
        // 定义失败的回调
        xhr.onerror = err => {
            reject(err)
        }
        xhr.ontimeout = () =>{
            reject('timeout')
        }

        //传参
        let _params = []
        let encodeData = ''
        for(let key in data){
            _params.push(encodeURIComponent(key)  + '=' + encodeURIComponent(data[key]))
        }
        encodeData = _params.join('&')

        //method为get拼接url
        if(method === 'get'){
            const index = url.indexOf('?')
            if(index === -1){
                url += '?'
            } else if(index !== url.length - 1){
                url += '&'
            }
            url += encodeData
        }

        //open
        xhr.open(method,url,async)

        //send
        if(method === 'get'){
            xhr.send(null)
        } else {
        xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')
            xhr.send(encodeData)
        }
    })
}

6. 常见的状态码

  • 100 Continue 继续。客户端应继续其请求

  • 200 OK 请求成功。一般用于 GETPOST 请求
  • 201 Created 已创建。成功请求并创建了新的资源
  • 202 Accepted 已接受。已经接受请求,但未处理完成
  • 204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
  • 205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
  • 206 Partial Content 部分内容。服务器成功处理了部分 GET 请求

  • 301 Moved Permanently 永久重定向。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的URI代替
  • 302 Found 临时重定向。与 301 类似。但资源只是临时被移动。客户端应继续使用原有URI
  • 303 See Other 查看其它地址。与 301 类似。使用 GETPOST请求查看
  • 304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
  • 305 Use Proxy 使用代理。所请求的资源必须通过代理访问
  • 307 Temporary Redirect 临时重定向。与 302 类似。使用 GET 请求重定向

  • 400 Bad Request 客户端请求的语法错误,服务器无法理解
  • 401 Unauthorized 请求要求用户的身份认证
  • 402 Payment Required 保留,将来使用
  • 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
  • 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。
  • 405 Method Not Allowed 客户端请求中的方法被禁止
  • 406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
  • 407 Proxy Authentication Required 请求要求代理的身份认证,与 401 类似,但请求者应当使用代理进行授权
  • 408 Request Time-out 服务器等待客户端发送的请求时间过长,超时
  • 409 Conflict 服务器完成客户端的 PUT 请求是可能返回此代码,服务器处理请求时发生了冲突
  • 410 Gone 客户端请求的资源已经不存在。410 不同于 404,如果资源以前有现在被永久删除了可使用 410 代码,网站设计人员可通过 301 代码指定资源的新位置
  • 411 Length Required 服务器无法处理客户端发送的不带 Content-Length 的请求信息
  • 412 Precondition Failed 客户端请求信息的先决条件错误
  • 413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个 Retry-After 的响应信息
  • 414 Request-URI Too Large 请求的 URI 过长(URI 通常为网址),服务器无法处理
  • 415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
  • 416 Requested range not satisfiable 客户端请求的范围无效
  • 417 Expectation Failed 服务器无法满足Expect 的请求头信息

  • 500 Internal Server Error 服务器内部错误,无法完成请求
  • 501 Not Implemented 服务器不支持请求的功能,无法完成请求
  • 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
  • 503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中
  • 504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
  • 505 HTTP Version not supported 服务器不支持请求的 HTTP 协议的版本,无法完成处理

7. content-type内容类型

1. 响应头

在响应中常见的Content-Type有:

  • application/json ajax请求数据返回json
  • application/javascript javascript文件
  • text/css css文件(注意与js不同的是这里是text,application通常表示可执行或可解析的二进制数据)
  • text/html html文件
  • text/plain 纯文本

2. 请求头

  • 使用form表单发送给请求,通常使用application/x-www-form-urlencoded
  • 对于二进制数据、文件、非ASCII字符,应使用multipart/form-data
  • json数据,使用application/json

5. 浏览器原理