# 基础

# 布局

例题:三栏布局,左右各300px,中间自适应

多想一些方法,常见方式使用float、table、grid、flex、fixed去实现

# 单位
  • 1em,等于本元素的字体大小,所以在不同的元素里1em的绝对大小是不一样的。
  • 1rem,等于根元素的字体大小,在一个页面中,无论在哪个元素上1rem都是一样的。

em 适合于用在需要大小需要跟随字体变化的属性上,比如padding、margin、height、width等等,元素继承了不同的字体大小,这些属性最好也能跟着变化;

rem适用于字体,这样就可以通过改变根元素的字体大小来改变整个页面的字体大小。

# css盒模型

# 标准、ie模型

标准宽和高指的是content的宽度

ie模型中宽和高是指content、padding、border的总和

# css如何设置两种模型

标准:box-size:content-box;

ie:box-size:border-box;

# js如何获取和模型的对应的宽和高
  • dom.style.width :只能获取内联样式,无法获取style结点和外部进入的样式
  • dom.currentStyle.width:渲染出来的宽,只支持ie
  • window.getComputedStyle(dom).width:所有浏览器支持
  • dom.getBoundingClientRect().width:获得四个数组,分别是left、top、width、height
# BFC

块级格式化上下文

渲染规则

  • bfc内部上下垂直方向的边距会有重叠
  • bfc区域不会和浮动元素重叠
  • bfc外面和内部元素不会互相影响,他是独立的容器
  • 浮动元素参与计算bfc的高度

如何创建BFC

  • float不为none
  • position不为static、relative
  • display为inline-block,table相关,flex
  • overflow不为visible

# DOM事件

# dom事件类
  • dom0:ele.onclick=function(){}
  • dom2:ele.addEventListener('click',function(){},false)
  • dom3:增加了鼠标、键盘等事件
# 事件模型
  • 捕获
  • 冒泡
# 事件流

捕获-->目标阶段-->冒泡

window-->document-->html-->父元素-->子元素

# 常见Event对象应用
  • preventDefault 取消默认事件
  • stopPropagation 阻止冒泡
  • stopImmediatePropagation 阻止该事件中其他函数的响应
  • currentTarget 绑定事件的元素
  • target 触发事件的元素
# 自定义事件
var eve=new Event('custome')
el.addEventListen('custome',function(){
	console.log()	
})
el.dispatchEvent(eve)

# 类型转换

# 数据类型

Boolean、Null、Undefined、Number、String、Symbol、object

# 显式转换

Number函数

对于object来说

1、先调用valueOf方法(返回Boolean对象的原始值),如果但会原始类型的值,则调用Number方法

2、如果valueOf返回复合类型的值,再向其调用toString方法,如果返回原始类型的值,则调用Number方法

3、如果toString返回的是复合类型的值,则报错。

  • 字符串:变为对应的数值,否则为NaN,空字符串为0
  • 布尔值:true为1,false为0
  • undefined:NaN
  • null:变为0
  • 数组:有一个元素转为对应数组,多个元素转为NaN,无元素转为0/

String函数

对于object来说

1、向其调用toString方法,如果返回原始类型的值,则调用String方法

2、向其调用valueOf方法,如果但会原始类型的值,则调用String方法

3、如果都返回的是复合类型的值,则报错。

  • 数值:对应字符串
  • 布尔值:转换成true或false
  • undefined:undefined
  • null:null

Boolean函数

除了undefined、null、+0、-0、NaN、""为false以外,全为true

# 隐式转换

