前端面试题集合

前端面试题集合

CSS

CSS选择器优先级

/* 内联 > ID选择器 > 类选择器 > 标签选择器 */

样式兼容

  • -moz代表firefox浏览器私有属性
  • -ms代表IE浏览器私有属性
  • -webkit代表chrome、safari私有属性
  • -o代表opera私有属性

伪类伪元素

伪类
  • 选择器的一种,用于选择处于特定状态的元素,例如:hover鼠标悬浮元素上面的时候
伪元素
  • 表现为往标记文本中添加全新的HTML元素一样,开头为双冒号***:😗**

BFC

定义
  • 全称Block Formatting Context块级格式化上下文,BFC是一个完全独立的空间,空间内子元素不会影响外面布局。
触发BFC
overflow: hidden;
display: inline-block;
position: absolute;
display: table-cell;
display: flex;
BFC规则
  • BFC是块级元素,垂直方向排列
  • 内部标签不会影响外部标签
  • 垂直方向距离由margin决定,同一个BFC两个相邻标签外边距会重叠
  • 计算BFC高度,浮动元素参与计算
BFC解决的问题
  • 使用float脱离文档流,高度塌陷
  • margin边距重叠
  • 两栏布局

javascript

继承

// 父类
function Parent(name) {
    // 属性
    this.name = name || 'Annie';
    // 实例
    this.sleep = function() {
        console.log(this.name + '正在睡觉');
    }
}

// 原型方法
Parent.prototype.eat = function(food) {
    console.log(this.name + '正在吃:' + food);
}
  • 原型链继承

    // 父类实例作为子类原型
    function Child() {}
    Child.prototype = new Parent();
    Child.prototype.name = 'haixia';
    
    const childIns = new Child();
    
    • 优点:
      • 简单易于实现,父类的新增的实例与属性子类都能访问
    • 缺点:
      • 可以在子类增加实例属性,如果要新增原型属性和方法需要在new父类构造函数后面
      • 无法实现多继承
      • 创建子类实例时,不能向父类构造函数中传参
  • 构造函数继承

    // 复制父类实例属性给子类
    function Child(name) {
        // 继承People
        Parent.call(this); // Parent.call(this, 'haixia');
        this.name = name || 'renbo';
    }
    const childIns = new Child();
    
    • 优点:
      • 解决子类构造函数向父类构造函数中传递参数
      • 可以实现多继承(call / apply 多个父类)
    • 缺点:
      • 方法都在构造函数中定义,无法复用
      • 不能继承原型属性和方法,只能继承父类实例属性和方法
  • 实例继承(原型式继承)

    function Child(name) {
        const instance = new Parent();
        instance.name = name || 'haixia';
        return instance;
    }
    const childIns = new Child();
    
    • 优点:
      • 不限制调用方式
      • 简单、易实现
    • 缺点:
      • 不能多次继承
  • 组合式继承

    // 调用父类构造函数、继承父类的属性,通过将父类实例作为子类原型,实现函数复用
    function Child(name) {
        Parent.call(this, name);
    }
    
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;
    
    const childIns = new Child('ren');
    childIns.eat();
    
    • 缺点:
      • 由于调用两次父类,所以产生两份实例
    • 优点:
      • 函数可以复用
      • 不存在引用属性问题
      • 可以继承属性和方法,且可继承原型的属性和方法
  • 寄生组合继承

    // 通过寄生的方式来修复组合式继承的不足,完美实现继承
    function Child(name) {
        // 继承父类属性
        Parent.call(this, name);
    }
    
    // 继承父类方法
    (function() {
        // 创建空类
    	let Super = function () {};
        Super.prototype = Parent.prototype;
        // 父类实例作为子类原型
        Child.prototype = new Super();
    })();
    
    // 修复构造函数指向问题
    Child.prototype.constructor = Child;
    const childIns = new Child();
    
  • es6 继承

    // 代码量少、易懂
    // class 相当于es5的构造函数
    // class 定义方法时,前后不能加function,全部定义在class的prototype属性中
    // class 定义所有方法不可枚举
    // class 只能定义方法,不能定义对象,变量等
    // class 和 方法内默认都是严格模式
    // es5 中 constructor 为隐式属性
    class Parent {
        constructor(name = 'wang') {
            this.name = name;
        }
        
        eat(food) {
            console.log(`${this.name}正在吃:${food}`);
        }
    }
    
    // 继承父类
    class Child extends Parent {
        constructor(name = 'ren', age = '27') {
            super(name);
            this.age = age;
        }
        
        eat(food) {
            // 继承父类方法
            super.eat(food);
        }
    }
    
  • es5继承和es6继承区别

    • es5 继承首先在子类中创建自己的this指向,最后将方法添加到this中

      Child.prototype = new Parent() || Parent.apply(this) || Parent.call(this)
      
    • es6 继承使用关键字class先创建父类实例对象this,最后在子类class中修改this

