浏览器内置 JS 对象详解 & 浏览器事件模型详解 & 浏览器请求相关内容详解

85 阅读4分钟

一、认识浏览器运行态下的JS

包含:BOM、DOM、ECMAScript
   简述:
   ECMAScript:基础逻辑、数据处理
   DOM:对于浏览器视窗内html文件的相关处理和操作
   BOM:对浏览器本身功能区域做处理
   (function(context, undefined) {
      const _class = ['js','vue','react'];
      // 向全局作用域中进行存储
      window.classArr = _class;
      // 获取当前页面地址
      const url = location.herf;
      // 获取渲染节点
      document.getElementById('app');
      // 获取页面标题
      document.title = 'hello world';
   })(this,undefined)

二、BOM

1、location
   location.href = 'http://www.baidu.com'; // 获取当前页面完整url
   location.protocol = 'http'; // 获取当前协议
   location.host = 'www.baidu.com'; // 获取当前主机名+ 端口
   loaction.hostname = 'www.baidu.com'; // 获取当前主机名
   location.port = '80'; // 获取当前端口号
   location.pathname = '/index.html'; // 获取当前路径
   location.search = '?name=zhangsan'; // 获取查询参数部分(以问号开头)
   location.hash = '#/home'; // 获取hash值--锚点部分 

   location.assign(url); // 跳转到指定页面,替换pathname的值
   location.reqlace(url); // 跳转到指定页面,同时会替换浏览历史
   location.reload(); // 重新加载
   location.toString(); // 当前完整地址字符串输出
  • 面试方向: 1、location本身api的操作 2、路由相关:跳转、参数、操作 => 场景: 返回(history)、刷新(hash) 3、url处理 - 正则 | js解析
2、history

history.pushState() // 跳转到指定状态页面 history.replaceState() // 替换当前状态

  • history与hash模式利弊 1、hash模式:浏览器兼容性更好,当#号后面路径发生变化时,浏览器不会重新发起请求,但会触发hashchange事件;书写不美观,同时服务端无法准确捕获路由信息 2、history模式:可读性和语义化更强,服务端可以准确捕获路由信息,但浏览器兼容性差,需要服务端配合,同时浏览器会重新发起请求,但会触发popstate事件
3、navigator
  • 浏览器系统信息大集合
   navigator.userAgent // 获取当前用户
  • 面试 1、userAgent 读取信息 => 浏览器兼容性(如何解析和检测不同浏览器及设备) 2、剪切板、键盘;
4、screen

表示显示区域的参数 - 屏幕

  • 面试 - 对于区域的判断window视窗判断: 全局入口处:window.innerWidth / innerHeight 文本处进行获取:document.documentElement.clientHeight / clientWidth | document.body.clientHeight / clientWidth 网页的size => offsetHeight = clientHeight + 滚动条 + 边框 document.documentElement.offsetHeight / offsetWidth | document.body.offsetHeight / offsetWidth 定位:scrollLeft / scrollTop -- 距离常规左 / 上; 滚动距离 offsetLeft / offsetTop -- 距离常规左 / 上 局对距离 el.getBoundingClientRect() // 获取元素在视窗内的位置信息 top: 元素上边到视窗上边的距离; left: 元素左边到视窗左边的距离; bottom: 元素下边到视窗下边的距离; right: 元素右边到视窗右边的距离;
    • 兼容性:IE会多出两个像素

三、事件模型

   <div id="app">
      <p id="dom">click</p>
   </div>
   // 冒泡:p => div => body => html => document -- 逐级向上触发
   // 捕获:html => document => body => div => p -- 逐级向下触发
   el.addEventListener(event, function, useCapture);
   // 1、如何阻止事件传播
   event.stopPropagation(); // 注:无论向上还是向下都可以阻止 => 无法阻止默认事件的发生,如 a 标签的跳转
   // 2、如何阻止默认事件的传播
   event.preventDefault();
   // 3、相同节点绑定多个同类事件,如何阻止
   event.stopImmediatePropagation();
   // 4、兼容性
   // addEventListtener vs IE - attachEvent
   // 区别:
   a.传参 attachEvent 对于所有的事件名都要加上 on
   b.执行顺序,attachEvent - 后绑定先执行:addEventListener - 先绑定先执行
   c.解绑:detachEvent 对比 removeEventListener
   d. e.cancelBubble = true; vs e.stopPropagation(); // 阻止事件传播
   e.阻止默认事件 e.returnValue vs e.preventDefault(); // 阻止默认事件

   // 手写统一事件绑定 兼容IE
   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) {
            const element = this.element;
            this.element.attachEvent('on' + type, () => {
               handler.call(element)
            })
         }
      }
      // 移除事件
      removeEventListener(type, handler) {
         if(this.element.removeEventListener) {
            this.element.removeEventListener(type,handler,false)
         }else if(this.element.detachEvent) {
            const element = this.element;
            this.element.detachEvent('on'+type, () => {
               handler.call(element);
            })
         }
      }
      // 阻止事件传播(冒泡)
      stopPropagation(e) {
         if(e.stopPropagation) {
            e.stopPropagation();
         }else {
            e.cancelBubble = true;
         }
      }
      // 阻止默认事件
      preventDefault(e) {
         if(e.preventDefault) {
            e.preventDefault();
         }else {
            e.returnValue = false;
         }
      }
   }

   性能优化 -- 事件代理
   <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
   </ul>
   <div class="context"></div>

   const ul = document.querySelector('ul');
   const list = ul.getElementsByTagName('li');
   const context = document.querySelector('.context');
   // 循环代理
   for(let i = 0; i < list.length; i++) {
      list[i].addEventListener('click', function() {
         console.log(this.innerHTML);
      }
   }
   // 代理后--利用冒泡
   function proxy(e) {
      let e = e || window.event;
      if(e.target.nodeName.toLowerCase === 'li') {
         const liList = this.querySelectorAll('li');
         let index = Array.prototype.indexOf.call(liList, e.target);
         console.log(e.target.innerHTML);
      }
   }

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

四、网络层

   // 实例化
   const xhr = new XMLHttpRequest();
   // 初始化连接
   xhr.open(method, url, async);
   // 发送请求
   xhr.send(data);
   // 接收统计
   xhr.readyStatus; // 0 > 尚未调用open ,1 > 已经调用open,2 > 已经调用send, 3 > 已接收到请求,4 > 请求完成
   // 接收回调
   xhr.onrenadystatechange = () => {
      if(xhr.readyState === 4) {
         if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
            console.log(xhr.responseText);
         }
      }
   }
   // 设置超时
   xhr.timeout = 1000;
   xhr.ontimeout = () => {
      console.log('超时');
   }
  • 面试 1、RESTFUL(接口规范):GET | POST | PUT | DELETE 2、跨域:代理(proxy)、JSONP、CORS、iframe 3、状态码:2xx、400、500、3xx => 浏览器缓存: 强缓存(expire + cache-control) | 协商缓存(last-modified + E-tag)

#### 浏览器原理

   1DOM
   2CSSOM 浏览器将css解析成树形的数据结构
   3、render Tree 合并后生成具有样式 + 元素 + 层级解析树
   4Layout 计算出每个节点要渲染的位置
   5Painting 渲染

   // 重排 | 重绘
   reflow 元素几何尺寸发生了变化,需要重新计算layout tree
   repaint 某个元素的背景、颜色、边框颜色
   display: none => reflow
   visibility: hidden => repaint