来自2024年应对面试的前端技术总结

0. 目录

[TOC]

1.HTML+CSS

1.1.如何理解HTML语义化?

  • 让人更加容易读懂(增加代码的可读性)
  • 让搜索引擎更容易读懂,有助于爬虫抓取有效信息、seo
  • 在没有CSS样式的情况下页面也能很好的呈现内容结构、代码结构。

1.2.script标签中的defer和async的区别

  • script 会阻碍HTML的解析
  • async script:解析HTML的过程进行脚本的异步下载,下载完成之后立即执行。
  • defer script:完全不会阻碍HTML的解析,解析完成之后再按顺序执行脚本。

1.3.在浏览器地址栏中输入url到请求返回发生什么?

    1. 输入url之后解析出协议、主机、端口、路径等信息,并构造一个http请求
    • 强缓存
    • 协商缓存
    1. DNS域名解析
    1. TCP连接
    • 三次握手
    1. http请求
    1. 服务器处理请求并返回HTTP报文。
    1. 浏览器渲染页面
    1. 断开TCP链接

1.4.两种盒模型

CSS 盒子模型本质上是一个盒子,封装周围的 HTML 元素,它包括:边框、外边距、内边距和实际内容

  • 标准盒模型、IE盒模型

    • 标准盒模型:box-sizing:content-box

      宽度和高度用来设置内容区的大小

    • IE盒模型:box-sizing:border-box

      宽度和高度用来设置整个盒子的可见框大小(即width和height指的是内容区、填充(内边距)、边框的大小)

1.5.CSS选择器和优先级

!important > style (内联样式)> id选择器 > class(类选择器)> 标签选择器 > 通配符选择器

CSS的权重关系:

(1)内联样式表的优先级别最高;

(2)内部样式表与外部样式表的优先级和书写的顺序有关,后书写的优先级别高;

(3)同在一个样式表中的优先级和书写的顺序也有关,后书写的优先级别高。(被覆盖的只是相同属性的样式)

选择器的权重(优先级) div{color: red !important;} > 行内样式(1000) > id选择器(100) > class选择器(10) > 标签选择器(1) > 继承的 (0) 群组选择器权重是它本身 后代选择器(包含选择器)是两者之和

注意:!important加在属性值的后面,; 的前面 !important有些浏览器不认识

1.6.重排(reflow)和重绘(repaint)

  • 重排:无论通过什么方式影响了元素的几何信息(元素在视口内的位置和尺寸的大小),浏览器需要重新计算元素在视口内的集合属性。
  • 重绘:通过构造渲染树和重排(回流 Layout)阶段,我们知道了那些节点是可见的,以及节点的样式和具体的几何信息(元素在视口的位置和尺寸的大小),接下来就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段叫重绘。
  • juejin.cn/post/684490...

​ 如何减少重排和重绘?

  • 最小化重绘和重排,修改样式的时候通过 css 类名修改或通过 cssText 修改。

  • 批量操作DOM

  • 使用absolute 和 fixed 使元素脱离文档流,这在制作复杂的动画时对性能的影响比较明显

  • 开启GPU加速,利用css属性transform、will-change 等,比如改变元素的位置,我们使用transform会比使用绝对定位改变其left、top等来的高效,因为他不会出发重排或重绘,transform使得浏览器为元素创建一个GPU图层,这使得动画元素在一个独立的层中进行渲染。当元素的内容没有发生改变,就没有必要进行重绘。

    juejin.cn/post/684490...

​ 减少回流和重绘:

​ 1)修改样式的时候通过 css 类名修改或通过 cssText 修改。

​ 2)DOM 元素离线修改--->隐藏元素,应用修改,重新显示。(浏览器本身也会有优化)

​ 3)避免触发同步布局事件,如获取 offsetWidth 等属性值,因为会强制浏览器刷新队列。

​ 4)使用绝对定位让复杂动画脱离文档流减少父元素以及后续元素频繁的回流。

​ 5)使用css3硬件加速,可以让 transform、opacity、filters、will-change 这些动画不会引起回流重绘 (会提高内存占用)。

1.7.BFC的理解

BFC(Block Formating Content)块级上下文

  • BFC 是一个CSS中隐藏的属性,可以为一个元素开启BFC

    • BFC是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。那么怎么使用BFC呢,BFC可以看做是一个CSS元素属性
  • 元素开启BFC的特点

      1. 开启BFC不会被浮动元素覆盖
      2. 开启BFC的元素子元素和父元素外边距不会重叠
      3. 开启BFC的元素可以包含浮动的子元素
  • 开启BFC的方式

    • 1.设置元素的浮动(不推荐)

      2.设置为行内块元素(不推荐)

      3.将元素的overflow设置为非visible的值

      - 常用:元素设置为 overflow:hidden 开启

回答是什么:BFC是一个完全独立的空间,将元素设置成BFC可以让空间里的子元素不会影响到外面的布局。

如何开启 :设置浮动float:left;、设置为行内块display:inline-block;、将元素的溢出属性设置为非visibleoverflow:hidden

解决了什么问题

  • 使用float脱离文档流,高度塌陷的问题,
  • margin外边距重叠的问题。
  • 两栏布局的问题

1.8 实现三栏布局(两边固定中间自适应)

1.9 line-height如何继承

line-height的几种写法:

  1. 写数值 如20px
  2. 写比例 如2/1
  3. 写百分比 如写200% 如果是前两种,则子元素就会直接继承数值或比例,但是如果写百分比还要进行相应的计算

针对不同写法 回答上面p标签的继承line-height如下:

1,如果body中的line-height:20px 那么 p标签中的line-height就是20px

2,如果body中line-height:1.5 那么p标签中的line-height:就是1.5*16px=24px

3.百分比的情况就是如上所诉

1.10 网站开发中,如何实现图片的懒加载

1.11 HTML 中有哪些语义化标签

  • header
  • footer
  • main
  • aside
  • article
  • section
  • address
  • summary/details
  • menu
  • h1/h2/h3/h4/h5/h6
  • img
  • p
  • strong/italic

1.12 HTML 标签有哪些行内元素

a、b、br、em、i、img、input、label、select、span、s、strike、sub、sup、textarea、u、var、abbr、acronym、bdo、big、cite、code、dfn、font、kbd、q、samp、small、tt

1.13实现垂直居中(flex,grid)

  1. flex实现

    css 复制代码
    html,body{
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center/* space-around */;               
    }
    .inner {
        width: 300px;
        height: 300px;
        background-color:rgb(192, 161, 161);
    }
  2. grid实现

    css 复制代码
    html,body{
        width: 100%;
        height: 100%;
        display: grid;       
    }
    .inner {
        width: 300px;
        height: 300px;
        background-color:rgb(192, 161, 161);
        margin: auto;
    }
  3. 绝对定位+ left + top