触发条件

  • 四则运算
  • 判断语句(例如if语句)
  • Native调用(例如console.log
[]+[]        //""
[]+{}        //"[object Object]"  
{}+[]        //0   此处会将{}当作一个代码块,实际运行的是+[]
{}+{}	 	 //chrome与firfox不一样
true+true	 //2
1+{a"1}		 //"1[object Object]"
# 对象转换的原理解释

当将对象转化为一版数据类型的时候其实是调用了isPrimitive这个方法。

isPrimitive方法的实现

// 获取类型
const getType = (obj) => {
    return Object.prototype.toString.call(obj).slice(8,-1);
}
// 是否为原始类型
const isPrimitive = (obj) => {
    const types = ['String','Undefined','Null','Boolean','Number'];
      return types.indexOf(getType(obj)) !== -1;
}
const ToPrimitive = (input, preferredType) => {
    // 如果input是原始类型,那么不需要转换,直接返回
    if (isPrimitive(input)) {
        return input;
    }
    let hint = '', 
        exoticToPrim = null,
        methodNames = [];
    // 当没有提供可选参数preferredType的时候,hint会默认为"default";
    if (!preferredType) {
        hint = 'default'
    } else if (preferredType === 'string') {
        hint = 'string'
    } else if (preferredType === 'number') {
        hint = 'number'
    }
    exoticToPrim = input.@@toPrimitive;//data对象才有.@@toPrimitive
    // 如果有toPrimitive方法
    if (exoticToPrim) {
        // 如果exoticToPrim执行后返回的是原始类型
        if (typeof (result = exoticToPrim.call(O, hint)) !== 'object') {
            return result;
        // 如果exoticToPrim执行后返回的是object类型
        } else {
            throw new TypeError('TypeError exception')
        }
    }
    // 这里给了默认hint值为number,Symbol和Date通过定义@@toPrimitive方法来修改默认值
    if (hint === 'default') {
        hint = 'number'
    }
    return OrdinaryToPrimitive(input, hint)
}
const OrdinaryToPrimitive = (O, hint) => {
    let methodNames = null,
        result = null;
    if (typeof O !== 'object') {
        return;
    }
    // 这里决定了先调用toString还是valueOf
    if (hint === 'string') {
        methodNames = [input.toString, input.valueOf]
    } else {
        methodNames = [input.valueOf, input.toString]
    }
    for (let name in methodNames) {
        if (O[name]) {
            result = O[name]()
            if (typeof result !== 'object') {
                return result
            }
        }
    }
    throw new TypeError('TypeError exception')
}

在 ES6 之后提供了 Symbol.toPrimitive 方法,该方法在类型转换的时候优先级最高。

Symbol.toPrimitive的示例

const obj = {
  toString() {
    return '1111'
  },
  valueOf() {
    return 222
  },
  [Symbol.toPrimitive]() {
    return 666
  }
}
const num = 1 + obj; // 667
const str = '1' + obj; // '1666'
# 连接符+
  • 先对两个数字执行 toPrimitive 方法,参数为默认
  • 然后判断如果有字符串则通过toString 转换后串起来
var a = 'hello ', b = {};
var c = a + b; // "hello [object Object]"

# 网络

# http协议特点
  • 简单快速:想访问某个资源只需访问对应的uri(每个资源对应uri是固定的)
  • 灵活:http就可以完成不同数据类型的传输
  • 无连接:不会保持连接
  • 无状态:客户端和服务端是两种身份,服务端是没法区分每次链接者身份
# 报文组成
  • 请求报文:请求行(http方法、页面地址、http协议版本)、请求头、空行、请求体
  • 响应报文:状态行(http协议版本、状态码、状态信息)、响应头、空行、响应体
# http方法
  • get:获取资源
  • post:传输资源
  • put:更新资源
  • delete:删除资源
  • head:获得报文首部
# post和get的区别
  • get在浏览器回退时无害的,post会再次提交请求
  • get的url可以被收藏,post不行
  • get会被浏览器主动缓存,post不行
  • get请求参数会保留在浏览器历史记录中,post不会
  • get在url中传送的参数有长度限制,post无限制
# http状态码

1XX:指示信息

2XX:成功请求

200:客户端请求成功

206:可送发送了也带有range(范围)头的get请求,例如当返回一个视频,由于视频很大,所以一段一段接收

3XX:重定向

301:请求已转至新的url

302:请求已转至临时新的url

304:客户端有缓存文档并发出了一个条件性请求,服务器告诉客户,原来缓存的文档还可以继续用

4XX:客户端请求错误

400:请求语法错误,不能被服务器理解

403:请求的资源禁止被访问

404:请求资源不村子啊

5XX:服务器错误

500:服务器发生了不可预期的错误

# 持久化连接

http采用“请求-应答”模式,当使用普通模式的时候,即非Keep-Alive模式时,每个请求/应答客户和服务器都要新建立一个连接,完成之后立即断开链接(http协议为无连接的协议)

当使用Keep-Alive模式(持久链接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当对服务器的后继请求时,Keep-Alive避免了建立或者重新连接。但是这要求http必须为1.1版本。

# 管线化

在持久连接的情况下,每个消息的传递类似于

请求1->响应1->请求2->响应2->请求3->响应3

使用管线化后,某个连接上的消息变为

请求1->请求2->请求3->响应1->响应2->响应3

  • 管线化通过持久化连接完成,仅支持http/1.1支持此技术
  • 只有get和head可以进行管线化,而post有所限制
  • 他不会影响响应到来的顺序
# tcp

三次握手,四次挥手

所谓的三次握手即TCP连接的建立。这个连接必须是一方主动打开,另一方被动打开的。

所谓的四次挥手即TCP连接的释放(解除)。连接的释放必须是一方主动释放,另一方被动释放。

三次握手

  • 客服端 :请求连接
  • 服务器:收到链接请求
  • 客户端:收到服务端准备好的消息

四次挥手

  • 客户端:请求断开
  • 服务端:收到请求报文
  • 服务端:必要的数据
  • 客户端:收到最后的数据

# 原型

# 由浅入深:
  • 如何创建变量
  • 原型、构造函数、实例、原型链
  • instanceof原理
  • new运算符
# 创建对象方式
  • 第一种:
var o1={name:'o1'}
var o11=new Object({name:'o11'})
  • 第二种:
var M=function(){this.name='o2'}
var o2=new M()
  • 第三种:将o3.__proto__指向p
var P={name:'o3'}//name变成o3的原型上的属性
var o3=Object.create(P)
# instanceof

实例对象的__proto__会指向构造函数的prototype指向的对象。

instanceof的实质是在判断前者的__proto__属性和后者的prototype属性是否是一个引用。

所以,instanceof无法区分后者是否为前者的构造函数,还可能是前者构造函数的构造函数。

# new运算符工作原理

工作步骤

  • 一个新对象(新实例)被创建,它继承自foo.prototype(foo为构造函数)
  • 构造函数foo被执行,执行的时候,相应的传参会被传入,同时上下文会被指定为这个新实例。
  • 如果构造函数返回了一个对象,那么这个对象会取代new的对象,如果构造函数没有返回对象,new出来的结果为步骤1创建的对象。
var new=function(func){
    var o=Object.create(func.prototype)
    var k=func.call(o);
    if(typeof k == 'object'){
       return k;
    }else{
        return o;
    }
}

# 面向对象

# 类与实例
/**
类的声明
*/
function Animate(){
    this.name='name'
}

class Animate2{
    constructor(){
        this.name='name';
    }
}

/**
实例化
*/
new Animate()
new Animate2()
# 类与继承


/**
继承
*/
1、借助构造函数
function dog(){
    Animate.call(this);//apply.改变this确保父类的属性挂载到子类上
    this.type='dog';
}
//原理:通过该边this执行使得dog也拥有Animate上的属性
//缺点:Animate原型链(prototype)上的东西不会被dog继承
2、借助原型链
function dog(){
    this.type='dog';
}
dog.prototype=new Animate();
//原理:使得(new dog).__proto__指向Animate的实例,Animate的实例的__proto__指向Animate的prototype。最后使得dog实例也继承了Animate的原型对象。并且dog实例对象的__proto__上面也有了Animate上生命的属性。
//缺点:dog的两个实例dog1.__proto__===dog2.__proto__。他们都指向同一个Animate的实例,所以Animate声明的方法‘name’对于dog1和dog2是共享的,一个改变另一个也变了。
3、组合方式(了解)
function dog(){
    Animate.call(this);
    this.type='dog';
}
dog.prototype=new Animate();
//原理:dog1.__proto__中也有name属性,但是dog构造函数中也有,他的实例会首先找到dog构造函数中的name属性。
//缺点:在声明dog的实例时,Animate构造函数被执行了两次
4、组合优化(了解)
function dog(){
    Animate.call(this);
    this.type='dog';
}
dog.prototype=Animate.prototype;
//缺点:无法区分dog1是由dog实例化的还是Animate实例化的
//dog1 instanceof dog  //true
//dog1 instanceof Animate //true
//dog1.constructor===Animate
5、组合再优化
function dog(){
    Animate.call(this);
    this.type='dog';
}
dog.prototype=Object.create(Animate.prototype);
dog.prototype.constructor=dog;
//原理:Object.create(Animate.prototype)生成了一个新对象设叫a,那么a.__proto__指向Animate.prototype。这样借助一个中间变量a,完成了继承。

# 通信类

# 浏览器中输入url经历了什么
  • 浏览器首先去找本地的hosts文件,检查在该文件中是否有相应的域名、IP对应关系,

    有,则向其IP地址发送请求

  • DNS进行递归查询和迭代查询,将域名解析成对应的服务器IP地址,发回给浏览器

  • 获取ip后,开始网络通讯。分层由高到低分别为:应用层、传输层、网络层、数据链路层。发送端从应用层往下走,接收端从数据链路层往上走

  • 渲染阶段,解析HTML生成DOM树。

  • 解析CSS生成CSSOM规则树。

  • 将DOM树与CSSOM规则树合并在一起生成渲染树。

  • 遍历渲染树开始布局,计算每个节点的位置大小信息。

  • 将渲染树每个节点绘制到屏幕。

# 同源策略及策略

它限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制

其中,源包含协议、域名、端口。

限制方面

  • Cookie、LocalStorage和IndexDB无法读取
  • DOM无法获得
  • AJAX请求不能发送
# 前后端如何通信
  • AJAX:无法跨域
  • WebSocket:不受同源策略限制
  • CORS:支持跨域通讯也支持同源通讯
# 如何创建Ajax

注意点:工作流程兼容性处理事件触发条件事件触发顺序

# 跨域通信
  • JSONP:使用script标签的异步加载实现
  • Hash:url地址中#后面的东西,hash的变动页面不会刷新。而search的改变会引起页面刷新,所以search不可做跨域通信
  • postMessage:h5新增
  • WebSocket
  • CORS:可理解为支持跨域通信的ajax

Hash使用场景?

	  // 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B
      // 在A中伪代码如下:
      var B = document.getElementsByTagName('iframe');
      B.src = B.src + '#' + 'data';
      // 在B中的伪代码如下
      window.onhashchange = function () {
          var data = window.location.hash;
      };

postMessage的使用

	  // postMessage
      // 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
      Bwindow.postMessage('data', 'http://B.com');
      // 在窗口B中监听
      Awindow.addEventListener('message', function (event) {
          console.log(event.origin);
          console.log(event.source);
          console.log(event.data);
      }, false);

WebSocket的使用

      // Websocket【参考资料】http://www.ruanyifeng.com/blog/2017/05/websocket.html

      var ws = new WebSocket('wss://echo.websocket.org');

      ws.onopen = function (evt) {
          console.log('Connection open ...');
          ws.send('Hello WebSockets!');
      };

      ws.onmessage = function (evt) {
          console.log('Received Message: ', evt.data);
          ws.close();
      };

      ws.onclose = function (evt) {
          console.log('Connection closed.');
      };

CORS标准的运用

原理:浏览器会拦截AJAX请求,当发现AJAX请求跨域后,会在请求头中标记一个origin

fetch可以用来实现CORS通讯

      // CORS【参考资料】http://www.ruanyifeng.com/blog/2016/04/cors.html
      // url(必选),options(可选)
      fetch('/some/url/', {
          method: 'get',
      }).then(function (response) {

      }).catch(function (err) {
        // 出错了,等价于 then 的第二个参数,但这样更好用更直观
      });
# 常见工具封装
/**
 * 功能类库
 */
 /**
  * [util 工具类]
  * @type {Object}
  */
 var util = {};

 /**
  * [function 返回数组的指定项]
  * @param  {[type]} array [description]
  * @param  {[type]} item  [description]
  * @return {[type]}       [description]
  */
 util.indexOf = function (array, item) {
     for (var i = 0; i < array.length; i++) {
         if (array[i] === item) {
             return i;
         }
     }
     return -1;
 };

 /**
  * [function 判断是否为函数]
  * @param  {[type]} source [description]
  * @return {[type]}        [description]
  */
 util.isFunction = function (source) {
     return '[object Function]' === Object.prototype.toString.call(source);
 };

 /**
  * [isIE 判断是不是ie]
  * @return {Boolean} [如果是ie返回版本号,不是则返回false]
  */
 util.isIE = function () {
     var myNav = navigator.userAgent.toLowerCase();
     return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
 };

 /**
  * [function 对象浅复制]
  * @param  {[type]} dst [description]
  * @param  {[type]} obj [description]
  * @return {[type]}     [description]
  */
 util.extend = function (dst, obj) {
     for (var i in obj) {
         if (obj.hasOwnProperty(i)) {
             dst[i] = obj[i];
         }
     }
 };

 /**
  * [function 获取一个随机的5位字符串]
  * @param  {[type]} prefix [description]
  * @return {[type]}        [description]
  */
 util.getName = function (prefix) {
     return prefix + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
 };

 /**
  * [function 在页面中注入js脚本]
  * @param  {[type]} url     [description]
  * @param  {[type]} charset [description]
  * @return {[type]}         [description]
  */
 util.createScript = function (url, charset) {
     var script = document.createElement('script');
     script.setAttribute('type', 'text/javascript');
     charset && script.setAttribute('charset', charset);
     script.setAttribute('src', url);
     script.async = true;
     return script;
 };

 /**
  * [function jsonp]
  * @param  {[type]} url      [description]
  * @param  {[type]} onsucess [description]
  * @param  {[type]} onerror  [description]
  * @param  {[type]} charset  [description]
  * @return {[type]}          [description]
  */
 util.jsonp = function (url, onsuccess, onerror, charset) {
     var callbackName = util.getName('tt_player');
     window[callbackName] = function () {
         if (onsuccess && util.isFunction(onsuccess)) {
             onsuccess(arguments[0]);
         }
     };
     var script = util.createScript(url + '&callback=' + callbackName, charset);
     script.onload = script.onreadystatechange = function () {
         if (!script.readyState || /loaded|complete/.test(script.readyState)) {
             script.onload = script.onreadystatechange = null;
             // 移除该script的 DOM 对象
             if (script.parentNode) {
                 script.parentNode.removeChild(script);
             }
             // 删除函数或变量
             window[callbackName] = null;
         }
     };
     script.onerror = function () {
         if (onerror && util.isFunction(onerror)) {
             onerror();
         }
     };
     document.getElementsByTagName('head')[0].appendChild(script);
 };

/**
 * [json 实现ajax的json]
 * @param  {[type]} options [description]
 * @return {[type]}         [description]
 */
 util.json = function (options) {
     var opt = {
         url: '',
         type: 'get',
         data: {},
         success: function () {},
         error: function () {},
     };
     util.extend(opt, options);
     if (opt.url) {
         var xhr = XMLHttpRequest
            ? new XMLHttpRequest()
            : new ActiveXObject('Microsoft.XMLHTTP');
         var data = opt.data,
             url = opt.url,
             type = opt.type.toUpperCase(),
             dataArr = [];
         for (var k in data) {
             dataArr.push(k + '=' + data[k]);
         }
         if (type === 'GET') {
             url = url + '?' + dataArr.join('&');
             xhr.open(type, url.replace(/\?$/g, ''), true);
             xhr.send();
         }
         if (type === 'POST') {
             xhr.open(type, url, true);
             xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
             xhr.send(dataArr.join('&'));
         }
         xhr.onload = function () {
             if (xhr.status === 200 || xhr.status === 304) {
                 var res;
                 if (opt.success && opt.success instanceof Function) {
                     res = xhr.responseText;
                     if (typeof res === 'string') {
                         res = JSON.parse(res);
                         opt.success.call(xhr, res);
                     }
                 }
             } else {
                 if (opt.error && opt.error instanceof Function) {
                     opt.error.call(xhr, res);
                 }
             }
         };
     }
 };

 /**
  * [function crc32加密]
  * @param  {[type]} str [description]
  * @return {[type]}     [description]
  */
 util.crc32 = function (url) {
     var a = document.createElement('a');
     a.href = url;
     var T = (function () {
         var c = 0,
             table = new Array(256);
         for (var n = 0; n != 256; ++n) {
             c = n;
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
             table[n] = c;
         }
         return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
     })();
     var crc32_str = function (str) {
         var C = -1;
         for (var i = 0, L = str.length, c, d; i < L;) {
             c = str.charCodeAt(i++);
             if (c < 0x80) {
                 C = (C >>> 8) ^ T[(C ^ c) & 0xFF];
             } else if (c < 0x800) {
                 C = (C >>> 8) ^ T[(C ^ (192 | ((c >> 6) & 31))) & 0xFF];
                 C = (C >>> 8) ^ T[(C ^ (128 | (c & 63))) & 0xFF];
             } else if (c >= 0xD800 && c < 0xE000) {
                 c = (c & 1023) + 64;
                 d = str.charCodeAt(i++) & 1023;
                 C = (C >>> 8) ^ T[(C ^ (240 | ((c >> 8) & 7))) & 0xFF];
                 C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 2) & 63))) & 0xFF];
                 C = (C >>> 8) ^ T[(C ^ (128 | ((d >> 6) & 15) | ((c & 3) << 4))) & 0xFF];
                 C = (C >>> 8) ^ T[(C ^ (128 | (d & 63))) & 0xFF];
             } else {
                 C = (C >>> 8) ^ T[(C ^ (224 | ((c >> 12) & 15))) & 0xFF];
                 C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 6) & 63))) & 0xFF];
                 C = (C >>> 8) ^ T[(C ^ (128 | (c & 63))) & 0xFF];
             }
         }
         return C ^ -1;
     };
     var r = a.pathname + '?r=' + Math.random().toString(10).substring(2);
     if (r[0] != '/') {
         r = '/' + r;
     }
     var s = crc32_str(r) >>> 0;
     var is_web = location.protocol.indexOf('http') > -1;
     return (is_web ? [location.protocol, a.hostname] : ['http:', a.hostname]).join('//') + r + '&s=' + s;
 };

 export default util;

