笔记5—浏览器相关

66 阅读3分钟

一、浏览器运行态下的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() → 打印当前地址(字符串)

面试场景

  • 手写拆解路径相关题目

面试方向

  1. location本身的api操作
  2. 路由相关:跳转(push、replace)、参数(query、params)、操作(路由栈) ⇒ 场景:是否可返回、是否可刷新(vue刷新,会返回第一个页面)⇒ 框架
  3. url处理 → 手写处理、正则

2. history

history.state → 存错当前页面状态

history.pushState() → 跳转到指定状态页

history.replaceState() → 替换当前状态

面试方向

  1. 路由方向 historyhash 模式的利弊分析

    • 两种路由方式的考察
    • 路径与浏览器渲染机制的联系

3. navigator

浏览器的信息大集合

navigator.userAgent() → 获取当前用户的环境信息

面试方向

  1. UA读取信息 ⇒ 浏览器兼容性

    • chrome 和 firefox:事件监听方式
  2. 剪切板、键盘操作 ⇒ 登录 / 验证码

  3. 环境 ⇒ 微信 ⇒ unionId appid

4. screen

表示显示区域—屏幕

面试方向

  1. 区域、大小、方位判断

window 视窗判断:

  • 全局入口

    window.innerHeight

    window.innerWidth

  • 文本处获取

    document.documentElement.clientHeight

    document.documentElement.clientWidth

    document.body.clientHeight

    document.body.clientWidth

  • 网页的size

    offsetHeight = clientHeight + 滚动条 + 边框

    offsetWidth = clientWidth + 滚动条 + 边框

    document.documentElement.offsetHeight

    document.documentElement.offsetWidth

    document.body.offsetHeight

    document.body.offsetWidth

  • 定位

    scrollLeft / scrollTop → 距离常规左上的滚动距离

    offsetLeft / offsetTop → 距离常规左上的相对距离

    let rect = el.getBoundingRect()

    rect.top rect.right rect.bottom rect.left rect.width rect.height

    • 兼容性问题

    IE浏览器会多出2px

三、Event 事件模型

  • 事件冒泡
  • 事件捕获
el.addEventListener(event, function(){}, useCapture);

// userCapture: false 默认事件冒泡

面试方向

  1. 如何阻止事件传播

    event. stopPropagation()

    event.cancelBubble() // IE浏览器

    无论向上向下都可以阻止 ⇒ 但无法阻止默认事件的发生(譬如:a标签跳转)

  2. 如何阻止默认事件传播

    event.preventDefault()

  3. 如何阻止,相同节点绑定多个同类事件

    event.stopImmediatePropagation()

    eg: input 既绑定click事件,又绑定hover事件,点击时,会同时触发hover事件

  4. 手写事件绑定

    • attachEvent

    • addEventListener

      区别

      1. 传参

        attachEvent:事件名加上on

      2. 执行顺序

        attachEvent ⇒ 后绑定先执行

        addEventListener ⇒ 先绑定先执行

      3. 解绑

        attachEvent:detachEvent

        addEventListener:removeEventListener

      4. 阻断

        attachEvent: e.cancelBubble = true

        addEventListener: e.stopPropagation()

      5. 默认事件

        attachEvent: e.returnValue

        addEventListener: 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;
    		}
    	}
    }
    
  5. 性能优化—事件代理

事件代理本身与性能优化无关系,但确实能提升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 发送请求

    • 内容

      1. post:将请求体的参数传入
      2. 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);
    		}
    	})
    }