css 复制代码
html,body{
    width: 100%;
    height: 100%;
    position: relative;
}
.inner {
    width: 300px;
    height: 300px;
    background-color:rgb(192, 161, 161);
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
  1. 绝对定位+margin

    css 复制代码
    html,body{
        width: 100%;
        height: 100%;
        position: relative;
    }
    .inner {
        width: 300px;
        height: 300px;
        background-color:rgb(192, 161, 161);
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        margin: auto;
    }

1.14 左侧固定,右侧自适应

  1. flex

    css 复制代码
    html,body{
        width: 100%;
        height: 100%;
        position: relative;
        display: flex;
    }
    .left{
        width: 200px;
        height: 10%;
        background: #222;
    }
    .right{
        width: 100%;
        height: 10%;
        background-color: cornflowerblue;
        flex-grow: 1;
    }
  2. grid

    css 复制代码
    .left{
        background-color: red;
    }
    .right{
        background-color: #bfc;
    }
    .wrap{
        display: grid;
        grid-template-columns: 10% auto;
        width: 1000px;
        height: 400px;
    }

1.15 响应式

​ 1. 是什么?一个URL可以响应多端

​ 2. 语法结构:

css 复制代码
@media only screen and (max-width:1000px) {
    ul li:last-child{
        display: none;
    }
}

​ only:可以排除不支持的媒体查询

​ screen:设备类型

​ max-width | max-height

​ min-width | min-height

  1. 响应式图片【性能优化】

1.16 flex布局(常用)

  1. 定义Flexbox

    css 复制代码
    display: flex | inline-flex;

    CSS的columns、float、clear、vertical-align属性在伸缩项目上也没有效果。

  2. 定义伸缩方向

    css 复制代码
    flex-direction: row | row-reverse | column | column-reverse
  3. 定义行数

    css 复制代码
    flex-wrap: nowrap | wrap | wrap-reverse // 不换行 换行 反向换行
  4. 定义对齐方式

    1. 主轴对齐

      css 复制代码
      justify-content: flex-start | flex-end | center | space-between | space-around
    2. 侧轴对齐

      css 复制代码
      align-items: flex-start | flex-end | center | baseline | stretch			
    3. 伸缩行对齐(一行的伸缩容器无效)

      css 复制代码
      align-content: flex-start | flex-end | center | space-between | space-around | stretch
  5. 定义伸缩项目

    1. 显示位置

      css 复制代码
      order: <integer>
    2. 扩展空间

      css 复制代码
      flex-grow: <number>
    3. 收缩空间

      css 复制代码
      flex-shrink: <number>
    4. 伸缩比率

      css 复制代码
      flex-basic: <length> | auto
    5. 对齐方式

      css 复制代码
      align-self: auto | flex-start | flex-end | center | baseline | stretch		

1.17 CSS怎么画三角形

zhuanlan.zhihu.com/p/482361933

2.JS基础


2.1 数据类型

​ 基础的数据类型:Undefined、null、Boolean、String、Number、Symbol、BigInt

​ 引用数据类型:Object

Symbol:独一无二的值

BigInt:可以表示任意大小的整数

值类型赋值的过程:

js 复制代码
let a = 100;
let b = a;
a = 200;
console.log(b); // 100

引用类型赋值的过程:

js 复制代码
let a = { age: 20 };
let b = a;
b.age = 30;
console.log(a.age); // 30

引用类型存储在**堆(heap)**中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;


Symbol的使用:

js 复制代码
let s = Symbol();

typeof s
// "symbol"

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

// 使用场景
1. 作为对象属性
当一个复杂对象中含有多个属性的时候,很容易将某个属性名覆盖掉,利用 Symbol 值作为属性名可以很好的避免这一现象。

2. 模拟类的私有方法
ES6 中的类是没有 private 关键字来声明类的私有方法和私有变量的,但是我们可以利用 Symbol 的唯一性来模拟。
const speak = Symbol();
class Person {
    [speak]() {
        ...
    }
}
因为使用者无法在外部创建出一个相同的 speak,所以就无法调用该方法

隐式转换:

字符串和其他类型相加都会转换成字符串类型

NaN是number类型

undefined+1 是NaN

null是object类型(typeof null )

null和undefined的区别:

undefined:表示未定义,一般变量声明了但没有定义返回的undefined,转化为数值为NaN

null:表示"无"的对象,转化为数值为0.

js在设计之初先设计出null,借鉴了Java,但是null会隐式转化为0,为了使得不容易发生错误,后来又设计了undefined。

2.2 数据类型的判断

  • typeof:能判断所有的值类型、函数。不可对null、对象、数组进行精确的判断,因为都是object。

  • typeof返回一个表示数据类型的字符串,返回结果包括:number、string、boolean、object、undefined、function。typeof可以对基本类型number、string 、boolean、undefined做出准确的判断(null除外,typeof null==="object",这是由于历史的原因,我就不巴拉巴拉了,其实我也说不清楚😢);而对于引用类型,除了function之外返回的都是object。但当我们需要知道某个对象的具体类型时,typeof就显得有些力不从心了。

js 复制代码
console.log(typeof undefined); // undefined
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof "str"); // string
console.log(typeof Symbol("foo")); // symbol
console.log(typeof 2172141653n); // bigint
console.log(typeof function () {}); // function
// 不能判别
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof null); // object
  • instanceof :能判断对象的类型,不能判断基本数据类型,其内部运行机制是判断在其原形链中能否找到该类型的原型

  • 当我们需要知道某个对象的具体类型时,可以用运算符 instanceof,instanceof操作符判断左操作数对象的原型链上是否有右边这个构造函数的prototype属性,也就是说指定对象是否是某个构造函数的实例,最后返回布尔值。 检测的我们用一段伪代码来模拟instanceof内部执行过程:

js 复制代码
class People {}
class Student extends People {}

const vortesnail = new Student();

console.log(vortesnail instanceof People); // true
console.log(vortesnail instanceof Student); // true
//其实现就是顺着原型链去找,如果能找到对应的 Xxxxx.prototype  即为 true 。比如这里的 vortesnail  作为实例,顺着原型链能找到 Student.prototype  及 People.prototype ,所以都为 true。
  • Object.prototype.toString.call():所有的原始数据类型都能判断,还有Error对象、Data对象。
js 复制代码
Object.prototype.toString.call(2); // "[object Number]"
Object.prototype.toString.call(""); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(Math); // "[object Math]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(function () {}); // "[object Function]"

判断变量是否为数组:

js 复制代码
Array.isArray(arr);
arr.__proto__ === Array.prototype; // true
arr instanceof Array; // true
Object.prototype.toString.call(arr); // "[object Array]"


2.3 手写深拷贝

前提为拷贝类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
js 复制代码
/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 * @param {Map} map 用于存储循环引用对象的地址
 */

function deepClone(obj = {}, map = new Map()) {
    // 如果不是对象类型直接返回该基础类型
  if (typeof obj !== "object") {
    return obj;
  }
   //  
  if (map.get(obj)) {
    return map.get(obj);
  }

  let result = {};
  // 初始化返回结果
  if (
    obj instanceof Array ||
    // 加 || 的原因是为了防止 Array 的 prototype 被重写,Array.isArray 也是如此
    Object.prototype.toString.call(obj) === "[object Array]"
  ) {
    result = [];
  }
  // 防止循环引用
  map.set(obj, result);
  for (const key in obj) {
    // 保证 key 不是原型属性
    if (obj.hasOwnProperty(key)) {
      // 递归调用
      result[key] = deepClone(obj[key], map);
    }
  }

  // 返回结果
  return result;
}

作者:vortesnail
链接:https://juejin.cn/post/7061588533214969892
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

JSON.parse(JSON.stringify(xxx))。

Object.assign()

2.4 IEEE754

​ 为什么0.1 + 0.2 !== 0.3?

juejin.cn/post/694040...

原因:

  • 进制转换:js在做数字计算的时候,0.1 和 0.2 都会被转化为二进制的形式后无限循环,但是js采用IEEE754 二进制浮点运算,最大可以存储53位有效数字,于是大于53 为后面的会全部裁掉,导致精度缺失。
  • 对介运算:由于指数的位数不相同,运算的时候需要对阶运算,阶小的尾数要根据阶差来右移(0舍1进),尾数位移可能会发生数丢失的情况,影响精度。

解决:

  1. 转为整数(大数)运算
  2. 使用Number.EPSION误差范围
js 复制代码
console.log(0.1 + 0.2 === 0.3); //false

/* 使用 Number.EPSILON 误差范围 */
function isEqual(a,b) {
    return Math.abs(a - b ) < Number.EPSILON
    //Number.EPSILON 的实质是一个可以接受的最小误差范围,一般来说为 Math.pow(2, -52) 。
}

console.log(isEqual(0.1 + 0.2,0.3)); //true

2.5 原型

​ 原型:每个JavaScript 对象(null除外)在创建的时候就会与之关联的另一个对象,这个对象就是我们说的原型,每个对象都会从原型"继承"属性,其实就是prototype对象。

​ 原型链:由相互关联的原型组成的链状结构就是原型链。

2.6 作用域与作用域链

js 复制代码
- 作用域:规定了如何查找变量,也就是确定当前执行代码对变量的方位权限。换句话说,作用域决定了代码区块中变量和其他资源的可见性。(全局作用域、函数作用域、块级作用域)
- 作用域链:从当前的作用域开始一层层往上找某个变量,如果找到全局作用域还没找到,就放弃寻找。**(由多个执行上下文的变量对象构成的链表就叫做作用域链)**

2.7 执行上下文

javascript 复制代码
执行上下文:指当前执行环境中的变量、函数声明,参数(arguments),作用域链,this等信息。
分为全局执行上下文、函数执行上下文,其区别在于全局执行上下文只有一个,函数执行上下文在每次调用函数时候会创建一个新的函数执行上下文。
kotlin 复制代码
- 变量对象( variable object,VO)
- 作用域链 (Scope chain)
- this :
	this是执行上下文的一个属性,在js脚本的执行阶段确定的,它指向最后调用这个方法的对象。函数调用方式、构造器模式调用方式、方法调用的方式、call、apply、bind的调用方式

解释型语言,js分类解释和执行两个阶段。

解释:

diff 复制代码
- 词法分析
- 语法分析
- 作用域规则确定

执行:

