前端面试知识点汇总

204 阅读19分钟

HTML

HTML 语义化

  1. 没有 CSS 样式下,页面也能呈现出很好地内容结构、代码结构
  2. 有利于 SEO,让搜索引擎更容易读懂,有助于爬虫抓取更多的有效信息
  3. 具有更好的可读性,有利于团队开发和维护

HTML5 新特性

  1. 新增了语义化标签,如 headernavarticleasidefooter 等等
  2. 新增了增强型表单
  3. 新增了音频视频标签
  4. 新增了 canvassvg 绘图
  5. 新增了地理定位
  6. 新增了拖拽API
  7. 新增了本地缓存 Web Storage
  8. 新增了多线程技术 Web Worker
  9. 新增了即时通讯 WebSocket

CSS

CSS3 新特性

圆角阴影背景与渐变过渡变形动画媒体查询多栏布局选择器 等等。

CSS 选择器

CSS 选择器优先级顺序

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

CSS 选择器优先级计算规则

  1. 如果存在 内联 样式,那么 A = 1,否则 A = 0
  2. B 的值等于 ID选择器 出现的次数
  3. C 的值等于 类选择器 和 伪类选择器属性选择器 出现的总次数
  4. D 的值等于 标签选择器 和 伪元素选择器 出现的总次数

总结: CSS 选择器优先级是由 A、B、C、D 的值来决定的,从左往右依次进行比较,较大者胜出,如果相等,则继续往右移动一位进行比较,如果四位全相等,后面的覆盖前面的

CSS 盒模型

CSS 盒模型有哪些?

CSS 盒模型有 标准盒模型IE盒模型 两种盒模型。

两种盒模型的区别?

两种盒子模型都是由 content + padding + border + margin 构成,其大小都是由 content + padding + border 决定,盒子内容宽/高度(width/height)的计算范围根据盒模型的不同会有所不同,标准盒模型只包含 content,IE盒模型包含 content + padding + border

如何设置盒模型?

标准盒模型 box-sizing: content-box
IE盒模型 box-sizing: border-box

清除浮动的几种方案?

  1. 浮动元素后增加一个空元素且设置 clear: both 属性
  2. 父元素设置 owerflow:auto|hidden 属性,IE6中还需要为父元素设置宽高或 zoom: 1
  3. 伪元素设置 display: block; clear: both 属性

元素水平垂直居中的几种方案?

1. flex布局
    display: flex;
    align-items: center;
    justify-content: center;

2. 绝对定位和 `transform` 配合
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

3. 绝对定位和 `margin: auto` 配合
    // 父元素设置 position: relative
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;

4. 文字水平垂直居中
    line-height: 500px; // 父元素高度
    text-align: center;

Transition 与 Animation 的异同?

TransitionAnimation 的功能相同,都是通过改变元素属性的值来实现动画效果,二者区别在于 Transition 只能指定属性的开始值与结束值,然后在这个属性值之间使用平滑过渡的方式实现动画效果,不能实现比较复杂的动画效果,Animation 通过定义多个关键帧,以及定义每个关键帧中元素的属性值来实现更为复杂的动画效果。

line-height 如何继承?

  1. 父元素的 line-height 设置了 具体数值,如 30px,则子元素继承该值
  2. 父元素的 line-height 设置了 比例,如 1.52,则子元素继承该值
  3. 父元素的 line-height 设置了 百分比,如 200%,则子元素继承的是父元素 font-size * 200% 计算出来的值

CSS 隐藏元素的方案?

  1. display: none 被隐藏的元素不会在页面中占据位置,也不会响应绑定的监听事件
  2. visibility: hidden 被隐藏的元素使用的空间保持不变
  3. opacity: 0transform: scale(0)transform: translate(-9999px, 0px)position: absolute; left: -999px 等等通过改变元素透明度或者平移、缩小等方式

JavaScript

JavaScript 有哪些数据类型?