# 安全类

# CSRF

基本概念

全称跨站请求伪造。

攻击原理

例如,用户时网站A的用户,并且登录网站A,网站A返回cookie,当用户请求网站B时,B中存在某个链接,这个链接指向的网站A的接口(例如是一个对网站A的get请求),这时用户浏览器会自动上传cookie去访问网站A,网站A验证用户通过请求后便会进行操作。

两个必要点时:首先用户在A网站登录过、网站A在对应接口上有此漏洞

防御措施

  • 添加token验证
  • Referer验证
  • 隐藏令牌
# XSS

跨域脚本攻击

攻击原理

防御措施

# 算法类

# 排序

冒泡、快速、选择、希尔

# 堆栈、队列、链表
# 递归
# 波兰式和逆波兰式

# 深入

# 渲染机制

# DOCTYPE作用

DOCTYPE是用来声明文档类型和DTD规范的,一个主要用途是文件的合法性检验,如果文件不合法,浏览器解析时会出错。

DTD:用它可以告诉浏览器是什么文档类型(告诉浏览器是那种DTD),浏览器会选择相对应的渲染方式。

html5写法<!DOCTYPE html>

html4.0有两种模式,传统模式和严格模式。在严格模式中,包含所有html元素和属性,但不包括展示性和启用的元素。