diff 复制代码
- 创建执行上下文
- 执行函数代码
- 垃圾回收

2.8 闭包

**闭包:**自由变量的查找,是在函数定义的地方,向上级作用域查找。不是在函数执行的地方。

一个函数执行返回一个函数,内部函数可以访问到外部函数的变量,可以在其他地方被调用执行,这个函数就是闭包。闭包可以使变量有一个单独的作用域,形成私有化变量,一直保留在内存中不被回收。

闭包有哪些应用???

防抖、节流、函数柯里化
应用实例:缓存工具、隐藏函数、只提供API。

闭包的应用:

  1. 函数作为参数传递

    js 复制代码
    function print(fn) {
      const a = 200;
      fn();
    }
    
    const a = 100;
    function fn() {
      console.log(a);
    }
    
    print(fn); // 100

2.函数作为返回值被返回

js 复制代码
function create() {
  const a = 100;

  return function () {
    console.log(a);
  };
}

const fn = create();
const a = 200;
fn(); // 100
js 复制代码
function createCache() {
  const data = {}; // 闭包中被隐藏的数据,不被外界访问
  return {
    set: function (key, val) {
      data[key] = val;
    },
    get: function (key) {
      return data[key];
    },
  };
}

const c = createCache();
c.set("a", 100);
console.log(c.get("a")); // 100

优点:

  1. 使我们在函数外部能够访问到函数内部的变量。通过使用闭包,我们可以通过在外部调用闭包函数,从而在外 部访问到函数内部的变量,可以使用这种方法来创建私有变量。

  2. 是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以 这个变量对象不会被回收。

闭包里面的变量最后怎么回收?(针对内存泄漏)

markdown 复制代码
1. 如果引用闭包函数是全局变量,那么闭包会一直存在直到页面关闭。
1. 如果是被局部变量引用,等函数销毁之后在下次Javascript引擎执行`垃圾回收机制`的时候回收这块内存。

2.9 call、apply、bind实现

call():在使用一个指定的this值和若干个指定的参数的前提下调用某个函数或方法。

通过call :

  • 改变this的指向
  • 执行函数

apply():和call一样,只是传参不同

bind:返回的是一个函数

call的实现:分别接受参数。

js 复制代码
Function.prototype.myCall = function (context) {
  // 判断调用对象
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  // 首先获取参数
  let args = [...arguments].slice(1);
  let result = null;
  // 判断 context 是否传入,如果没有传就设置为 window
  context = context || window;
  // 将被调用的方法设置为 context 的属性
  // this 即为我们要调用的方法
  context.fn = this;
  // 执行要被调用的方法
  result = context.fn(...args);
  // 删除手动增加的属性方法
  delete context.fn;
  // 将执行结果返回
  return result;
};

apply的实现:接受数组形式的参数

js 复制代码
Function.prototype.myApply = function (context) {
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  let result = null;
  context = context || window;
  // 与上面代码相比,我们使用 Symbol 来保证属性唯一
  // 也就是保证不会重写用户自己原来定义在 context 中的同名属性
  const fnSymbol = Symbol();
  context[fnSymbol] = this;
  // 执行要被调用的方法
  if (arguments[1]) {
    result = context[fnSymbol](...arguments[1]);
  } else {
    result = context[fnSymbol]();
  }
  delete context[fnSymbol];
  return result;
};

bind的实现:

js 复制代码
Function.prototype.myBind = function (context) {
  // 判断调用对象是否为函数
  if (typeof this !== "function") {
    throw new Error("Type error");
  }
  // 获取参数
  const args = [...arguments].slice(1),
  const fn = this;
  return function Fn() {
    return fn.apply(
      this instanceof Fn ? this : context,
      // 当前的这个 arguments 是指 Fn 的参数
      args.concat(...arguments)
    );
  };
}; 

2.10 箭头函数

箭头函数并不是使用function关键字定义的,而是使用被称为"胖箭头"的操作符=>定义的。箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }return

js 复制代码
// 箭头函数
let foo = (name) => `我是${name}`
foo('南玖') // 我是南玖

// 等同于下面这个普通函数
let foo2 = function(name) {
    return `我是${name}`
}
js 复制代码
let foo = (name) => {
    if(name){
        return `我是${name}`
    }
    return '前端南玖'
}
foo('南玖') // 我是南玖

箭头函数与普通函数的区别

  1. 箭头函数与普通函数相比,缺少了caller,arguments

  2. 声明不同:

    声明一个普通函数需要使用关键字function来完成,并且使用function既可以声明成一个具名函数 也可以声明成一个匿名函数

    声明一个箭头函数则只需要使用箭头就可以,无需使用关键字function,比普通函数声明更简洁。

    箭头函数只能声明成匿名函数,但可以通过表达式的方式让箭头函数具名

  3. this指向不同

    • 对于普通函数来说,内部的this指向函数运行时所在的对象。

    • 箭头函数没有自己 的this对象,内部的this就是定义 时上层作用域中的this,是固定的,不可通过(call、apply、bind修改)。

  4. 箭头函数没有原型prototype.

  5. 箭头函数不能当成一个构造函数。

    juejin.cn/post/706994...

2.11 this和new

this是在运行的时候进行绑定的,并不是在编写的时候绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

this对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,在全局函数中,this等于window,而当函数被作为某个对象调用时,this等于那个对象。 在实际开发中,this 的指向可以通过四种调用模式来判断。