JavaScript 共有八种数据类型,七种基本数据类型包含 StringNumberBooleanNullUndefinedSymbolBigInt,一种引用数据类型 Object,包含 ArrayDateFunction 等等。

JavaScript 数据类型有哪些判断方法?

1. typeof
    typeof '1' === 'string' => true
    typeof 1 === 'number' => true
    typeof true === 'boolean' => true
    typeof null === 'null'  => false // object
    typeof undefined === 'undefined' => true
    typeof {} === 'object' => true
    typeof [] === 'array' => false // object
    typeof function() {} === 'function' => true

    总结: 可以用来判断除 Null 以外的基本类型。

2. instanceof
    '1' instanceof String => false
    1 instanceof Number => false
    true instanceof Boolean => false
    {} instanceof Object => true
    [] instanceof Array => true
    function () {} instanceof Function => true

    总结: 可以用来判断引用数据类型。

3. constructor
    '1'.constructor === String => true
    (1).constructor === Number => true
    true.constructor === Boolean => true
    {}.constructor === Object => true
    [].constructor === Array => true
    function () {}.constructor === Function => true

    总结: 可以用来判断所有数据类型,有缺陷。

4. Object.prototype.toString.call
    Object.prototype.toString.call('1') === '[object String]' => true
    Object.prototype.toString.call(1) === '[object Number]' => true
    Object.prototype.toString.call(true) === '[object Boolean]' => true
    Object.prototype.toString.call(null) === '[object Null]' => true
    Object.prototype.toString.call(undefined) === '[object Undefined]' => true
    Object.prototype.toString.call({}) === '[object Object]' => true
    Object.prototype.toString.call([]) === '[object Array]' => true
    Object.prototype.toString.call(function () {}) === '[object Function]' => true

    总结: 可以用来判断所有数据类型,最完美的解决方案。

undefined 与 null 有什么区别?

  1. UndefinedNull 都是基本数据类型,他们的值分别为 undefinednull
  2. undefined 代表的是变量未定义,null 代表的是一个空对象,常用来作为变量的初始值
  3. 通过 typeof 判断类型时,undefined 返回 undefinednull 返回 object
  4. undefinedJavaScript 中不是一个保留字,可以使用 undefined 来作为一个变量名