# 浏览器渲染过程
  • html被domParse转成dom树,css被CSSParser转成css树
  • 将两棵树整合为RenderTree(不知道某一个元素的位置)
  • 通过layout计算位置
  • 最后进行渲染
# 重排Reflow

dom结构中元素都有自己的盒子,这些都需要浏览器根据各种样式来计算结果将元素放到她该出现的位置。这个过程叫重排。

触发重排

  • 增删改查dom结点

  • 当你移动dom位置

  • 修改css样式,例如宽和高

  • 当resize窗口的时候或是滚动(可能引起)

  • 当修改默认网页的默认字体

# 重绘Repaint

当各种盒子的位置、大小及属性确定后,浏览器将这些元素按照各自的特性绘制了一遍,于是页面内容出现了,这个过程称为repaint。

触发重绘

  • dom改动

  • css改动

如何减少repaint

一次性添加创建的dom元素,不要一个一个加,将其放入一个父元素中,一次性加入。

# js运行机制

# 异步任务的执行顺序

浏览器会先执行同步代码,遇到异步方法会先将其挂起,全部执行后才会开始执行之前挂起的方法。

console.log(1)
setTimeout(()=>{console.log(2)},1000)
while(1){
      
}
//只打印1,然后陷入死循环
# Event Loop

js引擎将同步任务放到运行栈中,当js引擎遇到settimeout时,浏览器的timer模块会将其拿走,但是先不放到异步任务队列,当settimeout的时间到了之后再放到任务队列中。