三次握手和四次挥手

TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道。

三次握手(过程中不包含数据)
  1. 客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等到服务器确认;
  2. 服务器收到syn包,必须确认客户的syn(ack=x+1),同事自己也发送一个syn包(seq=y),即syn+ack包,此时服务器进入syn_recv状态;
  3. 客户端收到服务器的syn+ack包,向服务器发送确认包ack(ack=y+1),此包发送完毕,客户端和服务器进入established状态。
四次挥手
  1. 主动关闭方发送FIN用来关闭主动方到被动方的数据传送;
  2. 被动关闭方收到FIN包,发送ACK包给对方;
  3. 被动关闭方发送一个FIN,用来关闭被动关闭方道主动关闭方的数据传送;
  4. 主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号3为收到序号+1.

js类型

  • 基本类型String、Number、boolean、null、undefined
  • 引用类型Object(function, Array, Date)

自定义instanceof

function myInstanceof(Fn, obj) {
  // 获取该函数显示原型
  const prototype = Fn.prototype;
  // 获取obj的隐式原型
  let proto = obj.__proto__;
  // 遍历原型链
  while (proto) {
    // 检测原型是否相等
    if (proto === prototype) {
      return true;
    }
    // 如果不等于则继续往深处查找
    proto = proto.__proto__;
  }
  return false;
}

nullundefined区别

  • nullundefined都表示空
  • null表示空对象,一般用于赋值给可能会返回对象的变量作为初始化,typeof null == 'object'
  • undefined表示未定义,一般变量声明了,但是没有赋值的时候会返回undefined, typeof undefined == 'undefined'

js作用域1

1. 除了函数外,js没有块级作用域
2. 作用域链:内部可以访问外部变量,但是外部不能访问内部变量。(注意:优先查找内部变量)
3. 注意声明变量使用var还是没有写`window`
4. 注意:js有变量提升机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
	- 普通声明函数不看顺序
  • 例子

    // 1.
    function fun() {
        for(var i = 0; i < 10; i ++) {}
        console.log(i); // 10
    }
    fun();
    
    // 2.
    var b = 20;
    function fun() {
        var a = 10;
        function foo() {
            console.log(a); // 10
            console.log(b); // 20
        }
        foo();
    }
    // console.log(a); // error
    fun();
    
    (function() {
        var a = b = 10;
    })()
    // console.log(a); // error
    
    // 考题一:
    function c() {
        var b = 1;
        function a() {
            console.log(b); // undefined
            var b = 2;
            console.log(b); // 2
        }
        a();
        console.log(b); // 1
    }
    c();
    
    // 考题二:
    var name = 'a';
    (function () {
        if (typeof name == 'undefined') {
            var name = 'b';
            console.log(111 + name); // 111b
        } else {
            console.log(222 + name);
        }
    })()
    
    // 考题三:
    var bar = 1;
    function test() {
        console.log(bar); // undefined
        var bar = 2;
        console.log(bar); // 2
    }
    test();
    
    function fun() {
        var a = 10;
        function a() {}
        console.log(a); // 10
        
        var a = 10;
        var a = () => {}
        console.log(a); // f a() {}
        
        console.log(a); // f a() {}
        function a() {}
        
        console.log(a); // f a() {}
        var a = 10;
        function a() {}
    }
    fun();
    
    function fun(a) {
        console.log(a); // 100
        var a = 10;
        
        console.log(a); // f a() {}
        function a() {}
        
        var a = 10;
        function a() {}
        console.log(a); // 10
    }
    fun(100);
    