var、let、const 的区别

  1. 块级作用域 var 没有块级作用域,letconst 具有块级作用域
  2. 变量提升 var 存在变量提升,let 及 const` 不存在变量提升
  3. 重复声明 var 可以重复声明变量,let 及 const 不可以重复声明变量
  4. 暂时性死区 var 声明的变量可以在声明之前使用,let 及 const 声明的变量在声明之前不可用
  5. 指针指向 varlet 声明的变量可以改变指针指向,const 声明的变量不可以改变指针指向

ES6 的新特性有哪些?

  1. letconst 声明变量
  2. 字符串模版
  3. 箭头函数
  4. 解构赋值
  5. 扩展运算符

闭包

简介

闭包是指有权访问另一个函数作用域中变量的函数。

用途

  1. 从外部读取函数内部的变量
  2. 让变量的值始终保持在内存中

原型及原型链

原型

JavaScript 中,每一个对象从被创建的时候就和另一个对象关联,从另一个对象上继承其属性,这个另一个对象就是原型。

原型链

当我们访问一个对象的属性时,先在对象的本身查找,找不到就去对象的原型上找,如果还是找不到,就去对象的原型的原型上找,如此继续,直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,这个查找属性的链路就叫原型链。

事件模型

定义

事件模型描述的是从页面中接收事件的顺序,包括下面几个阶段,分别为:事件捕获阶段 处于目标阶段 事件冒泡阶段

事件委托

事件委托指的是,不在事件的发生地上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,从而来做出不同的响应。

如何判断一个数组?

1. Object.prototype.toString.call([]) === '[object Array]'
2. [] instanceof Array === true
3. [].constructor === Array
3. Array.isArrray([]) === true

类数组

定义

一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象。

类数组转数组的方法有哪些?

1. Array.prototype.slice.call(arguments)
2. Array.form(arguments)
3. [...arguments]

JavaScript 继承有哪几种?

1. 原型链继承
    
    原理:将父类的实例作为子类的原型
    缺点:父类的引用属性会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
    
    function Parent() {
        this.info = { name: 'yhd', age: 18 };
    };
    Parent.prototype.getInfo = function() {
        console.log(this.info);
    };
    
    function Child() {}; 
    Child.prototype = new Parent();
    
    let child1 = new Child();
    child1.info.gender = '男';
    child1.getInfo(); // { name: 'yhd', age: 18, gender: '男' }
    
    let child2 = new Child();
    child2.getInfo(); // { name: 'yhd', age: 18, gender: '男' }
    
2. 构造函数继承

    原理:使用父类的构造函数来增强子类实例,等同于复制父类的实例属性和方法给子类
    缺点:只能继承父类的实例属性和方法,不能继承父类的原型属性和方法
    
    function Parent() {
        this.info = { name: 'yhd', age: 18 };
        this.getInfo = function() {
            console.log(this.info);
        };
    };
    
    function Child() {
        Parent.call(this);
    }
    
    let child1 = new Child();
    child1.info.gender = '男';
    child1.getInfo(); // { name: 'yhd', age: 18, gender: '男' }
    
    let child2 = new Child();
    child2.getInfo(); // { name: 'yhd', age: 18 }
    
3. 原型链与构造函数组合继承

    原理:1. 通过父类的构造函数实现对父类实例属性和方法的继承
         2. 通过原型链实现对父类原型属性和方法的继承(会将父类的实例属性和方法添加到子类的原型上)
    
    function Parent(name, age) {
        this.name = name;
        this.age = age;
        this.info = { name: 'yhd', age: 18 };
    };
    Parent.prototype.getAge = function() {
        console.log(this.age);
    };
    Parent.prototype.getName = function() {
        console.log(this.name);
    };
    Parent.prototype.getInfo = function() {
        console.log(this.info);
    };
    
    function Child(name, age) {
        Parent.call(this, name, age);
    };
    Child.prototype = new Parent();
    
    let child1 = new Child('小红', 18);
    child1.info.gender = '男';
    child1.getName(); // 小红
    child1.getAge(); // 18
    child1.getInfo(); { name: 'yhd', age: 18, gender: '男' }
    
    let child2 = new Child('小明', 20);
    child2.getName(); // 小明
    child2.getAge(); // 20
    child2.getInfo(); { name: 'yhd', age: 18 }
    
4. 原型式继承

    原理:将某个对象直接赋值给构造函数的原型
    缺点:父类的引用属性会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
    
    function copyObject(object) {
        function Fun() {};
        Fun.prototype = object;
        
        return new Fun();
    };
    
    let person = {
        name: '小明',
        colors: ['red', 'blue', 'yellow'],
        getName: function() {
            console.log(this.name)
        },
        getColors: function() {
            console.log(this.colors)
        }
    };
    
    let child1 = copyObject(person);
    child1.name = '小红';
    child1.colors.push('green');
    child1.getName(); // 小红
    child1.getColors(); // ['red', 'blue', 'yellow', 'green']
    
    let child2 = copyObject(person);
    child2.name = '小王';
    child2.colors.push('black');
    child2.getName(); // 小王
    child2.getColors(); // ['red', 'blue', 'yellow', 'green', 'black']

5. 寄生式继承(在原型式继承的基础上,增强对象的实例属性和方法)

    原理:将某个对象直接赋值给构造函数的原型
    缺点:父类的引用属性会被所有子类共享,更改一个子类的引用属性,其他子类也会受影响
    
    function copyObject(object) {
        function Fun() {};
        Fun.prototype = object;
        
        return new Fun();
    };
    
    function createAnother(original) {
        let clone = copyObject(original);
        
        clone.getName = function() {
            console.log(this.name);
        }
        
        clone.getColors = function() {
            console.log(this.colors);
        }
    };
    
    let person = {
        name: '小明',
        colors: ['red', 'blue', 'yellow']
    };
    
    let child1 = createAnother(person);
    child1.name = '小红';
    child1.colors.push('green');
    child1.getName(); // 小红
    child1.getColors(); // ['red', 'blue', 'yellow', 'green']
    
    let child2 = createAnother(person);
    child2.name = '小王';
    child2.colors.push('black');
    child2.getName(); // 小王
    child2.getColors(); // ['red', 'blue', 'yellow', 'green', 'black']

6. 寄生式组合继承

    原理:1. 通过父类的构造函数实现对父类实例属性和方法的继承
         2. 通过原型链实现对父类原型属性和方法的继承
    
    function copyObject(object) {
        function Fun() {};
        Fun.prototype = object;
        
        return new Fun();
    };
    
    function inheritPrototype(child, parent) {
        let prototype = copyObject(parent.prototype);
        prototype.constructor = child;
        child.prototype = prototype;
    }
    
    function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'yellow']
    }
    
    Parent.prototype.getName = function() {
        console.log(this.name);
    }
    
    Parent.prototype.getColors = function() {
        console.log(this.colors);
    }
    
    function Child(name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    
    inheritPrototype(Child, Parent);
    
    Child.prototype.getAge = function() {
        console.log(this.age);
    };
    
    let child1 = new Child('小明', 18);
    child1.getName(); // '小明'
    child1.getAge(); // 18
    child.colors.push('green');
    child1.getColors(); // ['red', 'blue', 'yellow', 'green']
    
    let child2 = new Child('小红', 20);
    child2.getName(); // '小红'
    child2.getAge(); // 20
    child2.getColors(); // ['red', 'blue', 'yellow']

Promise

阅读《关于Promise的个人理解》此篇文章

常用手写

节流

let timer = null;

if (timer) return;

timer = setTimeout(() => {
    clearTimeout(timer);
    console.log('Hello World!');
}, 200);

防抖

let timer = null;

if (timer) clearTimeout(timer);

timer = setTimeout(() => {
    clearTimeout(timer);
    console.log('Hello World!');
}, 200);

深拷贝

function deepCopy(obj) {
    let _obj = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
    
    for (let i in obj) {
        _obj[i] = typeof value === "object" && value !== null ? deepCopy(obj[i]) : obj[i];
    };
    
    return _obj;
};

冒泡排序

function bubbleSort(array) {
    let t = 0;
    
    for(let i = 0; i < array.length - 1; i++) {
        for (let j = 0; j < array.length - 1 - i; j++) {
            if (array[j] > array[j+1]) {
                t = array[j];
                array[j] = array[j+1];
                array[j+1] = t;
                t = 0;
            }
        }
    }
};

选择排序

function selectSort(array) {
    let t = 0;
    let index = 0;

    for(let i = 0; i < array.length; i++) {
        index = i;
        
        for(let j = i; j < array.length; j++) {
            if (array[j] < array[index]) {
                index = j;
            }
        }
        
        t =  array[index]
        array[index] = array[i]
        array[i] = t
    }
};

自定义 call 函数

function myCall(target, ...args) {
    target = target || window;
    const symbolKey = Symbol();
    target[symbolKey] = this;
    const res = target[symbolKey](...args);
    delete target[symbolKey];
    
    return res;
};

自定义 apply 函数

function myApply(target, args) {
    target = target || window;
    const symbolKey = Symbol();
    target[symbolKey] = this;
    const res = target[symbolKey](...args);
    delete target[symbolKey];
    
    return res;
};

自定义 bind 函数

function myBind(target, ...outArgs) {
    target = target || {};
    const symbolKey = Symbol();
    target[symbolKey] = this;
    
    return function(...innerArgs) {
        const res = target[symbolKey](...outArgs, ...innerArgs);
        
        return res;
    }
}

webpack

简介

webpack 是一种用于构建 JavaScript 应用程序的静态模块打包器,它能够以一种相对一致且开放的处理方式,加载应用中的所有资源文件,并将其合并打包成浏览器兼容的资源文件。

核心概念

  1. entry 一个可执行模块或者库的入口
  2. chunk 多个文件组成一个代码块
  3. loader 文件转换器
  4. plugin 扩展功能的插件
  5. output 编译结果文件输出

构建流程

  1. 初始化参数 结合配置文件和 Shell 的参数,计算出最终的配置参数
  2. 开始编译 通过初始化参数初始化 compiler 对象,注册所有配置的插件,执行 compiler 对象的 run 方法开始执行编译
  3. 确定入口 根据 entry 配置找出入口文件
  4. 编译模块 从配置的 entry 入口开始,根据配置的 loader 对模块进行转译,如果该模块还有依赖的模块,再递归本步骤直到所有依赖的文件都进行了转译
  5. 完成模块编译 经过 loader 编译完所有模块后,得到每个模块编译后的内容及每个模块之间的依赖关系
  6. 输出资源 根据入口文件和每个模块之间的依赖关系,生成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独文件加入到输出列表中这步是可以修改输出内容的最后机会
  7. 输出完成 根据输出项配置,将文件内容写到文件系统

Loader

定义

Loader 本质上是一个函数,负责代码的转译,即对接收的内容进行转译后并将结果返回,在 Module.rules 中以数组的形式进行配置

常见的 loader 及其作用

  1. raw-loader 加载文件的原始内容
  2. babel-loader 将 ES6 转为 ES5
  3. file-loader 可以指定要复制和放置资源文件的位置,并在代码中通过 URL 去引用输出的文件
  4. url-loaderfile-loader 功能相似,但可以通过设置阈值根据文件的大小使用不同的处理方式,大于阈值的交给 file-loader 处理,小于阈值的返回base64格式编码并将文件的 data-url 内联到 bundle
  5. img-loader 加载并压缩图片资源
  6. style-loaderCSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS 代码
  7. css-loader 加载 CSS 代码,支持模块化、压缩、文件导入等功能
  8. sass-loaderSASS/SCSS 代码转换成 CSS
  9. postcss-loader 扩展 CSS 语法,可以自动补齐 CSS3 前缀
  10. eslint-loader 通过 ESLint 检查 JavaScript 代码
  11. tslint-loader 通过 TSLint 检查 JavaScript 代码
  12. cache-loader 添加到一些开销较大的 loader 之前,将结果缓存到磁盘中
  13. source-map-loader 加载额外的 Source Map 文件

PLugin

定义

PLugin 本质上是一个函数,基于事件流框架 Tapable,通过监听 webpack 运行过程中广播的事件,在合适的时机通过 webpack 提供的 API 来改变输出结果,在 plugins 中以数组的形式进行配置

常见的 plugin 及其作用

  1. web-webpack-plugin 为单页面应用输出HTML
  2. css-minimize-webpack-plugin 压缩 CSS 代码
  3. uglifyjs-webpack-plugin 压缩 JavaScript 代码
  4. terser-webpack-plugin 压缩 ES6 代码
  5. mini-css-extract-pluginCSS 提取为独立文件,支持按需加载
  6. clean-webpack-plugin 清理目录
  7. ignore-plugin 忽略部分文件

Babel 原理

  1. 解析 进行代码分析,将代码分割成 token 流,再根据 token 流生成 AST
  2. 转换 遍历 AST 节点并生成新的 AST 节点
  3. 生成 根据新的 AST 生成目标代码

文件监听

文件监听原理

webpack 轮询判断文件文件的最后修改时间,当发现文件修改时间发生变化后,会先缓存起来等到 aggregateTimeout 再统一执行,重新构建出新的输出文件

开启文件监听方式

  1. 启动 webpack 命令时,带上 --watch 参数
  2. 在配置文件 webpack.config.js 中设置 watch: true

热更新

定义

webpack 热更新又称热替换,全称 Hot Module Replacement,缩写 HRM,此机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。

原理

  1. 使用 webpack-dev-server (WDS) 托管静态资源
  2. 浏览器加载页面后与 WDS 建立 WebSocket 连接
  3. webpack 监听到文件变化后,向浏览器推送更新并携带新的 hash
  4. 浏览器接收到 hash 事件与之前的 hash 进行对比,加载变更的增量模块
  5. webpack 触发变更模块的 module.hot.accept 回调,执行代码变更逻辑

Tree Shaking

定义

Tree Shaking 是一种基于 ES Module 规范的 Dead Code Elimination 技术,它会在运行过程中静态分析模块之间的导入导出,确定 ESM 模块中哪些导出值未曾被其他模块引用,将其删除,以此实现打包的优化。

原理

  1. 收集模块导出变量并记录到模块依赖关系图中,遍历模块依赖关系图并标记哪些导出未被使用
  2. 使用 Terser 将未被使用的导出删除

优化 webpack 构建的方式有哪些?

  1. 使用高版本的 webpacknode
  2. 使用 thread-loader 进行多线程构建
  3. 压缩代码
  4. 缩小文件的打包范围
  5. 将静态资源存储到 CDN
  6. 利用缓存提升二次构建速度

git 常用命令

  1. git config --global user.name "" 配置用户名
  2. git config --global user.email "" 配置用户邮箱
  3. git branch 查看本地分支
  4. git branch -f 查看远程分支
  5. git branch -a 查看本地和远程分支
  6. git branch -d <branch-name> 删除本地分支
  7. git push origin -d <branch-name> 删除远程分支
  8. git checkout <branch-name> 切换分支
  9. git checkout -b <branch-name> 创建并切换到新建分支
  10. git status 查看文件状态
  11. git log 查看提交日志
  12. git add . 将全部文件上传到暂存区
  13. git commit -m"" 提交到本地仓库
  14. git push <主机名> <本地分支名>:<远程分支名> 提交到远程仓库
  15. git fetch <主机名> <远程分支名>:<本地分支名> 拉取远程分支代码到本地分支
  16. git pull <主机名> <远程分支名>:<本地分支名> 拉取远程分支代码到本地分支并合并
  17. git merge <branch-name> 合并分支到本地分支

浏览器

重排与重绘

什么是浏览器的重排与重绘?

重排是重新排列,重新计算元素的位置和几何信息,会影响部分或整个页面的布局。

触发重排与重绘的情景

  1. 页面首次渲染
  2. 页面内容改变
  3. 添加或者删除可见的DOM元素
  4. 元素位置、尺寸、样式改变
  5. 浏览器窗口尺寸改变

如何减少重排与重绘

  1. 尽量避免频繁操作DOM
  2. 尽量避免频繁操作样式
  3. 尽量避免频繁读取会引发回流/重绘的属性
  4. 对具有复杂动画的元素使用绝对定位,使它脱离文档流
  5. 尽量避免使用table布局
  6. 尽量避免设置多层内联样式
  7. 尽量避免使用CSS表达式
  8. 尽量在DOM树的最末端改变class

总结:重排一定会引起重绘,重绘不一定引起重排

垃圾回收机制

简介

由于字符串、数组和对象没有固定大小,所以当他们的大小已知时,才能对他们进行动态的存储分配,JavaScript 每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要这样动态的分配了内存,最终都要释放这些内存以便他们能够被再次使用,否则,JavaScript 的解释器将会消耗完系统中所有可用的内存,造成系统崩溃,JavaScript 通过垃圾回收机制来自动管理内存.

原理

现在大多数浏览器都采用 标记清除 算法来进行内存的管理:当变量进入环境时,将变量标记为 进入环境,当变量离开环境时,将变量标记为 离开环境,在某一时刻垃圾回收器会过滤环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。

chrome V8引擎的垃圾回收策略

  1. 将整个堆内存分为新生代内存和老龄代内存,所有的内存分配操作发生在新生代
  2. 新生代内存又分为 FormTo 两部分空间,所有的内存分配操作发生在 Form 空间
  3. 新生代空间GC(复制算法)
    • From 空间中存活的对象复制到 To 空间,释放未存活的对象
    • 转换二者的角色,Form变 为 To 空间,To 变为 From 空间
    • 如果某个对象已经经历过一次复制算法,就将该对象复制到老龄代空间
    • 如果 To 空间使用率超过25%,将整个空间的对象复制到老龄代空间,主要为了角色转换之后留足分配内存的空间
  4. 老龄代空间GC(标记清除与标记合并)
    • 主要采用 标记清除 算法,通过标记清除算法清理未存活的对象
    • 清理完成之后会是内存空间出现不连续的状态,这种内存碎片会对后续的内存分配造成问题,因此在内存空间不足的时候采用 标记合并 算法,将活着的对象移动到内存的一端,再清除另一端的对象
  5. 新生代GC触发的要比老龄代GC频繁

同源策略

同源策略指的是一个域下的 js脚本 在未经允许的情况下,不能够访问另一个域的内容,同源的指的是两个域的协议、域名、端口号必须相同,否则则不属于同一个域,同源政策的目的主要是为了保证用户的信息安全,它只是对 js脚本 的一种限制,并不是对浏览器的限制,对于一般的 imgscript脚本 请求都不会有限制。

引起内存泄漏有哪些原因?

  1. 使用未声明的变量,会创建了一个全局变量,而使这个变量一直留在内存中无法被回收
  2. 设置了 setTimeoutsetInterval 未清除时会被一直留在内存中无法被回收
  3. 不合理的使用闭包,会导致某些变量一直被留在内存中无法被回收

localStorage、sessionStorage、Cookie之间有什么区别?

  1. 生命周期 localStorage 永久有效,sessionStorage 仅在当前会话下有效,关闭浏览器后则失效,Cookie 可以设置过期时间
  2. 数据大小 localStoragesessionStorage 可以存储5MB数据,Cookie 可以存储4KB数据
  3. 网络请求 localStoragesessionStorage 仅在客户端中保存,Cookie 则会被携带在 HTTP 请求头中传输到服务器端

浏览器缓存

阅读《关于浏览器缓存的个人理解》此篇文章

HTTP 与 HTTPS

阅读《关于HTTP、HTTPS的个人理解》此篇文章

三次握手与四次挥手

阅读《关于三次握手、四次挥手的个人理解》此篇文章

地址栏输入一个URL后发生了什么?

阅读《一个页面从输入URL到页面呈现发生了什么》此篇文章

React

React 简介

React 是一个用于构建用户界面的 JavaScript 库,提供 UI 层面的解决方案,React 拥有组件化、JSX语法、虚拟 DOM、单向数据绑定、数据驱动视图等特性。

React JSX 语法

JSX 语法是 JavaScript 语法的扩展,用于描述 UI 界面,并拥有 JavaScript 的全部功能,可以和 JavaScript 融合在一起使用,JSX 的顶层只能有一个根元素,为了方便阅读,我们通常在 JSX 的外层包裹一个小括号。

React 虚拟 DOM

简介

虚拟 DOM 是一个轻量级的 JavaScript 对象,是真实 DOM 结构的映射。

工作原理

挂载阶段 React 结合 JSX 的描述,构建出虚拟 DOM 树,然后通过 ReactDOM.render 将虚拟 DOM 映射到真实 DOM。

更新阶段 数据变化时会再次构建新的虚拟 DOM 树,借助算法对比出哪些虚拟 DOM 需要被改变,然后再将这些改变作用于真实 DOM。

优点

虚拟 DOM 能够有效避免真实 DOM 频繁更新,减少重排与重绘,能够很大程度上提高性能。

React Diff 算法

React Diff 策略

  1. Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
  2. 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
  3. 对于同一层级的一组子节点,它们可以通过唯一 key 进行区分

React Diff 算法

  1. React 对虚拟 DOM 树进行 分层比较、层级控制,只对同一父节点的子节点进行比较,当发现某一子节点不在了直接删除该节点以及其所有子节点
  2. React 对于同一类型的组件,通过 shouldComponentUpdate 方法优化,减少组件不必要的比较,对于非同一类型的组件,则替换整个组件下的所有子节点
  3. React 通过唯一 Key 来判断老集合中是否存在相同的节点,如果有,则判断是否可复用,如果没有,则创建新节点

React 组件通信

父子组件通信

父传子 父组件调用子组件时,将需要传递的信息存放到自定义属性中,子组件通过 props 来获取相应的数据。

子传父 父组件将一个函数传递给子组件,子组件调用该回调函数,便可以向父组件通信。

跨级组件通信

  1. 通过中间组件层层传递 props
  2. 通过 context
  3. 通过 redux

兄弟组件通信

  1. 通过事件订阅
  2. 通过 redux

React Hook

简介

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hook 使用遵循哪些规则?

  1. 只允许在最顶层使用 Hook
  2. 只允许在 React 函数中调用 Hook
  3. 只允许在自定义 Hook 中调用其他 Hook

Hook 解决了 class 类组件哪些痛点?

  1. 组件之间复用状态逻辑很难
  2. 复杂组件变得难以理解
  3. 难以理解的 class

Redux

简介

Redux 是一个 JavaScript 状态容器,提供可预测化的状态管理。

核心

store 用来连接 actionreducer

action 用来传递 statestore,是 store 的唯一来源。

reducer 用来处理 action,通过传入新的 stateaction 来指明如何更新 state,并返回新的 state

三大原则

单一数据源 整个应用的 state 被储存在唯一一个 store 中。

state 是只读的 改变 state 的唯一方法就是触发 actionaction 是一个用于描述已发生事件的普通对象。

使用纯函数修改 为了描述 action 如何改变 state,你需要编写 reducerreducer 只是一些纯函数,它接收先前的 stateaction,并返回新的 state

工作原理

  1. Redux 会将整个应用状态存储到到一个地方,称为 store,这个 store 里面保存一棵状态树
  2. 组件改变 state 的唯一方法是通过调用 storedispatch 方法,触发一个 action,这个 action 被对应的 reducer 处理,完成 state 更新
  3. 各个组件通过订阅 store 中的状态来更新自己的视图

react-router

react-router-dom、react-router、history 三者之间有什么关系?

  1. historyreact-router 的核心,也是整个路由原理的核心,集成了 popStatehistory.popStatehistory.replaceState 等方法
  2. react-routerreact-router 的核心,封装了 RouterSwitchRoute 等核心组件,实现了从路由切换到组件更新的核心功能

react-router 原理

  1. 调用 history.push() 方法改变路由,触发 history.popState() 方法,然后触发 historysetstate 方法,生成新的 location 对象,通知 Router 组件更新
  2. Router 组件接收到新的 location 后将其传递给 Switch 组件
  3. Switch 组件接收到更新流后,根据 Route 组件配置的 path 属性,匹配出对应的 Route 组件并将其渲染

React 性能优化

  1. 使用 shouldComponentUpdate 来避免不必要的 dom 操作
  2. 使用 useMemouseCallback 来缓存计算结果和函数
  3. 使用 key 来辅助 Diff 算法进行虚拟 DOM 计算,避免不必要的渲染
  4. 使用 React.Fragment 来避免不必要的div