浏览器执行运行栈,完成后检测任务队列有没有任务,若有,将任务队列的任务放到运行栈执行,执行结束后继续检测任务队列有没有任务,如此循环叫做事件循环

# 异步任务的类型
  • setTimeout和setInterval
  • DOM事件
  • Promise
# 宏任务和微任务

而宏任务一般是:同步代码,UI rendering,I/O,setTimeout,setInterval。

微任务:Promise,process.nextTick。

        setTimeout(() => {
            console.log(4);
        }, 0);
 
        new Promise(resolve=>{
            console.log(1);
            for(let i =0;i<10000;i++){
                i == 9999 && resolve();
            }
            console.log(2)
        }).then(()=>{
            console.log(5)
        })
 
        console.log(3)
//打印12354
//在执行完宏任务后,会执行微任务,而then的内容则是一个微任务,所以要先执行它

# 页面性能

# 提升页面性能方式
  • 资源压缩合并,减少http请求
  • 非核心代码异步加载==>异步加载方式==>异步加载区别
  • 利用浏览器缓存==>缓存分类==>缓存原理
  • 使用CDN
  • 预解析DNS
# 异步加载

异步方式

  • 动态脚本加载(js创建script标签)
  • defer
  • async

异步加载的区别

defer是在html解析完成之后执行,而且按照加载顺序执行