js类型判断2

typeof
  • 只能识别基础类型和引用类型

    • console.log(typeof null); // object
      console.log(typeof NaN); // number
      console.log(typeof document.all); // undefined
      
constructor3
String.prototype.constructor = function fn() {
  return {};
};

console.log("云牧".constructor); // [Function: fn]
instanceof
  • 语法 obj instanceof Type
  • 功能:判断obj 是否是 Type类的实例,只可用于判断引用数据
  • 实现思路:Type 的原型对象是否是obj原型链上的某个对象
  • 注意:右操作数必须是函数或者class
  • 手写instanceof
isPrototypeof
  • 是否在实例对象的原型链上
  • 功能基本等于instanceof
  • 左操作数是一个原型
Object.prototype.toString
  • 利用函数动态this的特性

    function typeOf(data) {
      return Object.prototype.toString.call(data).slice(8, -1);
    }
    
    // 测试
    console.log(typeOf(1)); // Number
    console.log(typeOf("1")); // String
    console.log(typeOf(true)); // Boolean
    console.log(typeOf(null)); // Null
    console.log(typeOf(undefined)); // Undefined
    console.log(typeOf(Symbol(1))); // Symbol
    console.log(typeOf({})); // Object
    console.log(typeOf([])); // Array
    console.log(typeOf(function () {})); // Function
    console.log(typeOf(new Date())); // Date
    console.log(typeOf(new RegExp())); // RegExp
    

== 与 === 区别

  • 检测两个操作数是否相等,允许类型转换
  • 严格比较,类型不匹配就返回false
  • string,number基本类型区别
    • ‘1’ == true => true -> 1 => ‘1’ == 1 => ‘1’ -> 1 => 1 == 1
  • 隐式类型转换

原型链实现机制

  • 所有函数数据类型有prototype原型属性,属性值为一个对象,浏览器默认给其分配一个堆内存
  • prototype.constructor指向当前函数本身
  • 每个对象都有__proto__指向当前实例所属类的prototype(顶层为Object)
  • 原型存储公共属性方法供实例调用
  • 原型链基于__proto__向上查找机制,操作实例属性或者方法时,首先查找自己空间私有属性或方法,找到则结束,没有找到则基于__proto__所属类的prototype,一直找到Object.prototype原型为止,如果再没有,则操作的属性或方法不存在
  • instanceof方法原理基于此

内存泄露

  • 意外的全局变量 -> 采用use strict严格模式解析javascript解决意外的全局变量
  • 遗忘的计时器或回调函数,例如setIntervaladdEventListener
  • 脱离DOM引用,保存DOM节点星系,节点被删除是,引用没有被清除
  • 闭包
Chrome内存工具检测
  • timelineprofiles heap allocations

promise用于解决什么问题

解决回调地狱问题

option预检请求

  • 跨域情况下
  • 请求头Content-Type: application/json
  • 设置用户自定义请求头
  • delete方法

http状态码(常用)

  • 200 请求成功
  • 304 用于缓存目的,命中协商缓存
  • 400 客户端错误,bad request
  • 401 未授权
  • 403 无权限访问
  • 404 not found
  • 405 请求方法不支持
  • 500 服务器遇到不知道如何处理的情况
  • 502 错误响应
  • 504 未得到及时响应

commonjs esmodule 区别

解决问题
  • 解决变量污染问题,每个文件都是独立的作用域,不存在变量污染
  • 解决代码维护问题,一个文件代码清晰
  • 解决文件依赖问题,一个文件里可以清楚看到依赖了那些文件