​ 当一个函数被调用时,会创建一个活动记录(有时候也称执行上下文)。这个记录包含调用栈、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的时候用到。

  1. 函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象。
  2. 方法调用,如果一个函数作为一个对象的方法来调用时,this指向这个对象。
  3. 构造函数调用,this指向这个用new新创建的对象。
  4. 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表,`` bind方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this指向除了使用new `时会被改变,其他情况下都不会改变。

在调用 new 时, 主要做了 4 件事:

  1. 创建一个新的空对象
  2. 将空对象的 __proto__ 指向构造函数的 prototype
  3. 执行构造函数, 并将新创建的空对象绑定为构造函数的this对象
  4. 如果构造函数有返回一个对象,则返回这个对象,否则返回新创建的那个对象

根据以上规则,我们可以模拟实现一个 new 函数

js 复制代码
function myNew (constructorFn, ...args) {
  // 1. 创建一个空对象
  const obj = {};
  // 2. 将空对象的__proto__指向constructor的prototype
  obj.__proto__ = constructorFn.prototype;
  // 3. 执行 constructor, 并将新对象绑定为 constructor 的 this 对象
  const res = constructorFn.apply(obj, args);
  // 4. 如果构造函数有返回值则返回res, 否则返回新对象
  return typeof res === 'object' ? res : obj
}

const p1 = myNew(Person, "jack");
p1.say();

作者:rugu 链接:juejin.cn/post/706266... 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.12 同步和异步的区别?

相关知识点:

同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是处于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。  

异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。 

回答:

同步指的是当一个进程在执行某个请求的时候,如果这个请求需要等待一段时间才能返回,那么这个进程会一直等待下去,直到消息返
回为止再继续向下执行。

异步指的是当一个进程在执行某个请求的时候,如果这个请求需要等待一段时间才能返回,这个时候进程会继续往下执行,不会阻塞等待消息的返回,当消息返回时系统再通知进程进行处理。

详细资料可以参考: 《同步和异步的区别》

2.13 ES6 模块化开发

2.14 ES6新特性

const/let 、 ... 、 function rest 、Proxy 、Promise 、 解构赋值 、箭头函数

  1. const let

const:声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

let:声明的变量可以改变,值和类型都可以改变,没有限制。

共同点:没有变量提升(var)。

  1. ... 扩展运算符

  1. function 函数默认值 default&rest
  2. Proxy
  3. Promise
  4. Object新API
    • Object.is()
    • Object.assign()
    • Object.setPrototypeof()
    • Object.getPrototypeOf()
    • Object.keys()
    • Object.values()

2.15 Object API(ES6)

  1. Object.assign() (浅拷贝)MDN
  2. Object.entries()MDN

Object.entries(obj)

​ 返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。

  1. Object.is()MDN

    Object.is() 方法判断两个值是否为同一个值

  2. Object.setPrototypeOf(obj, prototype)MDN

    设置一个指定的对象的原型 ( 即,内部 [[Prototype]] 属性)到另一个对象或 null

  3. Object.defineProperty()MDN

    方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

  4. Object.defineProperties()MDN

    方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象,可定义多个。

  5. Object.create()MDN

    创建一个新的对象,并使用现有的对象作为新对象的原型。

  6. Object.keys()

    返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。

  7. Object.getOwnPropertyNames()MDN

    返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。

  8. Object..values()MDN

    返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同(区别在于for...in循环枚举原型链中的属性)。

  9. Object.freeze()MDN

    可以冻结 一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

  10. Object.entries()

    返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值对数组。

  11. Object.prototype.hasOwnProperty()MDN

    会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

2.16 ES6 合并对象

  1. Object.assign() : 第一个参数是目标对象,后面的参数是源对象

    Object.assign(target, ...sources)

    js 复制代码
    let obj1= {a:9,d:19}
    let obj2 = {a:3,b:2}
    let obj = Object.assign(obj1,obj2)
    console.log(obj);// { a: 3, d: 19, b: 2 }
  2. ... {...obj1,...obj2}

    js 复制代码
    obj3 = {...obj1, ...obj2}
    console.log(obj3);// { a: 3, d: 19, b: 2 }

2.17 find 和 filter的区别

筛选数组中符合条件的元素。

filter: 返回一个新的数组,原数组不变。

find:返回第一个符合内容,原数组不变

js 复制代码
let arr = [13,1,2,4,6]
console.log(arr.find(bal =>{return bal>2})); // 13
console.log(arr.filter(bal =>{return bal>2})); // [13, 4, 6]

2.18 some 和 every的区别

Array.prototype.every():测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

Array.prototype.some():测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。

2.19 延迟加载JS的方式

  1. 将js脚本放在底部。

  2. defer:<script defer type="text/javascript" src="demo_async.js"></script>

    defer会让脚本和页面同时解析,当脚本解析完之后需要等到页面解析完成才执行脚本。

  3. async:<script async type="text/javascript" src="demo_async.js"></script>

​ async会让脚本和页面同时解析,当脚本解析完成之后立即执行脚本,如果这时候文档没有解析完会堵塞。多个 async 属性的脚本的执行顺序是不可预测的,谁先解析完谁先执行。

  1. 动态创建DOM标签的形式,我们可以对文档加载进行监听,当文档加载完成之后在动态的创建script标签来引入js脚本。

2.20 == 和 ===

隐式转换:通过valueOf转换(valueOf()方法通常由JavaScript在后台自动进行隐式转换。

== :等值符,比较值

===:等同符,比较值,也比较类型。

js 复制代码
null == undefined //true

NaN == NaN // false NaN和任何相比都是false

string == number//string转为number

boolean == number//boolean转为number

2.21 forEach、map、for...in、for...of

方式 取值方式 改变自身 改变item的属性
for list[index] 可以改变list[index] 基础类型不可以引用类型可以
for-in list[index] 可以改变list[index] 基础类型不可以引用类型可以
for-of item 不可以改变item 基础类型不可以引用类型可以
forEach item 不可以改变item 基础类型不可以引用类型可以
map item 不可以改变item 基础类型不可以引用类型可以

forEach VS map

相同点:

markdown 复制代码
1. 遍历数据的每个item,执行回调
1. 回调的参数都是一样的(item, index, array)=>{}
1. 可以修改原数组(地址)

不同:

arduino 复制代码
1. forEach执行之后不返回东西undefined,map执行之后返回的是一个新的经过回调处理完的数组。

for...in VS for...of

相同点:

markdown 复制代码
1. 遍历

不同点:

markdown 复制代码
1. for...in遍历索引,相当于for(),for...of遍历值value
1. for...in返回遍历数组的下标,for...of返回值
1. for...in适用于枚举(对象、数组、字符串),for...of适用于可迭代(数组、字符串、Map、Set)

注:可迭代:数组中有Symbol.iterator 属性;可枚举:属性的enumerable为true

如果一个属性的enumerable为false,下面三个操作不会取到该属性。

for..in循环

Object.keys方法

JSON.stringify方法

作者:华山令狐冲 链接:juejin.cn/post/714282...

2.22 Array.slice 和 Array.splice

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

slice() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝 (包括 begin,不包括end)。原始数组不会被改变。

3. 浏览器基础

3.1cookie

  1. 什么是cookie?

    cookie 是一种用于记录服务器与客户端对话状态的机制。

    HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息 ):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动 的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。

    cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。

    cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的靠的是 domain)

    • 本身用于浏览器和server通讯
    • 被"借用"到本地存储来的道
    • 可以用document.cookie='...'修改不知

    缺点:

    • 存储大小仅为4k

    • http请求时需要发送至服务器,增加请求数量

    • 只能用document.cookie='...'来修改

    • cookie安全性不够高。所有的cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理。

3.2 Session

session 是另一种记录服务器和客户端会话状态的机制

session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中

session 认证流程:

  • 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
  • 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器
  • 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
  • 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。

根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。


3.3 什么是Token(令牌)?

Acesss Token

  • 访问资源接口(API)时所需要的资源凭证

  • 简单token的组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

  • 特点:

    • 服务端无状态化、可扩展性好
    • 支持移动端设备
    • 安全
    • 支持跨程序调用
    • 判断有效性需要查询数据库
  • token的身份验证流程:

  1. 客户端使用用户名跟密码请求登录

  2. 服务端收到请求,去验证用户名与密码

  3. 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端

  4. 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里

  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 token

  6. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

  • 每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里

  • 基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库

  • token 完全由应用管理,所以它可以避开同源策略

3.4 Token 和 Session的区别

  • Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息 。而 Token 是令牌访问资源接口(API)时所需要的资源凭证 。Token 使服务端无状态化,不会存储会话信息。

  • Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。

  • 所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。

链接:juejin.cn/post/684490...

什么是 JWT?

  • JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。
  • 是一种认证授权机制
  • J
  • WT 是为了在网络应用环境间传递声明 而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
  • 可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。
  • 阮一峰老师的 JSON Web Token 入门教程 讲的非常通俗易懂,这里就不再班门弄斧了

原理:

  • JWT 认证流程

    • 用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT

    • 客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie

    • 当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样Authorization: Bearer <token>

    • 服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为

    • 因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要

    • 因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)

    • 因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制


**localStorage 和 sessionStorage**

  • HTML5 专门为存储来设计的,最大可存 5M。
  • API 简单易用, setItem getItem。
  • 不会随着 http 请求被发送到服务端。

它们的区别:

  • localStorage 数据会永久存储,除非代码删除或手动删除。
  • sessionStorage 数据只存在于当前会话,浏览器关闭则清空。
  • 一般用 localStorage 会多一些。

**什么是跨域?**

当一个请求url的协议、域名、端口三者之间任意一个与当前页面的url不同时即为跨域。

3.5 解决跨域

  1. 通过 jsonp 解决跨域

    JSONP 是服务器与客户端跨源通信的常用方法,最大特点就是简单适用,兼容性好,缺点是只能get请求,不支持post请求。

    核心思想:网页通过添加一个<script> 元素,向服务器请求JSON数据,服务器收到请求后将数据放到一个指定名字的回调函数参数位置传送回来。

    • 原生实现:

    • jQuery ajax:

      js 复制代码
      $.ajax({  
          url: 'http://www.test:8080/login',  
          type: 'get',  
          dataType: 'jsonp',  
          jsonpCallback: 'callBack',  
          data: {}, 
      })
    • Vue.js

      js 复制代码
      this.$http.jsonp('http://www.test:8080/login', {
        params: {},
        jsonp: 'callBack',
      }).then((res) => {
        console.log(res);x
      })
  2. 跨域资源共享(CORS)

  3. 使用 websocket 协议,这个协议没有同源限制。

  4. nginx 代理跨域,使用服务器来代理跨域的访问请求,就是有跨域的请求操作时发送请求给后端,让后端代为请求,然后最后将获取的结果发返回

3.6 AJAX

  1. ajax 的 readyState 有哪几个状态,含义分别是什么?
0 初始化 XMLHttpRequest对象还没有完成初始化
1 载入 XMLHttpRequest对象开始发送请求
2 载入完成 XMLHttpRequest对象的请求发送完成
3 解析 XMLHttpRequest对象开始读取服务器的响应
4 完成 XMLHttpRequest对象读取服务器响应结束

5 个状态,分别是 0-4 0 - (未初始化)还没有调用send()方法 1 - (载入)已调用send()方法,正在发送请求 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容 3 - (交互)正在解析响应内容 4 - (完成)响应内容解析完成,可以在客户端调用了

状态有0-4

0:XMLHttpRequest对象已经创建完成,此时还没有调用open

1.此阶段使用open方法对XMLHttpRequest进行初始化,设置请求的方式,url请求头等,在调用send方法后,状态的值仍然是1,

2.服务器响应成功,响应头已经被前端接受。可以通过XMLHttpRequest.getAllReaponse获取。 responseText为空

3.部分数据已经可以获取了, responseText已经有部分数据了

4.请求操作完成,响应数据全部接收完毕。

注意 2 3 4 这三种状态,只能在 onreadystatechange 会调用中才可以获取到

4.HTTP

4.0 HTTP & HTTPS基础

基本概念

http:是一个客户端与服务端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的超文本传输协议。

https:以安全为目的的HTTP通道,在HTTP的基础上添加了SSL层进行加密。建立了安全通道来确保数据的安全性.

区别&优缺点:(从安全、性能、证书等方面回答)

  • http的信息是明文传输,https加了SSL层可防止数据在传输过程中被窃取、改变,确保数据的完整性。
  • 默认端口不同,http为80,https为443
  • http是的连接比较简单,是无状态的连接,https握手阶段比较费时,会使得页面加载时间延长50%,增加10%~20%的耗电。
  • https的缓存不如http高效
  • https需要ca证书,费用较高,功能越强费用越多。
  • SSL证书需要绑定IP,不能在同一个IP绑定多个域名。

https协议的工作原理:

  1. 客户端使用https url访问服务器,要求建立SSL连接。
  2. web服务器收到请求之后,会将网站的证书(包含公钥),传给客户端
  3. 客户端与web服务器开始协商SSL连接的安全等级,即加密等级。
  4. 客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传给网站服务器。
  5. web服务器通过自己的私钥解密会话密钥。
  6. web服务器通过会话密钥加密与客户端的通信。

HTTP1.1 & HTTP/2

HTTP1.1的缺陷:

  1. 高延迟------队头阻塞
  2. 无状态特性------阻碍交互
  3. 明文传输------不安全
  4. 不支持服务器推送消息

HTTP/2

  1. 二进制传输

    使得传输的数据量大幅减少。

  2. Header压缩

  3. 多路复用

  4. Server Push

    HTTP2还在一定程度上改变了传统的"请求-应答"工作模式,服务器不再是完全被动地响应请求,也可以新建"流"主动向客户端发送消息。

  5. 提高安全性

HTTP/3

​ QUIC 基于 UDP 实现,是 HTTP/3 中的底层支撑协议,该协议基于 UDP,又取了 TCP 中的精华,实现了即快又可靠的协议。

4.1 http状态码

分类:

  • 1xx - 服务器收到请求
  • 2xx - 请求成功 , 如200
  • 3xx - 重定向 , 如302
  • 4xx - 客户端错误,如404
  • 5xx - 服务端错误,如500

常见的状态码

  • 200 - 成功
  • 301 - 永久重定向(配合location,浏览器自动处理)
  • 302 - 临时重定向 (配合location,浏览器自动处理)
  • 304 - 资源未被修改(协商缓存)
  • 403 - 没权限
  • 404 - 资源未找到
  • 500 - 服务器错误
  • 504 - 网关超时

关于协议和规范

  • 状态码都是约定出来的
  • 要求大家跟着执行
  • 不要违反规范,例如IE浏览器

4.2 http 缓存

【浏览器缓存】:浏览器缓存通过HTTP/HTTPS实现,存储的位置有 四种:

Service Worker

apl 复制代码
Service Worker 是运行在浏览器背后的独立线程,可以用来实现缓存功能。传输协议必须为 HTTPS。

Memory Cache(内存缓存)

apl 复制代码
	Memory Cache 是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据高效,但是缓存持续性很短。一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。而且由于计算机中的内存比硬盘容量小得多,我们能够使用存储缓存的内存并不多。

Disk Cache(硬盘缓存)

apl 复制代码
Disk Cache 是存储在硬盘中的缓存,读取速度比 Memory Cache 慢,但是存储量更大。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。

Push Cache(推送缓存)

apl 复制代码
Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。**它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂**,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。

如何查看缓存:

bash 复制代码
第一次打开该网站后,如果再次刷新页面。会发现浏览器加载的众多资源中,有一部分size有具体数值,然而还有一部分请求,比如图片、css和js等文件并没有显示文件大小,而是显示了 `from dis cache` 或者 `from memory cache` 字样。这就说明了,该资源直接从本地硬盘或者浏览器内存读取,而并没有请求服务器。
浏览器启用缓存至少有两点显而易见的好处:(1)减少页面加载时间;(2)减少服务器负载;
  • 关于缓存的介绍

    把一些不需要重新获取的内容再重新获取一次。网络请求相比于CPU计算和页面渲染是非常慢的。

    可以被缓存的资源:js、css、img等

  • http缓存策略(强制缓存 + 协商缓存)

    强制缓存

​ Cache-Control: 这在响应头Response Headers 中,是一个字段,控制强缓存的逻辑。

​ 例如:Cache-Control: max-age=3153600(单位是秒)

​ Cache-Control有哪些值:

max-age:缓存最大过期时间

no-cache:可以在客户端中存储资源,每次都必须区服务器中做新鲜度校验,来决定从服务器获取新的资源(200)还是使用客户端缓存(304)。

no-store:永远都不要再客户端存储资源,永远都去原始服务器中获取资源。

协商缓存

​ 服务器的缓存策略,服务器判断客户端资源是否和服务端的资源一致,一致则返回304 ,不一致返回200 和 最新的资源

​ 资源标识:

​ 在Response Headers 中有两种:

  1. Last-Modified:资源的最后修改时间。

  2. Etag:资源的唯一标识(一个字符串类似于人类的指纹)

    Last-Modified:

​ 服务端拿到 if-Modified-Since 之后拿这个时间去和服务端资源最后修改时间做比较,如果一致则返回 304 ,不一致(也就是资源已经更新了)就返回 200 和新的资源及新的 Last-Modified。

Etag

​ 其实 Etag 和 Last-Modified 一样的,只不过 Etag 是服务端对资源按照一定方式(比如 contenthash)计算出来的唯一标识,就像人类指纹一样,传给客户端之后,客户端再传过来时候,服务端会将其与现在的资源计算出来的唯一标识做比较,一致则返回 304,不一致就返回 200 和新的资源及新的 Etag。

Etag 和 Last-Modified 的比较:

  • 优先使用 Etag。
  • Last-Modified只能精确到秒级。
  • 如果资源被重复生成,而内容不变,则Etag更加精确。

以上综述:

  • 强制缓存的优先级高于协商缓存:

    • 强制缓存中:cache-control 的 max-age 优先级高于 Expires
    • 协商缓存中:Etag 优先级比 Last-Modified 高。

三种刷新操作:

​ 正常操作:地址栏输入url,跳转链接,前进后退等。

​ 手动刷新:f5,点击刷新按钮,右键菜单刷新

​ 强制刷新:Ctrl + f5,shift + command + r

正常刷新:强制缓存有效,协商缓存有效。

手动刷新:强制缓存失效,协商缓存有效。

强制刷新:强制缓存失效,协商缓存失效。

  • 刷新操作方式对缓存的影响

4.3 TCP

4.4 HTTP报文

一个http请求由请求行、请求头、空行和请求数据4个部分组成。

  1. 请求行

    1. 请求方法
      • GET
      • POST
      • HEAD
      • DELETE
      • OPTIONS
      • PUT
      • TRACE
      • CONNECT
  2. 请求头

    • User-Agent : 产生请求的浏览器类型
    • Accept : 客户端希望接受的数据类型,比如 Accept:text/xml(application/json)表示希望接受到的是xml(json)类型
    • Content-Type:发送端发送的实体数据的数据类型。 比如,Content-Type:text/html(application/json)表示发送的是html类型。
    • Host : 请求的主机名,允许多个域名同处一个IP地址,即虚拟主机
  3. 空行

​ 请求头之后是一个空行,通知服务器以下不再有请求头

  1. 请求体

GET没有请求数据,POST有。

与请求数据相关的最常使用的请求头是 Content-Type 和 Content-Length

5.Vue

5.1. 简述MVVM (优点)

​ 视图模型双向绑定,是Model-View-ViewModel的缩写,

​ M:Model 模板 data中的数据

​ V:View 视图 模板代码

​ VM:ViewModel 视图模型 Vue实例

回答:MVVM模型就是视图模型双向绑定,vue框架的设计参考了MVVM模型,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。以前是操作DOM结构更新视图,现在是数据驱动视图。这样在一个大的项目中可以做到独立开发,开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面的设计。

MVVM的优点:

  1. 低耦合。视图可以独立于Model变化和修改,一个Model可以绑定到不同的View上,当View变化的时候Model可以改变。
  2. 可复用性。可以把一些视图逻辑放到一个Model里面,让很多view重复使用。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
  4. 可测试。

5.2. Vue底层实现原理

vue的底层原理:vue.js采用的是数据劫持 结合发布者-订阅者模式 的方式,通过object.defineProperty()来劫持各个属性的setter和getter,在数据变动时发布消息给订阅者,触发相应的箭头函数回调。

数据代理:通过一个对象对另一个对象的属性的操作(读/写)。\

vue使用数据代理的好处:更加方便的操作data的数据。
object.defineProperty()的使用:直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。MDN

Object.defineProperty(obj, prop, descriptor)

vue的实例vm将模型数据添加到vm的_data中,并且通过Object.defineProperty(vm,key,{}),将data中的每个数据属性设置getter和setter来代理,在getter/setter内部去操作(读/写)data中的属性。

但是_data中也有getter和setter,这主要是用于数据劫持,用于更新界面实现响应式。

Observer(数据监听器):observer的核心是通过Object.defineProperty()来监听数据的变动的,这个函数内部可以定义setter和getter,每当数据发生变化,就会触发setter。这时候Observer就要通知订阅者,订阅者就是Watcher。

Watcher(订阅者):Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情:

  1. 在自身实例化时往属性订阅器(dep)里面添加自己。
  2. 自身必须有一个update()方法
  3. 待属性变动dep.notice()通知时,能够调用自身的updata()方法,并触发Compile中绑定的回调函数。

Compile(指令解析器):Compile主要做的事情是解析模板指令,将模板变量替换数据,然后初始化渲染页面的视图,并将每个指令对应的节点绑定更新回调函数,添加鉴定数据的订阅者,一旦数据有变动,收到通知,更新视图。

5.3. 谈谈对vue生命周期的理解

每个vue实例在创建是都会经历一系列的初始化过程,vue的生命周期钩子,就是说在达到某个 阶段或条件触发的函数,目的就是为了完成一些动作或事件。

  • create阶段:vue实例被创建

beforeCreate,创建前,此时data和methods中的数据还没有初始化

created,创建完毕,data中有值,没挂载,

  • mount阶段:vue实例被挂载到真实DOM节点

    beforemount,可以发起服务端请求,取数据

    mounted,此时可以操作DOM

  • update阶段:当实例里面的data数据发生变化时,触发组件的重新渲染

beforeUpdate,更新前,数据是新的,但是页面是旧的,页面尚未和数据保持同步

updated,更新后,数据是新的,页面也是新的,页面和数据保持一致

  • destroy阶段:vue实例被销毁

beforeDestroy,实例被销毁前,此时可以手动销毁一些方法,data、methods、指令等等都处于可用,完成一些收尾工作。

destroyed,销毁后

组件生命周期

生命周期(父子组件) 父组件beforeCreate --> 父组件created --> 父组件beforeMount --> 子组件beforeCreate --> 子组件created --> 子组件beforeMount --> 子组件 mounted --> 父组件mounted -->父组件beforeUpdate -->子组件beforeDestroy--> 子组件destroyed --> 父组件updated

加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

挂载阶段 父created->子created->子mounted->父mounted

父组件更新阶段 父beforeUpdate->父updated

子组件更新阶段 父beforeUpdate->子beforeUpdate->子updated->父updated

销毁阶段 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

bash 复制代码
vue的生命周期是指vue实例从创建到销毁的过程,即创建前后、挂载前后、更新前后、销毁前后八个生命周期钩子。
beforeCreate:无$el,无$data,
created:无$el,有$data,
beforeMount:无$el,有$data,
mounted:有$el,有$data,
beforeUpdate:有$el,有$data,
updated:有$el,有$data,
beforeDestroy:有$el,有$data,
destroyed:有$el,有$data,
vue实例创建,会执行前四个生命周期。beforeCreate、created、beforeMount、mounted

*如果加入keep-alive会多两个生命周期:
activated:
deactivated:
如果加入keep-alive,第一次进入组件,会执行前五个生命周期。beforeCreate、created、beforeMount、mounted、activated
如果加入keep-alive,第二次进入组件或第n次,会执行1个生命周期activated

生命周期的使用场景:(created有什么应用场景,mounted有什么应用场景)

(提出问题------解决问题)

  • created:通常用于初始化某些属性值,例如data中的数据,比如进行Ajax异步请求的数据获取、初始化数据。然后再渲染成视图。前后端数据交互,传送门axios
  • mounted:通常在初始化页面完成后,对html的dom节点进行需要的操作

5.4 组件中的data为什么是一个函数

  1. 一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是一个构造函数
  2. 如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。

5.5 为什么v-for和v-if不建议用在一起

  1. 当v-for和v-if处于同一个节点时,v-for的优先级高 ,这意味着v-if将分别重复运行与每个v-for循环中。如果要遍历的数组很大,而真正要展示的数据很少,这将造成很大的性能浪费。
  2. 这种场景建议使用computed,先对数据进行过滤。

5.6 React/vue项目中key的作用

  1. key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,更高效的更新虚拟DOM
  2. 为了在数据变化时强制更新组件,以避免"就地复用"带来的副作用。

5.7 vue组件的通信方式

bash 复制代码
props/$emit / 父子组件通信 父->子 props 子->父 $on、$emit
$emit / $on
Vuex跨级组件通信
全局事件总线
消息订阅与发布(pubsub)

5.8 nextTick的实现

nextTick:获取更新后的DOM的内容,使用场景:????

使得闯进去的函数被加载到异步栈中执行

js 复制代码
onCreated(() => {
    console.log(11);
    this.$nextTick(() => {
        console.log( 333 );
    })
})
onMounted(() =>{
    console.log(22);
    this.$nextTick(() => {
        console.log(44);
    })
}) 
//输出:11 22 33 44

5.9 nextTick的实现原理是什么

  1. 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

    我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新

    参考答案

5.10 使用过插槽吗?用的是具名插槽还是匿名插槽。

slot(插槽),一个占位的东西,vue中插槽分为三种:

  1. 默认插槽(匿名)
  2. 具名插槽
  3. 作用域插槽

5.11 keep-alive的实现

keep-alive是vue自带的一个组件,功能是用来缓存组件,用于提升性能。

使用场景:就是缓存组件,提升项目的性能。具体实现:比如从首页进入到详情页,点击不同的选项进入到不同的详情页,如果用户在首页每次点击都是相同的,那么详情页就没必要请求N次了,可以直接缓存起来,如果点击的不是同一个,那么就直接请求即可。

5.12. mixin

​ 当项目变得很复杂的时候,多个组件间有重复的逻辑就会用到mixin。多个组件有相同的逻辑就抽离出来。

参考答案

mixin并不是完美的解决方案,会有一些问题 vue3提出的Composition API旨在解决这些问题【追求完美是要消耗一定的成本的,如开发成本】 场景:PC端新闻列表和详情页一样的右侧栏目,可以使用mixin进行混合 劣势:

​ 1.变量来源不明确,不利于阅读

​ 2.多mixin可能会造成命名冲突

​ 3.mixin和组件可能出现多对多的关系,使得项目复杂度变高

5.13. Vuex的理解及使用场景。

​ Vuex是一个专为Vue应用程序开发的状态管理模式。每个Vuex应用的核心就是store(仓库)

markdown 复制代码
1. Vuex的状态存储是响应式的;当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应的得到高效的更新。

2. 改变store中的状态的唯一途径就是显示地提交(commit)mutation,这样使得我们可以方便地跟踪每一个状态的变化Vuex主要包括以下几个核心模块:

1. State:定义了应用的状态数据

2. Getter:在store中定义"getter"(可以认为是store的计算属性),

   就像计算属性一样,getter的返回值会根据它的依赖性被缓存起来,且只有当他的依赖值发生改变才会被重新计算。

3. Mutation:是唯一更改store中的状态方法,且必须是同步函数。

4. Action:用于提交mutation,而不是直接变更状态,可以包含任意异步操作。

5. Module:允许将单一的Store拆分为多个store且同时保存在单一的状态树中。

   ![img](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a7249773a1634f779c48f3f0ffabf968~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp)

5.14 vue3.0 中为什么要使用 Proxy,它相比以前的实现方式有什么改进

​ Proxy 可以直接监听对象而非属;

​ Proxy 可以直接监听数组的变化;

​ Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等,这些是Object.defineProperty不具备的。

​ Proxy 返回的是一个新的对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改。

​ Proxy作为新的标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利。

5.15 vue3 中,如何监听数组的变化。

5.16 v-if与v-show的区别

v-if : 创建一个DOM。

v-show:元素存在,但不展示即显示隐藏。display:none;

使用场景

​ 首次加载,v-if快,因为第一次加载的时候这个元素并不存在,触发的时候才创建,但是v-show是第一次加载的时候就存在了,不触发的时候也存在,开始开销慢。

​ 频繁切换的时候v-show更好,创建和删除的开销太大了,显示和隐藏开销比较小。

5.17 vue-router 是什么?

vue-router 是vue.js官方的路由插件,他和vue.js是深度集成的,基于路由与组件的,路由用于设定访问路径,将路径与组件映射起来实现页面切换。

5.18 vue-router 导航钩子(路由守卫)

路由钩子函数有三种:

  1. 全局钩子:beforeEach、afterEach、beforeResolve

  2. 单个路由里面的钩子:beforeEnter

  3. 组件路由:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

    beforeEach:全局前置守卫:初始化时执行、每次路由切换前执行

    afterEach:全局后置守卫:初始化时执行、每次路由切换后执行

    beforeResolve:路由解析之前

    路由钩子

5.19 history模式与hash模式

  1. 对于一个url来说,什么是hash值?------ #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

5.20 消息发布与订阅模式

  1. 一种组件间通信的方式,适用于任意的组件间通信。

  2. 使用步骤:

    kotlin 复制代码
    1. 安装pubsub: npm i pubsub-js
    
    	2. 引入 ```import pubsub from 'pubsub-js'```
    
    	3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的<span style="color:red">回调留在A组件自身。</span>
    
    ```js
    methods(){
      demo(data){......}
    }
    ......
    mounted() {
      this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
    }
    ```
    
    	4. 提供数据:pubsub.publish('xxx',数据)
    	
    	5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)

5.21 vue中watch和 computed的区别

computed是计算属性的意思,watch是监听的意思

  • computed是用来计算出来一个值的,这个值调用的时候不需要加括号,会根据依赖进行缓存,依赖不变,computed的值不会重新计算

  • watch是来监听的,当数据发送变化时,执行一个函数,有2个选项,

​ immediate:表示是否要在第一次渲染的时候执行这个函数

​ deep:如果我们监听一个对象,那么我们是否要看这个对象里面属性的变化

​ 如果某个属性变化了,就去执行一个函数

js 复制代码
(1)computed 是计算一个新的属性,并将该属性挂载到 Vue 实例上,而 watch 是监听已经存在且已挂载到 Vue 实例上的数据,所以用 watch 同样可以监听 computed 计算属性的变化。

(2)computed 本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,第一次访问 computed 属性,才会计算新的值。而 watch 则是当数据发生变化便会调用执行函数。

(3)从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据。

5.22 <math xmlns="http://www.w3.org/1998/Math/MathML"> r o u t e 和 route 和 </math>route和router 的区别?

<math xmlns="http://www.w3.org/1998/Math/MathML"> r o u t e 是"路由信息对象",包括 p a t h , p a r a m s , h a s h , q u e r y , f u l l P a t h , m a t c h e d , n a m e 等路由信息参数。而 route 是"路由信息对象",包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。而 </math>route是"路由信息对象",包括path,params,hash,query,fullPath,matched,name等路由信息参数。而router 是"路由实例"对象包括了路由的跳转方法,钩子函数等。

5.23 vuex

​ vuex:一个专为vue.js应用程序开发的状态管理模式+库。它采用集中式存储管理应用的所有组件状态,并以相应的规则保证状态以一种可预测的方式发生变化。也是一种组件通信的方式,且适用于任意组件键通信。

Action

  • Action类似于mutation,不同在于:
markdown 复制代码
- Action提交的是`mutation`,不是变更状态
- Action可以包含异步操作,而mutation只能是同步操作

Mutation

  • 类似于组件中的methods

  • 更改vuex中store状态的唯一方法是提交Mutation,类似于事件,每个Mutation都有一个字符串的事件类型type和一个回调函数handler。他会接受state作为第一个参数。

    js 复制代码
    const store = createStore({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })

    唤醒一个mutation处理函数:

    js 复制代码
    store.commit('increment')

State

​ 存储状态,类似于组件中的data

Getter

  • 类似于组件中的computed

​ 姑且认为是store的计算属性

js 复制代码
const store = createStore({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})	

Module

  • 把其他四个属性再细分,让仓库便于管理

5.24 讲一下hook

5.25 说一下vue的优缺点

优点:

  1. MVVM模型,数据驱动Dom,使我们更加专注于数据层
  2. MVVM模型让大型的项目更好的维护。
  3. spa单页面应用开发更加方便。

缺点:

  1. vue直接的框架不可以做seo,如果要做的话需要预渲染或服务器端渲染。
  2. 用js去渲染数据,第一次会造成重绘或者回流,性能有一定的影响

6.性能优化

代码层面:

diff 复制代码
- 防抖和节流(resize、scroll、input)
- 减少回流(重排)和重绘
- 事件委托
- css放顶部,js脚本放最底部。
- 减少DOM操作。
- 按需加载0

构建方面:

  • 压缩代码文件,在webpack中使用terser-webpack-plugin压缩JavaScript代码;使用css-minimizer-webpack-plugin压缩CSS代码,使用html-webpack-plugin压缩html代码。
  • 开启gzip压缩,webpack中使用compression-webpack-plugin,node作为服务器也要开启,使用compression。
  • 常见的第三方库使用CDN服务,在webpack中我们要配置externals,将比如React、vue这种包不打倒最终生成的文件中。而是采用CDN服务。

其他:

  • 响应式,面对不同的设备可以监听设备的变化,使用不同的图片

  • 使用http2.因为解析速度快,头部压缩、多路复用,服务器推送静态资源。

  • 使用服务器渲染。

  • 图片压缩。

    使用http缓存,比如服务器的响应中添加Cache-Control /Expires。

7.手写

1. 防抖

防抖: 多次触发事件,事件处理函数只能执行一次,并且是在触发操作结束时执行。也就是说,当一个事件被触发准备执行事件函数前,会等待一定时间(这个时间是自己定义的,比如1s),如果没有再次被触发,那么执行,如果被触发了,那就本次作废,重新触发的时间开始计算,并再次等待1秒,直到最终执行。

使用场景: 搜索框搜索输入,并在输入完成以后自动搜索,手机号,邮箱验证输入检测、窗口resize变化后重新渲染。

2. 节流

节流: 事件触发之后,规定时间内,处理函数不能再次被调用。也就是说在规定时间内函数只能调用一次,且是最先被触发调用的那次。

使用场景: 滚动加载更多、搜索框的索联想功能、高频点击、表单重复提交....

3. 快排

4. instanceof

5. 数组扁平化

6. 手写reduce

7. 带并发的异步调度器Scheduler

8. 去重

8. promise

基本使用

js 复制代码
let p = new Promise((resolve, reject) => {
  // 做一些事情
  // 然后在某些条件下resolve,或者reject
  if (/* 条件随便写^_^ */) {
    resolve()
  } else {
    reject()
  }
})

p.then(() => {
    // 如果p的状态被resolve了,就进入这里
}, () => {
    // 如果p的状态被reject
})

第一段调用了Promise构造函数,第二段是调用了promise实例的.then方法

  1. 构造实例
  • 构造函数接受一个函数作为参数
  • 调用构造函数得到实例p的同时,作为参数的函数会立即执行
  • 参数函数接受两个回调函数参数resolve和reject
  • 在参数函数被执行的过程中,如果在其内部调用resolve,会将p的状态变成fulfilled,或者调用reject,会将p的状态变成rejected
  1. 调用.then
  • 调用.then可以为实例p注册两种状态回调函数
  • 当实例p的状态为fulfilled,会触发第一个函数执行
  • 当实例p的状态为rejected,则触发第二个函数执行

总结

上面这样构造promise实例,然后调用.then.then.then的编写代码方式,就是promise。

其基本模式是:

  • 将异步过程转化成promise对象
  • 对象有3种状态
  • 通过.then注册状态的回调
  • 已完成的状态能触发回调

采用这种方式来处理编程中的异步任务,就是在使用promise了。

所以promise就是一种异步编程模式。

首先,promise实例有三种状态:

  • pending(进行中)
  • fulfilled(已执行)
  • rejected(已拒绝)

fulfilled和rejected有可以说是已成功和已失败,这两种状态又归为已完成状态

resolve和reject

调用resolve和reject能将分别将promise实例的状态变成fulfilled和rejected,只有状态变成已完成(即fulfilled和rejected之一),才能触发状态的回调

Promise API

promise的内容分为构造函数、实例方法和静态方法

  • 1个构造函数: new Promise
  • 2个实例方法:.then.catch
  • 4个静态方法:Promise.allPromise.racePromise.resolvePromise.reject

new Promise能将一个异步过程转化成promise对象。先有了promise对象,然后才有promise编程方式。

  1. .then用于为promise对象的状态注册回调函数 。它会返回一个promise对象,所以可以进行链式调用,也就是.then后面可以继续.then。在注册的状态回调函数中,可以通过return语句改变.then返回的promise对象的状态,以及向后面.then注册的状态回调传递数据;也可以不使用return语句,那样默认就是将返回的promise对象resolve。
  2. .catch用于注册rejected状态的回调函数 ,同时该回调也是程序出错的回调,即如果前面的程序运行过程中出错,也会进入执行该回调函数。同.then一样,也会返回新的promise对象。
  3. 调用Promise.resolve会返回一个状态为fulfilled状态的promise对象,参数会作为数据传递给后面的状态回调函数
  4. Promise.reject与Promise.resolve同理,区别在于返回的promise对象状态为rejected

Promise.all 用于将多个 Promise 实例,包装成一个新的 Promise 实例。

js 复制代码
const p = Promise.all([p1, p2, p3]);

上面代码中,Promise.all()方法接受一个数组作为参数,p1p2p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.race 同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

js 复制代码
const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.resolve 有时需要将现有对象转为 Promise 对象

Promise.reject 会返回一个新的 Promise 实例,该实例的状态为rejected


9.随手笔记

9.1 cookie、sessionStorage、localStorage的区别:

cookie:

  1. 生命期为设置的cookie过期时间之前有效,即窗口或浏览器关闭之前
  2. 存放数据大小4k左右
  3. 有个数限制
  4. 与服务器通讯,每次都会携带在HTTP头中,如果使用cookie过多会有性能问题。

LocalStorage:

  1. 生命周期永久,除非手动清除
  2. 存放数据大小5MB
  3. 只在客户端保存,不参与服务器通讯

SessionStorage:

  1. 生命期为当前会话有效,关闭页面会被清除
  2. 存放数据大小5MB
  3. 只在客户端保存,不参与服务器通讯

9.2 前端如何性能优化

  1. 减少HTTP请求的数量。(主要原因:消耗浏览器资源,每次请求都TCP三次握手,有性能的消耗)

    懒加载:Lazy load 网页一开始无序加载,等待用户需要这个资源的时候才开始加载。

    精灵图:CSS Sprites,将多张图合并为一张图,减少资源的请求。

    合并css和js:如grunt 、 gulp、webpack,、rollup等等。为了减少HTTP的请求数量,可以通过这些工具在发布前将多个CSS或者多个JS合并成一个文件。

  2. 控制资源文件加载的优先级。

    浏览器在加载HTML的内容时,是将HTML内容从上至下依次解析,解析到link或者script标签就会加载href或者src对应链接内容,为了第一时间展示页面给用户,就需要将CSS提前加载,不要受 JS 加载影响。一般情况都是CSS在头部,JS在底部 。

  3. 利用浏览器缓存。

  4. 减少重排reflow。

  5. 减少操作Dom。

  6. 图标使用IconFont替换。

11.Vue&React&Angular

链接:zhuanlan.zhihu.com/p/359540593

Angular

Angular 框架属于 MEAN 框架,是如今创业公司最热门的技术栈。Angular 是一个完整的基于 TypeScript 的 Web 应用开发框架,主要用于构建单页 Web 应用(SPA)。

与 AngularJS 这一早期的框架不同,Angular2 是基于组件的,与 MV* 模式没有什么关联。Angular 的结构方式包括模块、组件和服务。

在 Angular 框架中,每个组件都有一个类或模板,定义了应用逻辑和 MetaData(装饰器)。组件的这些元数据为创建和呈现其视图所需的构件在哪里提供了指引。

Angular 架构的另一个重要因素是,模板是用 HTML 编写的。它们还可以包含 Angular 模板语法,并带有特殊指令以输出响应式数据,并且可以渲染多个元素。

服务 ------ Angular 应用中的一个独特元素,被 Components 用于委托业务逻辑任务,如获取数据或验证输入。虽然使用服务并没有严格执行,但是将应用程序结构作为一组可复用的不同服务则是比较明智的。

React

React 是一个开源的前端库,主要用于开发用户界面。这种灵活的前端解决方案并不强制执行特定的项目结构。一个 React 开发者可能只需要几行代码就可以开始使用它。

React 是基于 JavaScript 的,但在大多数情况下,它与 JSX(JavaScript XML)相结合。JSX 是一种语法扩展,允许开发人员同时创建包含 HTML 和 JavaScript 的元素。实际上,开发者可以用 JSX 创建的任何东西也可以用 React JavaScript API 创建。React 元素比 DOM 元素更强大,它们是 React 应用的最小组成部分,即组件。

React 组件是一种构建模块,它决定了在整个 Web 应用中使用独立和可重用的组件。

Vue

用于开发用户界面和单页 Web 应用,Vue 是一个开源的 Model-View-View-Model (MVVM) 前端 JavaScript 库。它被称为渐进式框架,与其它工具一起被用于前端开发。Vue 的多用途、高性能和它在 Web 应用程序上的最佳用户体验成就了它的流行。

使用 Vue 时,开发者主要在 ViewModel 层上工作,以确保应用数据的处理方式能让框架呈现最新的视图。

Vue 的模板语法将可识别的 HTML 与特殊的指令和功能相结合。该语法允许开发人员创建 View 组件。

现在 Vue 中的组件是小巧、自成一体和可复用的。单文件组件(SFC)使用扩展名 .vue ,包含 HTML、JavaScript 和 CSS,因此所有相关代码都存放在同一个文件中。

在大型的 Vue.js 项目中,我们通常推荐使用 SFC 来组织代码。要将 SFC 移植到工作的 JavaScript 代码中,你需要 Webpack 或 Browserify 这样的构建工具。

12. webpack

官网文档

1.webpack 是用来做什么的,原理是什么

webpack启动之后,会从entry开始,递归解析entry依赖的所有module,找到每个module.rules里的配置的loader进行相应的转换处理,对module转换后,解析出当前model依赖的其他模块,解析的结果使一个个chunk,最后webpack会将所有chunk转换成文件输出的output 。

在整个构建过程中,webpack会执行plugin当中的插件,完成plugin的任务。

2.webpack 中的 loader 的作用是什么

官网:loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中"任务(task)",并提供了处理前端构建步骤的强大方法。

loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

3. webpack中的Loader和Plugin区别

区别链接

什么是plugin?

  • plugin是插件的意思,通常用于对现有的架构进行扩展。webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等等。

loader和plugin的区别:

  • loader主要用于转换某些类型的模块,是一个加载器
  • plugin是插件,对webpack本身进行扩展,是一个扩展器

plugin的使用步骤:

  1. 通过npm 安装需要使用的plugins(有些内置的不需要再安装);
  2. 在webpack.config.js中的plugins中配置插件
配置

webpack.config.js

javascript 复制代码
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装
const webpack = require('webpack'); //访问内置的插件
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    filename: 'my-first-webpack.bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader'
      }
    ]
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

4. 配置项:entry、output、module、plugin、devServer

  • entry:模块入口,使得源文件加入到构建流程中
  • output:配置如何输出最终
  • module: 配置各种类型模块的处理规则
  • plugin:配置扩展插件
  • devServer: 实现本地服务:包括http、 模块热替换、source map等服务

相关推荐
咔咔库奇1 小时前
【react】基础教程
前端·react.js·前端框架
前端小王hs1 小时前
MySQL后端返回给前端的时间变了(时区问题)
前端·数据库·mysql
千篇不一律1 小时前
工作项目速刷手册
服务器·前端·数据库
阿丽塔~3 小时前
vue3 下载文件 responseType-blob 或者 a标签
前端·vue·excel
七灵微4 小时前
【前端】Axios & AJAX & Fetch
前端·javascript·ajax
究极无敌暴龙战神X5 小时前
一篇文章学懂Vuex
前端·javascript·vue.js
shaoin_25 小时前
Vue3中ref与reactive的区别
前端·vue.js
院人冲冲冲5 小时前
微前端qiankun打包部署
开发语言·前端·javascript
我命由我123456 小时前
微信小程序 - 页面跳转(wx.navigateTo、wx.redirectTo、wx.switchTab、wx.reLaunch)
前端·微信小程序·小程序·前端框架·html·html5·js
肖老师xy6 小时前
uniapp修改picker-view样式
前端·uni-app