• 「随笔」前端面试 | 2022年前端面试基础必备



    前言

    本文主要介绍2022年前端面试基础试题


    1. CSS 盒子模型(Box Model)是什么?

    CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
    在这里插入图片描述

    • Margin(外边距) - 清除边框外的区域,外边距是透明的。
    • Border(边框) - 围绕在内边距和内容外的边框。
    • Padding(内边距) - 清除内容周围的区域,内边距是透明的。
    • Content(内容) - 盒子的内容,显示文本和图像。

    注:浏览器的兼容性问题
    IE5.X 和 6 在怪异模式中使用自己的非标准模型。这些浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度的总和。
    IE8 及更早IE版本不支持设置填充的宽度和边框的宽度属性。
    解决IE8及更早版本不兼容问题可以在HTML页面声明 即可。

    2. 网页布局有哪些?

    2.1 固定布局

    静态布局
    以像素为基本单位布局,像素是绝对单位

    特点:宽度都是固定的
    缺点:浏览器无法适配,页面小,内容显示不全,页面大,两侧空白过大

    2.2 流动布局

    HTML默认布局(DIV+CSS布局)

    • 块级元素(独占一行,宽度100%)
    • 内联元素(从左到右水平分布)

    特点:相对单位,根据页面进行等比改变
    缺点:文字无法进行流动

    2.3 浮动布局(float)

    CSS 的 Float(浮动),会使元素向左或向右移动,其周围的元素也会重新排列。
    在这里插入图片描述

    • 左浮动(几个浮动的元素放到一起,若有空间,它们将彼此相邻左对齐)float:left
    • 右浮动(几个浮动的元素放到一起,若有空间,它们将彼此相邻右对齐)float:right
    • 左右浮动(两个浮动的元素放到一起,若有空间,它们将彼此左右对齐)

    当我们想让两个块级元素再同一行排列是,可以使用浮动布局实现
    注:清除浮动,使用 clear:both;
    元素浮动之后,周围的元素会重新排列,为了避免这种情况,使用 clear 属性。

    特点:图文混排友好,块级元素布局友好
    缺点:浮动元素脱离文档流,无法撑起父元素,造成父元素高度坍塌,需配合clear:both

    2.4 表格布局

    • 父容器display:table
    • 子容器display:table-cell(空间平均划分:子级容器默认是自动平分宽度沾满父级容器;)

    对容器内内垂直居中,等高对齐布局友好
    特点:兼容性好,易上手,
    缺点:代码冗余,灵活性差,加载慢,SEO不友好

    2.5 响应式布局(responsive web design)

    • 百分比布局(bootstrap栅栏系统→传送门
    • rem布局(css3新增单位,移动端友好度极高)
    • 媒体查询@media screen(设置不同类型的媒体条件,适配不同设备pc大中小屏,移动端横竖屏,制作复杂,加载速度相对较慢)
    • flex布局(弹性布局,易上手,兼容IE9及以上)
    • vwvh布局(视窗单位,分为100份,相对视窗实际宽高自动计算适配,适合大数据大屏展示,1px、较小像素不好适配)

    特点:适配不同视窗网页结构(pc+手机+平板等),无滚动条,
    缺点:上手复杂度高,渲染速度相对较慢

    3. HTML5新标签新特性有哪些?

    • 用于绘画的 canvas 元素
    • 用于媒介回放的 video 和 audio 元素
    • 对本地离线存储的更好的支持
    • 新的特殊内容元素,比如 articlefooterheadernavsection
    • 新的表单控件,比如 calendardatetimeemailurlsearch

    HTML5新增语义化元素→传送门

    4. BFC是什么?

    Block Formatting Context 块级格式化上下文
    一个BFC区域包含创建该上下文元素的所有子元素,但是不包括创建了新的BFC的子元素的内部元素,BFC是一块块独立的渲染区域,可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素

    • 每个BFC区域只包涵其子元素,不包括子元素的子元素
    • 每个BFC区域都是独立隔绝,互不影响

    如何形成BFC区域:
    设置浮动,不包括none
    设置定位,absoulte或者fixed
    行内块显示模式,inline-block
    设置overflow,即hiddenautoscroll
    表格单元格,table-cell
    弹性布局,flex

    5. 浏览器加载页面顺序的工作机制是什么?

    • 用户输入网址(首次访问),浏览器向服务器发起请求,服务器返回HTML文件
    • 浏览器开始载入HTML代码,判断标签内是否有标签,若有服务器发出CSS文件的请求,服务器返回这个CSS文件
    • 浏览器继续载入HTML中的部分的代码。因为是异步请求,CSS文件开始渲染页面。
    • 若浏览器在代码中发现一个标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片加载完毕,而是继续渲染代码。
    • 服务器返回图片文件,影响了后面段落的布局,隐藏浏览器需要重新渲染这部分的代码。
    • 浏览器再代码中发现

    6. 如何加快HTML页面加载速度?

    • 删除不必要的空格和注释。
    • 将script和style代码移到外部文件。
    • 通过gulp等代码压缩工具。
    • 减少页面上引用文件数量,能合并就合并。
    • 减少HTTP连接数,下载到后引入。
    • 减少域名查询,DNS查询和解析不同域名耗时较久。如:Javascript、css、图片等资源引用
    • 缓存重用数据
    • 优化页面元素加载顺序,先加载文本后加载图片、多媒体等按需按序加载。
    • 避免使用嵌套table,渲染加载速度慢
    • 指定图像大小和table的大小,可以减少浏览器重新布局渲染

    7. CSS实现水平垂直居中的方式

    7.1 对单行元素进行居中

    .class{
    	height: 0.2rem;
    	line-height:0.2rem; // 垂直居中
    	text-align:center; // 水平居中
    	// vertical-align: middle; // 针对行内元素
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7.2 对文本进行居中

    .class{
    	display:flex;
    	align-items:center; // 垂直居中
    	justify-content:center; // 水平居中
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.3 transform的变形居中

    .father{
    	 position: relative;
    }
    .child{
    	position: absolute;
        top: 50%; 
        left: 50%;
        -webkit-transform: translate(-50%,-50%);
        -ms-transform: translate(-50%,-50%);
        transform: translate(-50%,-50%);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7.4 table表格居中

    .father{
    	display:table;
    	text align:center;
    }
    .child{
    	display:table cell;
    	vertical-align:middle;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    7.5 margin:auto实现居中

    .father{
    	position:relative;
    }
    .child{
    	width: 50%;  
        height: 50%;  
        overflow: auto;  
        margin: auto;  
        position: absolute;  
        top: 0; left: 0; bottom: 0; right: 0;  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    7.6 fixed实现居中

    .class{
    	position:fixed;
    	z-index: 99;//层叠属性值
    }
    
    • 1
    • 2
    • 3
    • 4

    7.7 margin实现居中

    .class{
    	margin:0 auto;//根据实际上下边距调整
    	display:block;//根据实际实际要求是否转化成行列元素和块级元素
    }
    
    • 1
    • 2
    • 3
    • 4

    特点缺点→传送门

    8. css常用选择器有哪些?

    CSS选择器的作用是从HTML页面中找出特定的某类元素。

    • 通配符选择器 *{属性:属性值;}常用于设置所有HTML标记
    • id 选择器 #id名{属性:属性值;}元素的id值是唯一的,只能对应于文档中某一个具体的元素。
    • 类(class)选择器 .class名{属性:属性值;}大多数HTML元素都可以定义class属性
    • 标签选择器 标签名{属性:属性值;}所有的HTML标记名都可以作为标签选择器,例如abodyph1等等。用标签选择器定义的样式对页面中该类型的所有标签都有效。
    • 伪选择器伪选择器是一种特殊的选择器,它分为伪类选择器伪对象选择器。前者分别对应HTML标记的五种状态:hover:link:focus:visited:active,后者根据对象内部的局部元素定义其样式:first-letter:first-line:before:after
    • 属性选择器 [标签名]{属性:属性值;}注:只有在规定了 !DOCTYPE 时,IE7 和 IE8 才支持属性选择器。在 IE6 及更低的版本中,不支持属性选择。
    • 并集选择器 标签名1,标签名2{属性:属性值;}同时匹配多个选择器,取多个选择器的并集,选择器之间用逗号隔开,如div,p{ }
    • 后代选择器 标签名1 标签名2{属性:属性值;}用来选择特定元素的后代.
    • 子代选择器 标签名1 > 标签名2{属性:属性值;}表示匹配第二个选择器,且为第一个选择器的元素的后代。
    • 兄弟选择器+~标签名1+标签名2{属性:属性值;}相邻兄弟选择器使用+号表示,如p+a{ },表示匹配紧跟第一个选择器并匹配第二个选择器的元素,如紧跟p元素后的a的元素。标签名1~标签名2{属性:属性值;}作用是查找某一个指定元素的后面的所有兄弟结点。

    css选择器→传送门

    注:样式优先级内联>id选择器>类(class)选择器>标签选择器

    9. SEO功能

    • 站点的TITLEKEYWORDSDESCRIPTION等都可以直接在后台进行动态修改和添加;每篇文章,管理者也可以设定不定的标题、关键词和描述,并可以生成地图文件sitemap.xml,以方便搜索引擎收录。
    • 重要的HTML代码放前面。语义化HTML标签。
    • 图片alt属性需要填写相关信息。
    • iframe不会被搜索引擎收录。

    10. ES6新增的方法

    10.1 两种声明的方式

    letconst块级作用域,同一作用域不允许重复声明变量。

    • 变量提升 var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefinedletconst不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错。
    • 暂时性死区
      var不存在暂时性死区
      letconst存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
    • 块级作用域
      var不存在块级作用域
      letconst存在块级作用域
    • 重复声明
      var允许重复声明变量
      letconst在同一作用域不允许重复声明变量
    • 修改声明的变量
      varlet可以
      const声明一个只读的常量。一旦声明,常量的值就不能改变
      使用

    注:能用const的情况尽量使用const,其他情况下大多数使用let,避免使用var

    10.2 解构赋值

    解构赋值就是从目标对象或数组中提取自己想要的变量
    默认值

    let arr=[1,2]
    let [a,b,c=10]=arr //其中的10就是默认值
    console.log(a,b,c);
    //在浏览器中打印出来的是[1,2,10]
    
    • 1
    • 2
    • 3
    • 4

    交换变量

    let arr=[1,2,3]
    let [a,b,c=10]=arr //其中的10就是默认值
    console.log(a,b,c);  //那么c就被重新赋值
    //在浏览器中打印出来的是[1,2,3]
    
    • 1
    • 2
    • 3
    • 4

    将剩余数组赋给一个变量

    let [a,b,[...c]] = [1,2,3,4,5,6,7] // a=1,b=2,c=[3,4,5,6,7]
    let [a,b,[...c]] = 'hello'
    console.log(a,b,c) // h e [llo]
    
    • 1
    • 2
    • 3

    给新的变量名赋值

     let {a,b} = {a:1,b:2} // a=1,b=2
    
    • 1

    10.3 promise方法

    通过新建一个 Promise,更加优雅地书写复杂的异步任务。我们之前遇到的异步任务都是一次异步,如果需要多次调用异步函数呢?例如,如果我想分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒:

    // 地狱式回调
    setTimeout(function () {
        console.log("First");
        setTimeout(function () {
            console.log("Second");
            setTimeout(function () {
                console.log("Third");
            }, 3000);
        }, 4000);
    }, 1000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    // 构造 Promise 优化地狱回调
    new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("First");
            resolve();
        }, 1000);
    }).then(function () {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                console.log("Second");
                resolve();
            }, 4000);
        });
    }).then(function () {
        setTimeout(function () {
            console.log("Third");
        }, 3000);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    注:resolve 代表一切正常 reject 是出现异常时所调用的

    // 拓展Promise "计时器" 代码
    new Promise(function (resolve, reject) {
        var a = 0;
        var b = 1;
        if (b == 0) reject("Divide zero");
        else resolve(a / b);
    }).then(function (value) {
        console.log("a / b = " + value);
    }).catch(function (err) {
        console.log(err);
    }).finally(function () {
        console.log("End");
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Promise 类有 .then() .catch().finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列

    10.4 数据类型symbol和map

    symbol
    symbol 是ES6 引入了一种新的基本数据类型(原始数据类型) Symbol ,表示独一无二的值。它是JavaScript 语言的第七种数据类型,前六种是: undefinednull 、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

    // Symbol语法
    let sym1 = Symbol('vitian')
    let sym2 = Symbol('vitian')
    Symbol('vitian') === Symbol('vitian') // false
    sym1 === sym2 // false
    // Symbol.for() 重新使用同一个 Symbol 值
    let sym1 = Symbol.for('vitian')
    let sym2 = Symbol.for('vitian')
    sym1 === sym2 // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Symbol的值是唯一的,用来解决命名冲突的问题
    Symbol值不能与其他数据类型进行运算
    Symbol定义得的对象的属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名

    Map
    Map是一个类似于对象的数据类型,它是object的升级版,它可以将任何类型的数据作为key值;

    // map的写法
    let a = new Map();  //创建一个map对象
    a.set(name , 'vitian');  //赋值
    // 通过get方法获取 map数据类型中的数据
    const b = new Map([ ['name','vitian'] , ['age',18] ]);
    console.log(b.get('name'));  //获取Map数据类型并在打印输出 'vitian'
    // 去重数组的重复对象
    let arr = [1, 2, 3, 2, 1, 1]
    [... new Set(arr)]	// [1, 2, 3]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    共同点:set、Map可以储存不重复的值
    不同点:set是以 [value, value]的形式储存元素,Map是以 [key, value] 的形式储存

    10.5 箭头函数

    函数参数的默认值

    this.$nextTick(function(){
    	// Do SomeThing
    })
    
    • 1
    • 2
    • 3
    this.$nextTick(()=>{
    	// Do SomeThing
    })
    
    • 1
    • 2
    • 3

    10.6 async await 异步函数

    异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。

    // 将异步操作变得像同步操作一样容易,代码变得更好看,增加易读性
    async function asyncFunc() {
        await print(1000, "First");
        await print(4000, "Second");
        await print(3000, "Third");
    }
    asyncFunc();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // 处理异常的机制将用 try-catch 块实现
    async function asyncFunc() {
        try {
            await new Promise(function (resolve, reject) {
                throw "Some error"; // 或者 reject("Some error")
            });
        } catch (err) {
            console.log(err);
            // 会输出 Some error
        }
    }
    asyncFunc();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    10.7 class 以及 extends继承

    新的class写法让对象原型的写法更加清晰,更加面向对象编程的语法。

    class Foo{
        static classMethod(){
            return 'vitian';
        }
    }
    console.log(Foo.classMethod()); // =>vitian
    let foo=new Foo();
    foo.classMethod();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // 父类的静态方法可以被子类继承
    class Foo{
        static classMethod(){
            return 'vitian';
        }
    }
    class Bar extends Foo{
    }
     
    console.log(Bar.classMethod()); // =>vitian
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    10.8 模块化

    模块化是指将一个很大的程序文件,拆分为许多个小的文件,然后将多个小文件组合起来。

    • export 命令:用于规定模块的对外接口
    • import 命令:用于输入其他模块提供的功能
    //export 暴露方式1 分别暴露 js/a1.js
    export let a= 'vitian';
    export function b() {
       	return "vitian.vip";
    };
    //export 暴露方式2 统一暴露 js/a2.js
    let a= 'vitian';
    function b() {
       	return "vitian.vip";
    };
    export {a, b};
    //export 暴露方式3 默认暴露 js/a3.js
    export default {
       	a: 'vitian',
       	change: function(){
           	return "vitian.vip";
       	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    // import 导入方式1 通用的导入方式
    import * as _a1 from "js/a1.js"; // 引入 a1.js 模块内容
    _a1.a; // vitian
    import * as _a2 from "js/a2.js"; // 引入 a2.js 模块内容
    _a2.b(); // vitian.vip
    import * as _a3 from "js/a3.js"; // 引入 a3.js 模块内容
    _a3.default.change(); // vitian.vip
    
    // import 导入方式2 解构赋值形式
    import {a,b}  from "js/a1.js"; // 引入 a1.js 模块内容
    console.log(a); // vitian
    import {a as _a2,b as _b2}  from "js/a2.js"; // 引入 a2.js 模块内容
    console.log(_b2); // vitian.vip
    import {default as _a3}  from "js/a3.js"; // 引入 3.js 模块内容
    console.log(_a3.change()); // vitian.vip
    
    // import 导入方式3 只针对于默认暴露
    import _a3 from "js/a3.js"; // 引入 3.js 模块内容
    console.log(_a3.change()); // vitian.vip
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    10.9 includes函数

    用于判断数组是否包含给定的值,返回一个布尔值

    // 判断字符串
    var str = 'vitian'
    console.log(str.includes('vi'))   // 返回的结果是true
    // 判断数组
    var arr = ['a','b','c']
    console.log(arr.includes('a')) // 返回结果是true
    //判断NaN
    var arr = ['a','b',NaN]
    console.log(arr.includes(NaN))  //返回的记过是true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    10.10 find()和findIndex()查找函数

    find()findIndex()查找函数

     // 用来查找目标元素,找出第一个符合条件的数组,找到就返回该元素,找不到返回undefined.
     array.find((value, index, arr) => {value === '匹配对象'}
     // 用来查找目标元素,找到就返回元素的位置,找不到就返回-1。
     array.findIndex((value, index, arr) => {value === '匹配对象'})
    
    • 1
    • 2
    • 3
    • 4

    11. JS数组增删改查

    11.1 增

    // 方法1 数组.push():向数组的末尾添加元素 params多个任意类型值
    let arr = [1,2],
       	newArr = arr.push(3);
    console.log(arr) // push方法会改变原数组,向数组的末尾添加元素 => [1,2,3]
    console.log(newArr) // 增后的数组长度 => 3 
    
    // 方法2 数组.unshift():向数组开头添加元素 params多个任意类型值
    let arr = [1,2],
       	newArr = arr.unshift(3);
    console.log(arr) // unshift方法会改变原数组,向数组开头添加元素 => [3,1,2]
    console.log(newArr) // 增后的数组长度 => 3 
    
    // 方法3 数组.concat():向数组的末尾添加元素 params多个任意类型值
    let arr = [1,2],
       	newArr = arr.concat(3);
    console.log(arr) // concat方法不会改变原数组 => [1,2]
    console.log(newArr) // 向数组的末尾添加元素 => [1,2,3] 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    11.2 删

    // 方法1 数组.pop():删除数组中最后的那项 params多个任意类型值
    let arr = [1,2,3],
       	newArr = arr.pop();
    console.log(arr) // pop方法会改变原数组,删除数组中最后的那项 => [1,2]
    console.log(newArr) // 被删除的那项 => 3 
    
    // 方法2 数组.shift():删除数组中开头的那项元素 params多个任意类型值
    let arr = [1,2,3],
       	newArr = arr.shift();
    console.log(arr) // shift方法会改变原数组,删除数组中开头的那项元素 => [2,3]
    console.log(newArr) // 被删除的那项 => 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    11.3 改

    // 数组.splice():数组从索引n开始向后截取n个元素,并在此插入元素n params多个任意类型值
    let arr = [1,2,3],
       	newArr = arr.splice(0,1,3);
    console.log(arr) // splice方法会改变原数组,从索引0开始向后截取1个元素,并在此插入元素3 => [3,2,3]
    console.log(newArr) // 被截取的那项 => [0]
    
    // 拓展1 增 
    let arr = [1,2,3],
       	newArr = arr.splice(arr.length-1,0,4);
    console.log(arr) //从数组末尾开始向后截取0个元素,并在此插入新元素7 => [1,2,3,4]
    console.log(newArr) // 被截取的项 => []
    
    // 拓展2 删 
    let arr = [1,2,3],
       	newArr = arr.splice(0,1);
    console.log(arr) //从索引0开始向后截取1个元素 => [2,3]
    console.log(newArr) // 被截取的项 => [0]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    11.4 查

    // slice方法不会改变原数组
    let arr = [1,2,3],
       	newArr = arr.slice(0,arr.length);
    console.log(arr) //从索引0开始查询到数组末尾的后一项(复制数组) => [1,2,3]
    console.log(newArr) // 复制数组 => [1,2,3]
    
    let arr = [1,2,3],
       	newArr = arr.slice(0.9,1.1); // 当x和y为小数时,会先通过parseInt转换为整数在进行查询操作 => 从索引0开始查询到索引为1的元素
    console.log(arr) // => [1,2,3]
    console.log(newArr) //  => [1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    12. JS常用的排序方法

    12.1 冒泡排序(升序)

    冒泡排序法,也叫升序排序法,但是相比起二分法查找只能应用于有序数列

    let arr = [1,3,7,5,9];
    bubblingFunc(arr){
    	if(Array.isArray(arr)){
    		if(arr.length==1){
    			return arr;
    		}
    		let data = null;
    		for(let i = 0; i< arr.length; i++) {
    			for (let j = 0; j< arr.length; j++) {
    				if(arr[j] > arr[i]) {
    					data = arr[j];
    					arr[j] = arr[j + 1];
    					arr[j + 1] = data;
    				}
    			}	
    		}
    		return arr;
    	}
    }
    console.log(bubblingFunc(arr)); // => [1,3,5,7,9]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    12.2 选择排序(降序)

    在未排序序列中找到最小元素,把它放到排序序列起始位置。从剩余未排序序列中继续寻找最小元素,然后放在排序序列末尾。以此类推,直至所有元素排序完成。

    let arr = [1,3,7,5,9];
    selectionFunc(arr){
    	if(Array.isArray(arr)){
    		if(arr.length==1){
    			return arr;
    		}
    		let data = null;
    		for(let i = 0; i< arr.length-1; i++) {
    			let min = i; 
    			for (let j = i+1; j< arr.length; j++) {
    				min = arr[min] < arr[j] ? j : min
    			}
    			[arr[i],arr[min]] = [arr[min],arr[i]];	
    		}
    		return arr;
    	}
    }
    console.log(selectionFunc(arr)); // => [9,7,5,3,1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    12.3 递归排序(快速)

    从数列中取出一个数作为参考,分区过程。将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。对左右区间重复第二步,直到各区间只有一个数。

    let arr = [1,3,7,5,9];
    recursiveFunc(arr){
    	if(Array.isArray(arr)){
    		if(arr.length==1){
    			return arr;
    		}
    		let mid= Math.ceil(arr.length / 2);
    		let val = arr.splice(mid, 1);
    		let left = [];
            let right = [];
    		array.forEach((value)=> {
                 if (value > val) {
                     left.push(value);
                 }
                 else {
                     right.push(value);
                 }
             });
    	     return recursiveFunc(left).concat(val , digui(right));
    	}
    }
    console.log(recursiveFunc(arr)); // => [9,7,5,3,1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    12.4 插入排序

    将要排序的数组分成两部分,每次从后面的部分取出索引最小的值插入前面适当的位置。

    let arr = [1,3,7,5,9];
    insertFunc(arr){
    	if(Array.isArray(arr)){
    		if(arr.length==1){
    			return arr;
    		}
    	 	for (let i = 1; i < arr.length; i++) {
                let current = arr[i];
                let preIndex = i - 1;
                 while (preIndex >= 0 && arr[preIndex] < current) {
                     arr[preIndex + 1] = arr[preIndex];
                     preIndex--;
                 }
                 arr[preIndex + 1] = current;
             }
             return arr;
    	}
    }
    console.log(insertFunc(arr)); // => [9,7,5,3,1]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    12.5 sort倒叙

    let arr = [1,3,7,5,9];
    arr .sort((a,b)=>{
    	return b - a;
    })
    console.log(arr );// => [9,7,5,3,1]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    12.6 reverse数组元素方向反转

    let arr = [1,3,7,5,9];
    arr.reverse();
    console.log(arr);// => [9,5,7,3,1]
    
    • 1
    • 2
    • 3

    13. JS去重方法

    const array = [1,1,2,'a','a','b',true,true,false,undefined,undefined,NaN,NaN];
    
    • 1

    13.1 利用Set()+Array.from()去重

    const result = Array.from(new Set(array)) // =>[1,2,a,b,true,false,undefined,NaN]
    
    • 1

    13.2 利用两层循环+数组的splice方法

    removeFunc(array) {
      for (let i = 0; i < array.length; i++) {
        for (let j = i + 1; j < array.length; j++) {
          if (array[i] === array[j]) {
            array.splice(j, 1)
            len-- 
            j-- 
          }
        }
      }
      return array
    }
    const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    13.3 利用indexOf去重

    removeFunc(array) {
      let data = []
      array.forEach(e=>{
    	if(data.indexOf(e) === -1) {
    		data.push(e)
    	}
      })
      return data
    }
    const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    13.4 利用includes去重

    removeFunc(array) {
      let data = []
      array.forEach(e=>{
    	if(!data.includes(e)) {
    		data.push(e)
    	}
      })
      return data
    }
    const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    13.5 利用filter()+indexOf()去重

    removeFunc(array) {
      return array.filter((e, i) => {
        return array.indexOf(e) === i
      })
    }
    const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    13.6 利用数组Map()去重

    removeFunc(array) {
      let map = new Map()
      let data = []
      array.forEach(e=>{
    	if (!map.has(e)) { // has()用于判断map是否包为item的属性值
          map.set(e, true) // 使用set()将item设置到map中,并设置其属性值为true
          data.push(e)
        }
      })
      return data 
    }
    const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    14. 本地存储的方式

    • cookie 适用标记用户与跟踪用户行为的情况
    • sessionStorage 适用敏感账号一次性登录
    • localStorage 适用长期保存在本地的数据
    • indexedDB 适用存储大量数据的情况、在线文档

    存储大小:cookie数据大小不能超过4k,sessionStoragelocalStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
    有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据; sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。
    数据与服务器之间的交互方式:cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端; sessionStoragelocalStorage不会自动把数据发给服务器,仅在本地保存。

    15. 闭包是什么

    闭包是指有权访问另外一个函数作用域中的变量的函数,可以理解为(能够读取其他函数内部变量的函数)

    特点: 正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的 变量,在函数执行完之后依旧保持没有被垃圾回收处理掉。
    缺点:闭包会导致内存占用过高,因为变量都没有释放内存。

    for (let i = 0; i < 4; i++) {
      setTimeout(()=> {
        console.log(i);
      }, 300);
    }
    // => 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    16. 防抖和节流是什么

    • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
    • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

    17. ajax是什么

    基于JQueryAJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
    特点:

    • 可以无需刷新页面而与服务器端进行通信。
    • 允许你根据用户事件来更新部分页面内容。

    缺点:

    • 没有浏览历史,不能回退
    • 存在跨域问题(同源)
    • SEO不友好
  • 相关阅读:
    设计模式学习笔记 - 桥接模式
    详解python中的序列类型概述
    从源码角度看Flink从上游获取数据、处理数据并发往下游算子的过程关键StreamInputProcessor
    轻松上手 | 使用国内资源安装 K3s 全攻略
    Git常用操作
    c#基础0-类型、起步
    神经网络的图像识别技术,人工神经网络图像识别
    在矩池云使用安装AgentTuning
    this 内存原理
    unity游戏开发中的随机算法
  • 原文地址:https://blog.csdn.net/qq_38209578/article/details/125507301