commonjs
  • 导出

    // 导出对象
    module.exports = {}
    
    // 导出任意值
    module.exports.name = '';
    module.exports.sex = '';
    
    // 直接导出
    exports.name = '';
    exports.sex = '';
    // 注意,如果exports单个值后,不能导出一个对象值,否者会覆盖之前的值
    
    // 混合导出
    exports.name = '';
    module.exports.age = 24
    
  • 导入

    const data = require('.');
    
    // 重复导入
    const data1= require('.'); // 不会在执行了
    
    // 动态导入
    const lists = ['./index.js', './config.js'];
    lists.map((l) => require(l));
    
  • 导入值变化

    • 导入值是拷贝的,可以对导入的值进行修改
  • 代码发生在运行时

esmodule
  • 导出

    // 导出变量
    export const name = '';
    export const age = 24;
    
    // 导入函数
    export const test = () => {}
    
    // 另一种导出
    const sex = '';
    export sex;
    
    // 多个导出
    const name = '';
    const sex = '';
    export {name, sex}
    
    // 混合导出
    export const name = '';
    export const age = 24;
    export default = {
        fn() {},
        msg: ''
    }
    
  • 导入

    import { name, age } from '.';
    // 全部导出单个
    import * as all from '.';
    
    // 混合导入
    import msg, {name, age} from '.';
    
    // 别名导入
    import { default as all, name, age } from '.';
    
  • 导入值变化

    • export出的值是引用,和内部由映射关系,导入的值不可修改,只读
  • es module是静态

    • import语句只能在文件最顶部,不能动态加载语句
  • 代码发生在编译时

typescript

interface type区别

interface 接口
  • 定义对象类型,描述对象形状
  • 合并重复声明
type 类型别名
  • 类型名字
  • 可声明基本类型、联合类型、交叉类型、元组

vue

vue2 和vue3 区别

vue3 优点
  • 速度更快
    • vue3 重写虚拟DOM实现,编译模板优化,更高组件初始化,update性能提高,ssr速度提高。
  • 体积更小
    • 通过tree-shaking,仅打包需要的模块。
  • 更易维护
    • composition Api 和 现有的option Api 一起使用
    • 灵活的逻辑组合和复用
    • vue3 模块可与其他框架搭配使用
  • 更接近原生
    • 自定义渲染API
  • 更易使用
    • 响应式Api暴露出来
    • 轻松识别组件重新渲染原因
  • diff算法即差异查找算法
    • 新旧虚拟DOM比对
    • vue3 只对比变动的值,性能更好

vue3新增特性

framents
  • 模板支持多节点
Telport
  • 模板移动至vue app之外的技术
composition Api
  • 组合式API,相同功能变量集中式管理

vue3 非兼容性变更

Global Api
  • 全局vue api 更改为使用应用程序实例
  • 全局和内部 api 重构为可tree-shakable
模板指令
  • v-model 用法更改
  • template v-for 和 非 v-for 节点上key用法更改,会自动生成key
  • v-for中ref不在注册ref数组
组件
  • 只能使用普通函数创建功能组件,setup(props, { slot, expose });
  • s c o p e d S l o t s p r o p e r t y 已删除,所有插槽通过 scopedSlots property已删除,所有插槽通过 scopedSlotsproperty已删除,所有插槽通过slots作为函数暴露
  • 自定义指令api已经更改为与组件生命周期一致
  • 转换class重命名
    • v-enter -> v-enter-from
    • v-leave -> v-leave-from
  • 组件watch选项和实例方法$watch不在支持点分割字符串路径,只用计算函数作为参数
  • vue2.x使用根容器outerHTML,vue3.x使用应用程序容器的innerHTML
其他
  • destroyed 生命周期选项重命名为onUnmounted
  • beforeDestroyed -> onBeforeUnmount
  • 工厂函数不在有权访问this上下文
  • 自定义指令API已经更改为和组件生命周期一致
  • data应始终声明为为函数
  • 来自mixin的data选项现在可以简单合并
  • attribute强制策略已经更改
  • 过渡class重命名
移除API
  • keyCode作为v-on修饰符
  • o n 、 on、 onoff 和 $once
  • 过滤filter
  • $destroy实例方法
  • 内联模板attribute

