【八股文】---前端面试经验总结

前言

一、HTML+CSS

基础知识icon-default.png?t=M4ADhttps://zhuanlan.zhihu.com/p/28415923

  • HTML与CSS基础总结
  • 一. 认识WEB
    • 1.浏览器的分类和内核
    • 2.图解web标准
  • 二. HTML基础
    • 1.文档类型
    • 2.页面语言lang
    • 3. 字符集
    • 4.标签语义化
    • 5.锚点定位
    • 6.base 标签
    • 7.特殊符号
  • 三. CSS选择器
    • 1.三种样式表总结
    • 2.CSS基础选择器
    • 3.font字体
    • 4.CSS外观属性总结
    • 5.链接伪类选择器
    • 6.复合选择器总结
    • 7.背景总结
  • 四. CSS的三大特性
    • 1.层叠性
    • 2.继承性
    • 3.优先级
  • 五. CSS布局问题与边框阴影样式
    • 1.外边距合并
    • 2.圆角边框
    • 3.盒子阴影和文字阴影

二、JavaScript基础

1. 数据类型

在 JS 中共有 8 种基础的数据类型,分别为: UndefinedNullBooleanNumberStringObjectSymbolBigInt

  • Symbol 代表独一无二的值,最大的用法是用来定义对象的唯一属性名。
  • BigInt 可以表示任意大小的整数。

区别

  • 值类型是直接存储在**栈(stack)**中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
  • 引用类型存储在**堆(heap)**中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;

数据类型的判断

  1. typeof:能判断所有值类型,函数。不可对 null、对象、数组进行精确判断,因为都返回 object 。
    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
    

  2. instanceof:能判断对象类型,不能判断基本数据类型,其内部运行机制是判断在其原型链中能否找到该类型的原型
    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
    

  3. Object.prototype.toString.call():所有原始数据类型都是能判断的,还有 Error 对象,Date 对象等。
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]"

如何判断变量是否为数组

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

2. 数组去重

  1. indexOf 循环去重
  2. ES6 Set 去重;Array.from(new Set(array))
  3. Object 键值对去重;把数组的值存成 Object 的 key 值,比如 Object[value1] = true, 在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。

3.闭包

什么是闭包

闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是 就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈 上分配而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。

为什么要用闭包

匿名自执行函数:我们知道所有的变量,如果不加上 var 关键字,则默认的会添加到全 局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误 用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链 上遍历的)。除了每次使用变量都是用 var 关键字外,我们在实际情况下经常遇到这样一 种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。 结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象, 每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函 数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如 果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部 的引用,从而函数内部的值可以得以保留。 封装:实现类和继承等。

应用

函数作为参数被传递

function print(fn) {
  const a = 200;
  fn();
}

const a = 100;
function fn() {
  console.log(a);
}

print(fn); // 100

函数作为返回值被返回:

function create() {
  const a = 100;

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

const fn = create();
const a = 200;
fn(); // 100

4. 浅拷贝和深拷贝

数组的浅拷贝: 如果是数组,我们可以利用数组的一些方法,比如 slice,concat 方法返回一个新数组的 特性来实现拷贝,但假如数组嵌套了对象或者数组的话,使用 concat 方法克隆并不完整, 如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或数组,就会只拷 贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化,我们 把这种复制引用的拷贝方法称为浅拷贝, 深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也互相分离,修改一个对象 的属性,不会影响另一个 如何深拷贝一个数组 1、这里介绍一个技巧,不仅适用于数组还适用于对象!那就是:

var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]
var new_arr = JSON.parse( JSON.stringify(arr) );
console.log(new_arr);

原理是 JOSN 对象中的 stringify 可以把一个 js 对象序列化为一个 JSON 字符串,parse 可 以把 JSON 字符串反序列化为一个 js 对象,通过这两个方法,也可以实现对象的深复制。 但是这个方法不能够拷贝函数 浅拷贝的实现: 以上三个方法 concat,slice ,JSON.stringify 都是技巧类,根据实际项目情况选择使用,我 们可以思考下如何实现一个对象或数组的浅拷贝,遍历对象,然后把属性和属性值都放 在一个新的对象里即可