async时再加载完成后立即执行,如果是多个,首先加载完的先执行

使用方法如下

<script src-"" defer></script>
<script src="" async></script>
# 浏览器缓存
  • 强缓存:强迫浏览器必须使用本地缓存。常见http请求头Expires(后面加过期时间,是绝对时间点,以服务器时间为准)Cache-Control(后面加相对时间)当两个请求头都出现时,一第二种为准。
  • 协商缓存:浏览器会先向服务器询问是否使用缓存。常见http请求头Last-Modified(服务器发送的上次修改的时间) If-Modified-Since(浏览器携带此值发送给服务器询问这个时间是否有效)Etag If-None-Match(与上两个值相似,但是携带的是哈希值)
# DNS预解析

下面第二句是设置预解析。

页面中的a标签在一般浏览器默认打开dns预解析,如果页面是https协议开头的,一般浏览器时默认不开启dns预解析,通过第一句话去设置预解析。

<meta http-equiv="x=dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//host_name_to_prefetch.com">

# 错误监控

# 前端错误的分类
  • 代码错误(即时运行错误)
  • 资源加载错误
# 错误的捕获

捕获执行运行错误

  • try...catch
  • window.onerror

捕获资源加载错误

  • object.onerror:此处的object指的是某个具体html元素如img对象,因为错误是无法冒泡的,所以要监控元素本身而不能检测其父元素
  • performance.getEntries():高级浏览器自带对象,返回所有加载资源的
  • Error事件捕获:错误无法冒泡但是可以捕获,通过在捕获阶段获取错误

跨域时,可以捕获错误么?

可以但是错误提示只有Script error,无法获取报错信息。需要在script标签中加入crossorigin属性,然后再js资源响应头中加入Access-Control-Allow-Origin:*

# 上报错误的基本原理
  • 采用Ajax信息的方式上报
  • 利用image对象上报
<script>
	(new Image()).src="http://baidu.com/psth?r=test"
</script>

利用这种方式发送一个请求。

# MVVM

# 对mvvm的认识
  • 是从mvc和mvp演化而来
  • mvvm定义
  • mvvm与mvc的区别
# 双向绑定的原理
# 简易版mvvm