算法

排序算法

冒泡排序
function bubbleSort(arr: number[]) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        ;[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
      }
    }
  }
  return arr
}
选择排序
function selectionSort(arr: number[]) {
  let min = 0
  for (let i = 0; i < arr.length - 1; i++) {
    min = i
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[min]) {
        min = j
      }
    }
    if (i !== min) {
      ;[arr[i], arr[min]] = [arr[min], arr[i]]
    }
  }
  return arr
}
插入排序
function insertionSort(arr: number[]) {
  for (let i = 1; i < arr.length; i++) {
    let j = i
    let temp = arr[i]
    // 插入操作
    while (j > 0 && arr[j - 1] > temp) {
      // 数组原位置向后移动
      arr[j] = arr[j - 1]
      j--
    }
    arr[j] = temp
  }
  return arr
}
希尔排序
function shellSort(arr: number[]) {
  // 获取增量
  let gap = Math.floor(arr.length / 2)
  // 增量等于1 即为 插入排序 原始。 一定会将数组排好 这个时候结束循环
  while (gap >= 1) {
    // 进行插入排序
    for (let i = gap; i < arr.length; i++) {
      let j = i
      let temp = arr[i]
      while (j > gap - 1 && arr[j - gap] > temp) {
        arr[j] = arr[j - gap]
        j -= gap
      }
      arr[j] = temp
    }
    // 缩小增量
    gap = Math.floor(gap / 2)
  }
  return arr
}
快速排序
function quickSort(arr) {
    if (arr.length <= 1) return arr
    let left = [],
        right = []
    // 将中间值 取除 并在arr中移除
    let middle = arr.splice(Math.floor(arr.length / 2), 1)[0]
    arr.forEach((el) => (el >= middle ? right.push(el) : left.push(el)))
    return quickSort(left).concat(middle, quickSort(right))
}
归并排序
function mergeSort(arr: number[]) {
  // 分而治之
  // 先分
  if (arr.length > 1) {
    // 将数组 分成两半 递归进行 直到 数组长度 小于等于 1
    let middle = Math.floor(arr.length / 2)
    let left = mergeSort(arr.slice(0, middle))
    let right = mergeSort(arr.slice(middle, arr.length))
    // 然后 合并排序
    arr = merge(left, right)
  }
  // 将结果返回
  return arr
}

function merge(left: number[], right: number[]) {
  // 将左右两个数组 合并 排序
  // i 指向 左数组   j 指向右数组
  let i = 0
  let j = 0
  // 将结果有序的push 进 result 中
  let result = []

  while (i < left.length && j < right.length) {
    // 排序
    result.push(left[i] < right[j] ? left[i++] : right[j++])
  }

  // 合并 =>将 左右 数组 剩余的部分 concat
  return result.concat(i < left.length ? left.slice(i) : right.slice(j))
}
计数排序
function countingSort(arr: number[]) {
  let count = [] //计数
  let result = [] //结果
  arr.forEach((el) => {
    //将每个数字 以索引 存入 count中
    if (!count[el]) {
      count[el] = 0
    }
    count[el]++
  })

  count.forEach((el, i) => {
    //将count 取出
    if (el && el > 0) {
      for (let j = 0; j < el; j++) {
        result.push(i)
      }
    }
  })
  return result
}
基数排序
// 数组每一项个位、十位···`计数排序处理`
function radixSort(arr: number[]) {
  if (arr.length < 2) {
    return arr
  }
  //找到最大值
  let max = -Infinity
  arr.forEach((el) => (el > max ? (max = el) : null))
  //求他的位数
  let digit = (max + '').length
  //循环计数排序
  let count = []
  for (let i = 0; i < digit; i++) {
    //按 个位排序, 十位排序 ,百位排序 ....
    arr.forEach((el) => {
      let str = el + ''
      let temp = +str[str.length - 1 - i]
      if (isNaN(temp)) {
        temp = 0
      }
      if (Array.isArray(count[temp])) {
        count[temp].push(el)
      } else {
        count[temp] = [el]
      }
    })
    arr = []
    count.forEach((el) => {
      if (Array.isArray(el)) {
        el.forEach((e) => {
          arr.push(e)
        })
      }
    })
    count = []
  }
  return arr
}