var shallowCopy = function(obj) {
// 只拷贝对象
if (typeof obj !== 'object') return;
100
// 根据 obj 的类型判断是新建一个数组还是对象
var newObj = obj instanceof Array ? [] : {};
// 遍历 obj,并且判断是 obj 的属性才拷贝
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;

深拷贝的实现 那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类型, 如果是对象,我们递归调用深拷贝函数就可以了

var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}

手写深拷贝

/**
 * 深拷贝
 * @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(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;
}

5. 原型和原型链

  • 原型:每一个 JavaScript 对象(null 除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,其实就是 prototype 对象。
  • 原型链:由相互关联的原型组成的链状结构就是原型链。

深入了解可以阅读:JavaScript 深入之从原型到原型链和轻松理解 JS 原型原型链

6. 作用域和作用域链

  • 作用域:规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。换句话说,作用域决定了代码区块中变量和其他资源的可见性。(全局作用域、函数作用域、块级作用域)
  • 作用域链:从当前作用域开始一层层往上找某个变量,如果找到全局作用域还没找到,就放弃寻找 。这种层级关系就是作用域链。(由多个执行上下文的变量对象构成的链表就叫做作用域链,学习下面的内容之后再考虑这句话)

可以阅读JavaScript 深入之词法作用域和动态作用域和深入理解 JavaScript 作用域和作用域链。

​7.执行上下文

当 JavaScript 代码执行一段可执行代码时,会创建对应的执行上下文。对于每个执行上下文,都有三个重要属性:

  • 变量对象(Variable object,VO);
  • 作用域链(Scope chain);
  • this。

推荐阅读:

  • JavaScript 深入之执行上下文栈;
  • JavaScript 深入之变量对象;
  • JavaScript 深入之作用域链;
  • JavaScript 深入之执行上下文。
  • JavaScript 的 this 原理
  • JavaScript 深入之闭包

8. call、apply、bind 实现

call

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

举个例子:

var obj = {
  value: "vortesnail",
};

function fn() {
  console.log(this.value);
}

fn.call(obj); // vortesnail

通过 call 方法我们做到了以下两点:

  • call 改变了 this 的指向,指向到 obj 。
  • fn 函数执行了。

那么如果我们自己写 call 方法的话,可以怎么做呢?我们先考虑改造 obj 。

var obj = {
  value: "vortesnail",
};

function fn() {
  console.log(this.value);
}

fn.call(obj); // vortesnail

这时候 this 就指向了 obj ,但是这样做我们手动给 obj 增加了一个 fn 属性,这显然是不行的,不用担心,我们执行完再使用对象属性的删除方法(delete)不就行了?

obj.fn = fn;
obj.fn();
delete obj.fn;

根据这个思路,我们就可以写出来了:

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

我们会了 call 的实现之后,apply 就变得很简单了,他们没有任何区别,除了传参方式。

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

bind 返回的是一个函数,这个地方可以详细阅读这篇文章,讲的非常清楚:解析 bind 原理,并手写 bind 实现。

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)
    );
  };
};

9. new关键字

  1. 首先创一个新的空对象。
  2. 根据原型链,设置空对象的 __proto__ 为构造函数的 prototype 。
  3. 构造函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)。
  4. 判断函数的返回值类型,如果是引用类型,就返回这个引用类型的对象。

指向问题

  • 默认绑定:全局环境中,this 默认绑定到 window。 隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this 隐式绑定 到该直接对象。
  • 隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到 window。
  • 显 式绑定:通过 call()、apply()、bind()方法把对象绑定到 this 上,叫做显式绑定。
  • new 绑定:如果函数或者方法调用之前带有关键字 new,它就构成构造函数调用。对于 this 绑定来说,称为 new 绑定。

【1】构造函数通常不使用 return 关键字,它们通常初始化新对象,当构造函数的函数 体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这 个新对象的值。

【2】如果构造函数使用 return 语句但没有指定返回值,或者返回一个原始值,那么这 时将忽略返回值,同时使用这个新对象作为调用结果。

【3】如果构造函数显式地使用 return 语句返回一个对象,那么调用表达式的值就是这 个对象。

10. Promise

实现一个promise.all

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    // 参数可以不是数组,但必须具有 Iterator 接口
    if (typeof promises[Symbol.iterator] !== "function") {
      reject("Type error");
    }
    if (promises.length === 0) {
      resolve([]);
    } else {
      const res = [];
      let count = 0;
      const len = promises.length;
      for (let i = 0; i < len; i++) {
        //考虑到 promises[i] 可能是 thenable 对象也可能是普通值
        Promise.resolve(promises[i])
          .then((data) => {
            res[i] = data;
            if (++count === len) {
              resolve(res);
            }
          })
          .catch((err) => {
            reject(err);
          });
      }
    }
  });
};

11.  async/await 和 Promise 的关系

  • async/await 是消灭异步回调的终极武器。
  • 但和 Promise 并不互斥,反而,两者相辅相成。
  • 执行 async 函数,返回的一定是 Promise 对象。
  • await 相当于 Promise 的 then。
  • tru...catch 可捕获异常,代替了 Promise 的 catch。

12. 写一个函数,第一秒打印 1,第二秒打印 2

  • let 块级作用域
for(let i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},1000*i)
}

13. JS 中继承实现的几种方式

原型链继承

  • 核心: 将父类的实例作为子类的原型
  • 特点: 非常纯粹的继承关系,实例是子类的实例,也是父类的实例 父类新增原型方法/原型属性,子类都能访问到 简单,易于实现
  • 缺点: 要想为子类新增属性和方法,不能放到构造器中 无法实现多继承 来自原型对象的所有属性被所有实例共享 创建子类实例时,无法向父类构造函数传参

构造继承

  • 核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用 到原型)
  • 特点: 解决了子类实例共享父类引用属性的问题 创建子类实例时,可以向父类传递参数 可以实现多继承(call 多个父类对象)
  • 缺点: 实例并不是父类的实例,只是子类的实例 只能继承父类的实例属性和方法,不能继承原型属性/方法 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

 实例继承

  • 核心:为父类实例添加新特性,作为子类实例返回
  • 特点: 不限制调用方式,不管是 new 子类()还是子类(),返回的对象具有相同的效果 缺点: 实例是父类的实例,不是子类的实例 不支持多继承

拷贝继承

  • 特点: 支持多继承
  • 缺点: 效率较低,内存占用高(因为要拷贝父类的属性)

组合继承

  • 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作 为子类原型,实现函数复用
  • 特点: 可以继承实例属性/方法,也可以继承原型属性/方法 既是子类的实例,也是父类的实例 不存在引用属性共享问题 可传参 函数可复用

寄生组合继承

  • 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作 为子类原型,实现函数复用

总结

原型链继承 将父类的实例作为子类的原型,他的特点是实例是子类的实例也是父 类的实例,父类新增的原型方法/属性,子类都能够访问,并且原型链继承简单易于实 现,缺点是来自原型对象的所有属性被所有实例共享,无法实现多继承,无法向父类构 造函数传参。
构造继承 使用父类的构造函数来增强子类实例,即复制父类的实例属性给子类, 构造继承可以向父类传递参数,可以实现多继承,通过 call 多个父类对象。但是构造继 承只能继承父类的实例属性和方法,不能继承原型属性和方法,无法实现函数服用,每 个子类都有父类实例函数的副本,影响性能
实例继承 为父类实例添加新特性,作为子类实例返回,实例继承的特点是不限制 调用方法,不管是 new 子类()还是子类()返回的对象具有相同的效果,缺点是实 例是父类的实例,不是子类的实例,不支持多继承
拷贝继承 特点:支持多继承,缺点:效率较低,内存占用高(因为要拷贝父类的 属性)无法获取父类不可枚举的方法(不可枚举方法,不能使用 for in 访问到) 5、组合继承:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父 类实例作为子类原型,实现函数复用
寄生组合继承 通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构 造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

14.事件委托以及冒泡原理

  •   事件委托是利用冒泡阶段的运行机制来实现的,就是把一个元素响应事件的函数委托到 另一个元素,一般是把一组元素的事件委托到他的父元素上,委托的优点是 减少内存消耗,节约效率 动态绑定事件
  • 事件冒泡,就是元素自身的事件被触发后,如果父元素有相同的事件,如 onclick 事件, 那么元素本身的触发状态就会传递,也就是冒到父元素,父元素的相同事件也会一级一 级根据嵌套关系向外触发,直到 document/window,冒泡过程结束。

15.事件模型

JSDOM 事件流存在如下三个阶段:

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段

JSDOM 标准事件流的触发的先后顺序为:先捕获再冒泡,点击 DOM 节点时,事件传播 顺序:事件捕获阶段,从上往下传播,然后到达事件目标节点,最后是冒泡阶段,从下 往上传播

DOM 节点添加事件监听方法 addEventListener,中参数 capture 可以指定该监听是添加在 事件捕获阶段还是事件冒泡阶段,为 false 是事件冒泡,为 true 是事件捕获,并非所有 的事件都支持冒泡,比如 focus,blur 等等,我们可以通过 event.bubbles 来判断。

事件模型有三个常用方法:

event.stopPropagation:阻止捕获和冒泡阶段中,当前事件的进一步传播, event.stopImmediatePropagetion,阻止调用相同事件的其他侦听器, event.preventDefault,取消该事件(假如事件是可取消的)而不停止事件的进一步传播, event.target:指向触发事件的元素,在事件冒泡过程中这个值不变 event.currentTarget = this,时间帮顶的当前元素,只有被点击时目标元素的 target 才会等 于 currentTarget,

最后,对于执行顺序的问题,如果 DOM 节点同时绑定了两个事件监听函数,一个用于 捕获,一个用于冒泡,那么两个事件的执行顺序真的是先捕获在冒泡吗,答案是否定的, 绑定在被点击元素的事件是按照代码添加顺序执行的,其他函数是先捕获再冒泡

16. 事件代理

事件代理是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开 始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给 最里面的 a 加一个 click 点击事件,那么这个事件就会一层一层的往外执行,执行顺序 a>li>ul>div,有这样一个机制,那么我们给最外面的 div 加点击事件,那么里面的 ul,li, a 做点击事件的时候,都会冒泡到最外层的 div 上,所以都会触发,这就是事件代理, 代理它们父级代为执行事件。

17. setTimeout和Promise的执行顺序

setTimeout(function() {
console.log(1)
}, 0);
new Promise(function(resolve, reject) {
console.log(2)
for (var i = 0; i < 10000; i++) {
if(i === 10) {console.log(10)}
i == 9999 && resolve();
}
console.log(3)
}).then(function() {
console.log(4)
})
console.log(5);
//2 10 3 5 4 1 

要先弄清楚 settimeout(fun,0)何时执行,promise 何时执行,then 何时执行 102 settimeout 这种异步操作的回调,只有主线程中没有执行任何同步代码的前提下,才会 执行异步回调,而 settimeout(fun,0)表示立刻执行,也就是用来改变任务的执行顺序, 要求浏览器尽可能快的进行回调

promise 何时执行,由上图可知 promise 新建后立即执行,所以 promise 构造函数里代码 同步执行的, then 方法指向的回调将在当前脚本所有同步任务执行完成后执行,

那么 then 为什么比 settimeout 执行的早呢,因为 settimeout(fun,0)不是真的立即执行, 经过测试得出结论:执行顺序为:同步执行的代码-》promise.then->settimeout

18. Eventloop

任务队列中,在每一次事件循环中,macrotask 只会提取一个执行,而 microtask 会一直 提取,直到 microsoft 队列为空为止。 也就是说如果某个 microtask 任务被推入到执行中,那么当主线程任务执行完成后,会 循环调用该队列任务中的下一个任务来执行,直到该任务队列到最后一个任务为止。而 事件循环每次只会入栈一个 macrotask,主线程执行完成该任务后又会检查 microtasks 队 列并完成里面的所有任务后再执行 macrotask 的任务。macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering microtasks: process.nextTick, Promise, MutationObserver

三、ES6

ES6必会十大新特性

四、VUE

vue面试知识点总结icon-default.png?t=M4ADhttps://blog.csdn.net/qq_53061847/article/details/125015424?spm=1001.2014.3001.5502

五、AJAX

原生AJAX的请求

Ajax 能够在不重新加载整个页面的情况下与服务器交换数据并更新部分网页内容,实现 局部刷新,大大降低了资源的浪费,是一门用于快速创建动态网页的技术,ajax 的使用 分为四部分

1、创建 XMLHttpRequest 对象 var xhr = new XMLHttpRequest();

2、向服务器发送请求,使用 xmlHttpRequest 对象的 open 和 send 方法,

3、监听状态变化,执行相应回调函数

var xhr = new XMLHttpRequest();
xhr.open('get', 'aabb.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
if(xhr.readyState==4) {
if(xhr.status==200) {
console.log(xhr.responseText);
}
}
}

通过实例化一个 XMLHttpRequest 对象得到一个实例,调用实例的 open 方法为这次 ajax 请求设定相应的 http 方法,相应的地址和是否异步,以异步为例,调用 send 方法,这 个方法可以设定需要发送的报文主体,然后通过监听 readystatechange 事件,通过这个实 例 的 readyState 属性来判断这个 ajax 请求状态

其中分为 0,1,2,3,4 这四种状态:

  • 0 未初始化
  • 1 载入/正在发送请求
  • 2 载入完成/数据接收
  • 3 交互/解析数据
  • 4 接收数据完成

当状态为 4 的时候也就是接受数据完成的时候,这时候可以通过实例的 status 属 性判断这个请求是否成功

需求:如何实现 ajax 请求,假如我有多个请求,我需要让这些 ajax 请求按照某 种顺序一次执行,有什么办法呢?如何处理 ajax 跨域

使 ajax 请求按照队列顺序执行,通过调用递归函数: //按顺序执行多个 ajax 命令,因为数量不定,所以采用递归

function send(action, arg2) {
//将多个命令按顺序封装成数组对象,递归执行
//利用了 deferred 对象控制回调函数的特点
$.when(send_action(action[0], arg2))
.done(function () {
//前一个 ajax 回调函数完毕之后判断队列长度
if (action.length > 1) {
//队列长度大于 1,则弹出第一个,继续递归执行该队列
action.shift();
send(action, arg2);
}
}).fail(function (){
//队列中元素请求失败后的逻辑
//
//重试发送
//send(action, arg2);
//
//忽略错误进行下个
//if (action.length > 1) {
//队列长度大于 1,则弹出第一个,继续递归执行该队列
// action.shift();
// send(action, arg2);
//}
});
}
//处理每个命令的 ajax 请求以及回调函数
function send_action(command, arg2) {
var dtd = $.Deferred();//定义 deferred 对象
$.post( "url", {
command: command

arg2: arg2
}
).done(function (json) {
json = $.parseJSON(json);
//每次请求回调函数的处理逻辑
//
//
//
//逻辑结束
dtd.resolve();
}).fail(function (){
//ajax 请求失败的逻辑
dtd.reject();
});
return dtd.promise();//返回 Deferred 对象的 promise,防止在外部

六、移动web开发

1. 移动布局方案icon-default.png?t=M4ADhttps://links.jianshu.com/go?to=https%3A%2F%2Fjuejin.im%2Fpost%2F599970f4518825243a78b9d5%23heading-22

2. Rem, Em

 https://blog.csdn.net/romantic_love/article/details/80875462

一、rem 单位如何转换为像素值 1.当使用 rem 单位的时候,页面转换为像素大小取决于叶根元素的字体大小,即 HTML 元素的字体大小。根元素字体大小乘 rem 的值。例如,根元素的字体大小为 16px,那么 10rem 就等同于 10*16=160px。

二、em 是如何转换成 px 的 当使用 em 单位的时候,像素值是将 em 值乘以使用 em 单位的元素的字体大小。例如一 个 div 的字体为 18px,设置它的宽高为 10em,那么此时宽高就是 18px*10em=180px。

七、HTTP

http原理详解icon-default.png?t=M4ADhttps://blog.csdn.net/hguisu/article/details/8680808

前端工程师做出网页,需要通过网络请求向后端获取数据,因此 http 协议是前端面试的必考内容。

1、http 状态码

1.1 状态码分类

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

1.2 常见状态码

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

1.3 关于协议和规范

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

2、http 缓存

  • 关于缓存的介绍。
  • http 缓存策略(强制缓存 + 协商缓存)。
  • 刷新操作方式,对缓存的影响。

4.1 关于缓存

什么是缓存? 把一些不需要重新获取的内容再重新获取一次

为什么需要缓存? 网络请求相比于 CPU 的计算和页面渲染是非常非常慢的。

哪些资源可以被缓存? 静态资源,比如 js css img。

4.2 强制缓存

【八股文】---前端面试经验总结_第1张图片

Cache-Control:

  • 在 Response Headers 中。
  • 控制强制缓存的逻辑。
  • 例如 Cache-Control: max-age=3153600(单位是秒)

Cache-Control 有哪些值:

  • max-age:缓存最大过期时间。
  • no-cache:可以在客户端存储资源,每次都必须去服务端做新鲜度校验,来决定从服务端获取新的资源(200)还是使用客户端缓存(304)。
  • no-store:永远都不要在客户端存储资源,永远都去原始服务器去获取资源。

4.3 协商缓存(对比缓存)

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

【八股文】---前端面试经验总结_第2张图片

资源标识:

  • 在 Response Headers 中,有两种。
  • Last-Modified:资源的最后修改时间。
  • Etag:资源的唯一标识(一个字符串,类似于人类的指纹)。

Last-Modified:

【八股文】---前端面试经验总结_第3张图片

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

Etag:

【八股文】---前端面试经验总结_第4张图片

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

两者比较:

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

4.4 综述

【八股文】---前端面试经验总结_第5张图片

4.4 三种刷新操作对 http 缓存的影响

  • 正常操作:地址栏输入 url,跳转链接,前进后退等。
  • 手动刷新:f5,点击刷新按钮,右键菜单刷新。
  • 强制刷新:ctrl + f5,shift+command+r。

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

3. 面试

对于更多面试中可能出现的问题,我还是建议精读这篇三元的文章:HTTP 灵魂之问,巩固你的 HTTP 知识体系。

比如会被经常问到的: GET 和 POST 的区别。

  • 缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
  • 编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
  • 参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
  • 幂等性的角度,GET 是幂等的,而 POST 不是。(幂等表示执行相同的操作,结果也是相同的)
  • TCP 的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

HTTP/2 有哪些改进?(很大可能问原理)

  • 头部压缩。
  • 多路复用。
  • 服务器推送。

关于 HTTPS 的一些原理,可以阅读这篇文章:这一次,彻底理解 https 原理。接着你可以观看这个视频进行更进一步的学习:HTTPS 底层原理,面试官直接下跪,唱征服!

关于跨域问题,大部分文章都是理论性比较强,还不如读这篇文章,聊聊跨域的原理与解决方法,讲的非常清晰,我个人觉得对付面试就是先知道使用流程,把这个流程能自己说出来,然后再讲下原理即可。
参考链接:https://juejin.cn/post/7061588533214969892
 

你可能感兴趣的:(八股文,vue,前端,javascript,vue.js)