webpack

sourcemap

存储代码位置信息。

前端优化

减少HTTP请求
  • 合并文件,减少请求
使用HTTP2
  • 解析速度快
    • HTTP2基于帧协议,每个帧都有表示长度的字段
  • 多路复用
    • 多个请求公用一个TCP连接,通过唯一流ID重新组建
  • 首部压缩
服务端渲染SSR
  • 客户端渲染
    • 获取HTML文件,根据需要下载javascript 文件,运行文件,生成DOM,再渲染
  • 服务端渲染
    • 服务端返回HTML文件,客户端只需要解析HTML
    • 优点:首屏渲染快,SEO好
    • 缺点:配置麻烦,增加服务器计算压力
静态资源代理CDN
  • 最近服务器下载资源
代码结构调整
  • CSS放在文件头部,CSS执行会阻塞渲染,阻止js执行
  • javascript脚本文件放在body标签底部,js加载和执行会阻塞HTML解析,阻止CSS,DOM构建
使用字体图标iconfont代替图片图标
  • 矢量图不会失真,生成文件特别小
缓存机制
  • 添加Expires | max-age控制缓存行为
  • 强缓存与协商缓存
压缩文件
  • 开启代码压缩
  • http请求头重Accept-Encoding添加gzip
图片懒加载
  • 图片进入可见区域再加载
响应式图片
  • 根据屏幕大小自动加载合适的图片
    • picture标签实现
    • @media媒体查询实现
调整图片大小
  • 缩略图展示,鼠标悬浮加载全图
使用css3效果代替图片
使用webp格式图片
  • 更优图片数据压缩算法,体积小
减少重绘重排
  • javascript 修改样式时,不要直接写样式,替换class改变样式
  • 修改DOM元素时,将DOM元素脱离文档流,修改完成之后再带回文档,推荐使用隐藏元素display: none
使用事件委托
  • 事件委托利用事件冒泡,只指定一个事件处理程序,管理某一类型的所有事件,节省内存
if-elseswitch
  • js中的switch不是基于哈希实现,而是循环判断,if-else和switch性能上是一样的
requestAnimationFrame实现视觉变化
  • js代码在帧开头执行,解决动画卡顿问题
使用Web Workers
  • 独立主线程之外,不干扰用户界面
降低CSS选择器复杂性
  • 选择器越短越好
  • 尽量使用高优先级选择器
  • 避免使用通配符 *

浏览器

事件循环Event Loop

事件循环是什么
  • JS的运行机制就是事件循环
JS执行顺序
  • 从上至下执行
  • 某一行执行报错,则停止执行下面的代码
  • 先执行同步代码,再执行异步代码
事件循环执行过程
  • 同步代码,调用栈执行后直接出栈
  • 异步代码,放到Web Api中,合适的时候放入回调队列(callbackQueue),调用栈空的时候eventLoop开始工作
  • 微任务执行时机比宏任务早
  • 微任务在DOM渲染前触发,宏任务在DOM渲染后触发
微任务宏任务根本区别
  • 宏任务:setTimeout、setInterval、Ajax、DOM事件
  • 微任务:Promise async/await
  • 微任务执行时机比宏任务早
  • 微任务由ES6语法规定的
  • 宏任务由浏览器规定的
事件循环整体流程
  • 清空call stack同步代码
  • 执行微任务队列中微任务
  • 尝试DOM渲染
  • 触发Event Loop反复询问callbackQueue中是否有要执行的语句,有则放入call stack继续执行

url输入之后有什么操作

域名解析
  • 查找浏览器缓存
  • 查找系统缓存
  • 查找路由器缓存
  • ISP DNS缓存查找
  • 浏览器域名服务器向根域 > com 顶级域 > 二级域
建立TCP连接
HTTP请求
服务器处理请求,浏览器接受HTTP响应
服务器与客户端断开连接
页面渲染,构建DOM树

浏览器缓存

强制缓存
  • 直接从缓存中读取资源,状态码200
  • 响应头Expires过期时间,优先级低于Cache-Control
  • Cache-Control 值为max-age=300(单位s)
    • -no-cache不使用本地缓存,使用协商缓存
    • -no-store禁止浏览器缓存数据,每次请求都是最新完整资源
    • -public可以被所有用户缓存,包括终端用户以及CDN中间代理服务器
    • -privatte只能被终端用户缓存,不允许CDN等中继缓存服务器对其缓存
协商缓存
  • 使用本地缓存之前,需要向服务器发送请求,命中返回状态码304
  • 响应头Last-Modify/if-Modify-Since服务器根据Last-Modify与资源修改时间比对,相等则命中协商缓存,返回状态码304,若状态码为200表示重新获取数据,优先级低于Etag
  • Etag/if-None-Match:etag是一个hash值,服务器比对内部etag标识,相等则返回304,否则etag发生变化,返回状态码200Etag用于解决Last-Modify资源毫秒内修改读取不出来的问题,但生成hash值加大服务器开销

浏览器渲染过程

  • 解析HTML生成DOM树
  • 解析CSS生成CSSOM规则树
  • 将DOM树与CSSOM规则树合并生成渲染树
  • 遍历渲染树布局,计算每个节点位置大小信息
  • 将渲染树每个节点绘制到屏幕
重排
  • 当DOM元素位置或大小改变时,浏览器重新生成渲染树
重绘
  • 重新生成渲染树后,渲染树每个节点绘制到屏幕,这个过程叫做重绘。字体颜色改变只会导致重绘,重排会导致重绘,重绘不会导致重排

web屏幕适配解决方案

PC
  • 版心布局解决
    • 当屏幕大于版心宽度的时候,版心居中显示
    • 当屏幕小于版心宽度时,屏幕出现横向滚动条
移动端
  • 流式布局,容器和元素宽度不是定死的,使用百分比单位或者rem单位

网络攻击

XSS跨站脚本攻击
  • web页面按恶意插入HTML或Script标签,XSS利用用户对网站的信任盗取cookie
  • 盗用cookie无密码登录获取用户信息
  • 劫持访问,恶意跳转
  • 配合CSRF攻击完成恶意请求
  • 防御方法
    • 标签过滤
    • 编码,对字符<&等进行转义
    • cookie防盗,设置http-onlyjs脚本无法读取cookie信息
    • 纯前端渲染,明确innerText, setAttributestyle,将代码与数据分隔开
    • 避免不可信的数据拼接到字符串中传递给这些API
      • location, onclick,onload,a href 属性, js eval()
CSRF跨站点请求伪造
  • 未退出信任网站的情况访问危险网站,危险网站发出访问信任网站的请求,浏览器在用户不知情情况下携带cookie访问信任网站,达成攻击目的
  • 防御方法
    • 验证码,对敏感操作加入验证码,强制用户与网站交互
    • cookie设置SameSite属性,标识cookie不随跨域请求发送
    • 检查HTTPreferer字段,记录请求来源地址
    • 请求头中加入token验证字段,添加加密jwt身份认证
    • http中自定义属性并验证
点击劫持
  • clickjacking点击劫持,透明化隐藏页面,显示诱导信息点击,例如点击弹窗右上角关闭按钮,跳转到其他页面

  • iframe覆盖,iframe透明不可见,覆盖到其他经过伪装的DOM上,用户点击DOM的时候实际上是iframe内嵌网页的DOM而触发请求

  • 防御方法:

    • javascript禁止内嵌

      <script>
          if (top.location != window.location) {
              //如果不相等,说明使用了iframe,可进行相关的操作
          }
      </script>
      
    • 设置http响应头X-FRAME-OPTIONS

注释


  1. (https://space.bilibili.com/3493141948664095)[bilibili小张小张] ↩︎

  2. 点击这里查看详情 ↩︎

  3. 注意nullundefined 没有 constructor, 以及 constructor 可以被改写 ↩︎

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值