web前端面试宝典——带你直击面试重难点(40个经典题目,涵盖近90%的考点,码字2w,干货满满!)

简介: web前端面试宝典——带你直击面试重难点(40个经典题目,涵盖近90%的考点,码字2w,干货满满!)

var 和 let const 的区别

  • var 是 ES5 语法,let const 是 ES6 语法;var 有变量提升
  • var 和 let 是变量,可修改;const 是常量,不可修改
  • let const 有块级作用域,var 没有

示例:变量提升

    console.log(a) // undefined
    var a = 100
  // 相当于
  var a
    console.log(a) // undefined
    a = 100

示例:块级作用域

  for(let i = 0; i < 5; i++) {
        let j = i + 1
    }
    console.log(i)

81850439ab904da1972945763bbc1392.png

typeof 能判断哪些类型

  • undefined string number boolean symbol
  • object(注意:typeof null === ‘object’)
  • function

列举强制类型转换和隐式类型转换

  • 强制:parseInt parseFloat toString
  • 隐式:if、逻辑运算、==、+ 拼接字符串

手写深度比较 lodash.isEqual

isObject 来判断传入的参数是否为对象

如果不是对象直接返回它们的全等

如果传入的是同一个对象或数组,直接返回 true

如果传入的是两个不相等的对象或数组

则先判断长度,不一样直接返回 false

一样的话,以 obj1 为基准,和 obj2 依次递归比较

不相等返回 false,全满足了返回 true

    // 判断是否是对象或数组
    function isObject(obj) {
        return typeof obj === 'object' && obj !== null
    }
    function isEqual(obj1, obj2) {
        if(!isObject(obj1) || !isObject(obj2)) {
            // 值类型(参与 equal 的一般不会是函数)
            return obj1 === obj2
        }
        if(obj1 === obj2) {
            return true
        }
        // 两个都是对象或数组,而且不相等
        // 1.先取出 obj1 和 obj2 的 keys,比较个数
        const obj1Keys = Object.keys(obj1)
        const obj2Keys = Object.keys(obj2)
        if(obj1Keys.length !== obj2Keys.length) {
            return false
        }
        // 2.以 obj1 为基准,和 obj2 依次递归比较
        for(let key in obj1) {
            // 比较当前 key 的 val —— 递归
            const res = isEqual(obj1[key], obj2[key])
            if(!res) {
                return false
            }
        }
        // 3.全相等
        return true
    }
  // 测试
    const obj1 = {
        a: 10,
        b: {
            x: 100,
            y: 200
        }
    }
    const obj2 = {
        a: 10,
        b: {
            x: 100,
            y: 200
        }
    }
    console.log(isEqual(obj1, obj2)) // true

split() 和 join() 的区别

  • Array.join() 方法是 String.split() 方法的反向操作
  • split('-') 把字符串去掉 - 转为数组
  • join('-') 把数组加上 - 转为字符串
  '1-2-3'.split('-')
  [1, 2, 3].join('-')

e036457d1ddd46daa6571ca8be1ce800.png

pop、shift、push、unshift

以 arr 为例

  const arr = [10, 20, 30]

pop:

  • 功能:删除数组的最后一个元素
  • 返回值:被删除的元素
  • 是否对原数组造成影响:是
  // pop
  const popRes = arr.pop()
    console.log(popRes, arr) 

36c5afb1ab1343239e146cecb5681ac8.png

shift:

  • 功能:删除数组的第一个元素
  • 返回值:被删除的元素
  • 是否对原数组造成影响:是
    const shiftRes = arr.shift()
    console.log(shiftRes, arr)

56bef435579a4569ac944011b2e81049.png

push:

  • 功能:从尾部添加元素
  • 返回值:数组的长度
  • 是否对原数组造成影响:是
    const pushRes = arr.push(40)
    console.log(pushRes, arr)

96065b59101a4cf59ae5a2951a2c0bc1.png

unshift:

  • 功能:从头部添加元素
  • 返回值:数组的长度
  • 是否对原数组造成影响:是
    const unshiftRes = arr.unshift(5)
    console.log(unshiftRes, arr)

e0efe4b7a9b942ff9dfd0278b745202a.png

数组的API,有哪些是纯函数

纯函数:

  • 不改变源数组(没有副作用)
  • 返回一个数组
  const arr = [10, 20, 30]

concat:

    // concat
    const arr1 = arr.concat([40, 50]) 
    console.log(arr, arr1)

d5be727764f7459a84557ee9cf162a81.png

map:

    // map
    const arr2 = arr.map(num => num * 2)
    console.log(arr, arr2)

ff7117fda86c4306aa0730f5ddb4b5bd.png

filter:

    // filter
    const arr3 = arr.filter(num => num > 15)
    console.log(arr, arr3)

837181ffd73c42368776dcc1cb32c893.png

slice:

    // slice
    const arr4 = arr.slice(2)
    console.log(arr, arr4)

e22ce1bfd63749cbbf068e4795a74efc.png

数组 slice 和 splice 的区别

  • 功能区别:slice 切片,splice 剪接
  • 参数和返回值
  • 是否纯函数

slice:

  • slice 切片
  • 参数可传可不传,返回值是切片后的数组
  • 是纯函数(不改变原始数组)
  const arr  = [10, 20, 30, 40, 50]
    // slice 纯函数
    const arr1 = arr.slice()
    const arr2 = arr.slice(1, 4)
    const arr3 = arr.slice(2)
    const arr4 = arr.slice(-2)
    console.log(arr) // [10, 20, 30, 40, 50]
    console.log(arr1) // [10, 20, 30, 40, 50]
    console.log(arr2)// [20, 30, 40]
    console.log(arr3) // [30, 40, 50]
    console.log(arr4) // [40, 50]

9f20180cba3645e58d7519570e46ce63.png

splice:

  • splice 剪接
  • 参数可传可不传,返回值是剪接掉的元素的数组
  • 非纯函数(改变原始数组)
  • 解读:开始剪接的元素索引(1),剪接的元素个数(2),要添加的元素('a', 'b', 'c')
    const arr  = [10, 20, 30, 40, 50]
    // splice 非纯函数
    const spliceRes = arr.splice(1, 2, 'a', 'b', 'c')
    console.log(spliceRes) // [20, 30]
    console.log(arr) // [10, 'a', 'b', 'c', 40, 50]

6bcd0013dd8141e4b32b1a247e165685.png

    const arr  = [10, 20, 30, 40, 50]
    // splice 非纯函数
    const spliceRes1 = arr.splice(1, 0, 'a', 'b', 'c') 
    console.log(spliceRes1) // []
    console.log(arr) // [10, 'a', 'b', 'c', 20, 30, 40, 50]

2df9f937e0f349149c4066f5d43948f8.png

    const arr  = [10, 20, 30, 40, 50]
    // splice 非纯函数
    const spliceRes2 = arr.splice(1, 2) 
    console.log(spliceRes2) // [20, 30]
    console.log(arr) // [10, 40, 50]

058a6a8281e246f59d88fece33340c25.png

[10, 20, 30].map(parseInt) 输出结果

map:

  • 第一个参数是当前元素
  • 第二个参数是当前索引值

parseInt:

  • 第一个参数是要被解析的字符串
  • 第二个参数是要解析的数字的基数(进制数 2~36)
    const res = [10, 20, 30].map(parseInt)
    console.log(res)
    // 拆解
    const res2 = [10, 20, 30].map((num, index) => {
        return parseInt(num, index)
    })
    console.log(res2)

a83300d69af64ee49c578895ce9453d7.png

ajax 请求 get 和 post 的区别

  • get 一般用于查询操作,post 一般用于提交操作
  • get 参数拼接在 url 上,post 放在请求体内(数据体积可更大)
  • 安全性:post 易于防止 CSRF 攻击

函数 call 、apply 和 bind 的区别

  • call 传入的参数直接放进去
  • apply 传入的参数是一个数组或类数组
  • bind 返回的是一个新的函数,必须调用它才会被执行
    fn.call(this, p1, p2, p3)
    fn.apply(this, arguments)
    fn.bind(this, p1, p2, p3)()

示例:

    let zhangsan = {
        name: '张三',
        age: 20,
        myFun(from, to) {
            console.log(this.name + this.age, '来自' + from + '去往' + to)
        }
    }
    let lisi = {
        name: '李四',
        age: 22
    }
    zhangsan.myFun.call(lisi, '济南', '青岛')
    zhangsan.myFun.apply(lisi, ['济南', '青岛'])
    zhangsan.myFun.bind(lisi, '济南', '青岛')()

f437d8c473aa449aa06b2067288c9b5d.png

事件代理(委托)

  • 把 每个 li 的点击事件委托给 ul
  • 可以减少内存的消耗
  • 可以减少重复性操作
    <ul id="list">
        <li>item 1</li>
        <li>item 2</li>
        <li>item 3</li>
        ...
        <li>item n</li>
    </ul>
  //获取目标元素
    const lis = document.getElementsByTagName('li')
    //循环遍历绑定元素
    for (let i = 0; i < lis.length; i++) {
        lis[i].onclick = function (event) {
            console.log(event.target.innerHTML)
        }
    }

adc6a45ddffa44fba09447befe5a1ea4.png

闭包

闭包的影响:变量会常驻内存,得不到释放。闭包不要乱用

阻止事件冒泡和默认行为

  • 阻止冒泡:event.stopPropagation() 点击激活,不会弹出取消
  • 阻止默认行为:event.preventDefault() 点击超链接,不会跳转
    <p id="p1">激活</p>
    <a id="a1" href="https://baidu.com">取消</a>
  const p1 = document.getElementById('p1')
    const a1 = document.getElementById('a1')
    const body = document.body
    p1.addEventListener('click', function (event) {
        event.stopPropagation()
        alert('激活')
    })
    body.addEventListener('click', function (event) {
        alert('取消')
    })
    a1.addEventListener('click', function(event) {
        event.preventDefault()
    })

阻止冒泡 —> 点击激活,不会弹出取消:

da286f0ef1f94f9093129837ffc7a87c.png

如何减少 DOM 操作

  • 缓存 DOM 查询结果
  • 多次 DOM 操作,合并到一次插入
    <ul id="list"></ul>
  const list = document.getElementById('list')
  // 创建一个文档片段,此时还没有插入到 DOM 结构中
    const frag = document.createDocumentFragment()
    for(let i = 0; i < 10; i++) {
        const li = document.createElement('li')
        li.innerHTML = `List item ${i}`
        // 先插入文档片段中
        frag.appendChild(li)
    }
    // 都完成之后,再统一插入到 DOM 结构中
    list.appendChild(frag)

0ee12be72819427da002c8ba91f9518a.png

jsonp 的本质是 ajax 吗

  • 不是,因为没有用到 XMLHttpRequest 对象,所以 jsonp 不属于 ajax。
  • 它是通过能够绕过跨域的 <script> 标签实现的

document load 和 ready 的区别

    window.addEventListener('load', function() {
        // 页面的全部资源加载完才会执行,包括图片、视频等
    })
  window.addEventListener('DOMContentLoaded', function() {
        // DOM 渲染完即可执行,此时图片、视频还可能没有加载完
    })

== 和 ===

  • == 会尝试类型转换
  • === 严格相等
  • 除了 == null 之外,其他都用 ===

函数声明和函数表达式的区别

  • 函数声明:function fn() {…}
  • 函数表达式:const fn = function () {…}

示例:函数声明

    const res = sum(10, 20)
    console.log(res) // 30
    function sum(x, y) {
        return x + y
    }

示例:函数表达式

  const res = sum(10, 20)
    console.log(res) // 报错
    const sum = function (x, y) {
        return x + y
    }

f0c9f0bf2d5e49ac8629eee2ae2c4f3f.png

用 var 声明:

62520061ee2c45f6a2fd98d7cdd1917d.png

关于 this 的场景题

  • 第一个输出:this 指向当前对象 obj
  • 第二个输出:this 指向 window

    const obj = {
        count: 1,
        getCount: function() {
            return this.count
        }
    }
    console.log(obj.getCount()) // 1
    const func = obj.getCount
    console.log(func()) // undefined

e54b2554cc0b4d5cb87800b7c52fe510.png

new Object() 和 Object.create() 区别

  • {} 等同于 new Object(),原型 Object.prototype
  • Object.create(null) 没有原型
  • Object.create({…}) 可以指定原型

示例:

    const obj1 = {
        a: 10,
        b: 20,
        sum() {
            return this.a + this.b
        }
    }
    const obj2 = new Object ({
        a: 10,
        b: 20,
        sum() {
            return this.a + this.b
        }
    })

4f9401d609fd42ffab7f12411523fcf6.png

示例:

    const obj1 = {
        a: 10,
        b: 20,
        sum() {
            return this.a + this.b
        }
    }
    const obj2 = new Object(obj1)

1ff7dc5042b6458e8da7bbc9faae5bb1.png

示例:

    const obj1 = Object.create(null)
    const obj2 = new Object(null)

d8ef8b93e8de40399ed749b5f5b52ece.png

示例:

    const obj1 = {
        a: 10,
        b: 20,
        sum() {
            return this.a + this.b
        }
    }
    const obj2 = Object.create(obj1)

a3aa6cb1b4bc442aa92c6257d576e8ac.png

作用域和自由变量

  • let 的 i 和 for 循环里面的 i 不在同一块级作用域,相当于在 for 循环里面用 var 声明
    let i 
    for(i = 0; i <= 3; i++) {
        setTimeout(function(){
            console.log(i)
        },0)
    }

52b9607b2f8c4507a6082ca88c7fd408.png

  • for 循环里面用 let 声明,形成块级作用域,i 每增加一次,输出一次
    for(let i = 0; i <= 3; i++) {
        setTimeout(function(){
            console.log(i)
        },0)
    }

487bbc9741744881aa479018c93054ea.png

作用域和自由变量 2

  • 先把函数当成一条语句,不要看具体内容,等调用它的时候再看里面的内容
    let a = 100
    function test() {
        console.log(a) // 100
        a = 10
        console.log(a) // 10
    }
    test()
    console.log(a) // 10

60ed354ed8f54556819a3782088537be.png

常见正则表达式

正则表达式30分钟入门教程

    // 邮政编码
    /\d{6}/
    // 小写英文字母
    /^[a-z]+$/
    // 英文字母
    /^[a-zA-Z]+$/
    // 日期格式
    /^\d{4}-\d{1,2}-\d{1,2}$/
    // 用户名
    /^[a-zA-Z]\w{5, 17}$/

手写字符串 trim

  • 在 String 原型上添加 trim 方法
  • this 指向输入的字符串
  • 使用正则表达式做替换
    String.prototype.trim = function () {
        return this.replace(/^\s+/, '').replace(/\s+$/, '')
    }

获取多个数字中的最大值

  • 把要比较的数转为数组
  • 也可以用 [...arguments] 代替 Array.from(arguments)

示例:

    function max() {
        const nums = Array.from(arguments)
        let max = nums[0]
        nums.forEach(n => {
            if(n > max) {
                max = n
            }
        })
        return max
    }
    const res = max(-1, -2, 2, 4, 8)
    console.log(res) // 8

class 实现继承

  • extends
  • super
  • 扩展或重写方法
    // 父类
    class People {
        constructor(name) {
            this.name = name
        }
        eat() {
            console.log(`${this.name} eat food`);
        }
    }
    // 子类
    class Student extends People {
        constructor(name, number) {
            super(name)
            this.number = number
        }
        sayHi() {
            console.log(`姓名 ${this.name} 学号 ${this.number}`);
        }
    }
    // 子类
    class Teacher extends People {
        constructor(name, major) {
            super(name)
            this.major = major
        }
        teach() {
            console.log(`${this.name} 教授 ${this.major}`)
        }
    }
    // 实例
    const xialuo = new Student('夏洛', '2022')
    console.log(xialuo.name) // 夏洛
    console.log(xialuo.number) // 2022
    xialuo.sayHi() // 姓名 夏洛, 学号 2022
    xialuo.eat() // 夏洛 eat food
    // 实例
    const wanglaoshi = new Teacher('王老师', '语文')
    console.log(wanglaoshi.name) // 王老师
    console.log(wanglaoshi.major) // 语文
    wanglaoshi.teach() // 王老师 教授 语文
    wanglaoshi.eat() // 王老师 eat food

如何捕获 JS 中的异常

  1. try…catch 手动捕获
    try {
        // todo
    } catch (ex) {
        console.error(ex)
    } finally {
        // todo
    }
  1. 自动捕获
    // 自动捕获
    window.onerror = function (message, source, lineNum, colNum, error) {
        // 第一,对于跨域的 js,如 CDN 的,不会有详细的错误报告
        // 第二,对于压缩的 js,还要配合 sourceMap 反查未压缩代码的行、列
    }

什么是 JSON

JSON 是一种数据格式,本质是一段字符串

JSON 格式和 JS 对象结构一致,对 JS 语言更友好

window.JSON 是一个全局对象:JSON.stringify(JS 对象转为 JSON 格式) JSON.parse(JSON 格式转为 JS 对象)

获取当前页面 url 参数

传统方式,查找 location.search

f1be90f7ae1041359828adc0ec54cf9f.png

  • substr(1) 截取掉字符串的第一个字符
  • 'i' 代表不区分大小写
    // 传统方法
    function query(name) {
        const search = location.search.substr(1)
        // search: 'a=10&b=20&c=30'
        const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
        const res = search.match(reg)
        if(res === null) {
            return null
        }
        console.log(res)
        return res[2]
    }
    let res = query('b')
    console.log(res)

09736c56730142d3afa59c8df30560e6.png

  • 新 API,URLSearchParams
    // URLSearchParams
    function query(name) {
        const search = location.search
        const p = new URLSearchParams(search)
        return p.get(name)
    }
    let res = query('b')
    console.log(res) // 20

将 url 参数解析为 JS 对象

传统方式,分析 search:

  • 定义 res 为一个空对象,用来存放对象
  • substr(1) 去掉前面的 ?
  • 两次 split,分别去掉 & 和 =
  • key 充当键,val 充当值
  • 最后返回结果 res
  • 3dec790de8dc432fb1165a680d246895.png
    // 传统方式,分析 search
    function queryToObj() {
        const res = {}
        const search = location.search.substr(1)
        search.split('&').forEach(paramStr => {
            const arr = paramStr.split('=')
            const key = arr[0]
            const val = arr[1]
            res[key] = val
        })
        return res
    } 
    let res = queryToObj()
    console.log(res)

54e4d85d71934b16b3d316b2828b7a94.png

使用 URLSearchParams:

    // 使用 URLSearchParams
    function queryToObj() {
        const res = {}
        const pList = new URLSearchParams(location.search)
        pList.forEach((val, key) => {
            res[key] = val
        })
        return res
    } 
    let res = queryToObj()
    console.log(res)

9434280e2f8441ac9ec4dc0fe5395a89.png

数组去重

  • 传统方式,遍历元素挨个比较、去重
  • indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
  • 如果没有找到匹配的字符串则返回 -1。
    function unique(arr) {
        const res = []
        arr.forEach(item => {
            if(res.indexOf(item) < 0) {
                res.push(item)
            }
        })
        return res
    }
    let res = unique([20, 10, 20, 30, 10])
    console.log(res)

e8f708fd5e6948a98a99c4f84fb9481e.png

  • 使用 Set(计算效率更高)
    function unique(arr) {
        const newArr = new Set(arr)
        return [...newArr]
    }
    let res = unique([20, 10, 20, 30, 10])
    console.log(res)

e8f708fd5e6948a98a99c4f84fb9481e.png

手写深拷贝

  • 深拷贝:一个对象B复制另一个对象A,当改变B对象的值是A对象的值不会改变。
    function deepClone(obj = {}) {
        if (typeof obj !== 'object' || obj == null) {
            //  obj 是 null,或者不是对象和数组,直接返回
            return obj
        }
        // 初始化返回结果
        let result
        if (obj instanceof Array) {
            result = []
        } else {
            result = {}
        }
        for (let key in obj) {
            // 保证 key 不是原型的属性
            if (obj.hasOwnProperty(key)) {
                // 递归
                result[key] = deepClone(obj[key])
            }
        }
        // 返回结果 
        return result
    }
    const obj1 = {
        name: '张三',
        age: {
            realAge: 20
        },
        arr: ['a', 'b', 'c']
    }
    const obj2 = deepClone(obj1)
    obj2.name = '李四'
    obj2.age.realAge = 22
    obj2.arr[0] = 'x'
    console.log(obj1.name) // 张三
    console.log(obj1.age.realAge) // 20
    console.log(obj1.arr[0]) // a

Object.assign()

assign 的属性拷贝是浅拷贝

用于将源对象的所有可枚举属性复制到目标对象中

如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性

如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回

    let sourceObj = {
        a: {
            b: 1
        }
    }
    let targetObj = {
        c: 3
    }
    let res = Object.assign(targetObj, sourceObj)
    console.log(res) // {c: 3, a: {b: 2}}
    targetObj.a.b = 2
    console.log(sourceObj.a.b) // 2

8d3da458976b4d18a707a3c254454f2c.png

了解 RAF requestAnimationFrame

  • 要想动画流畅,更新频率要60帧/s,即 16.67ms 更新一次视图
  • setTimeout 要手动控制频率,而 RAF 浏览器会自动控制
  • 后台标签或隐藏 iframe 中,RAF 会暂停,而 setTimeout 不会停止
    #div1 {
        width: 100px;
        height: 50px;
        background-color: red;
    }
  <div id="div1"></div>
    const div1 = document.getElementById('div1')
    let curWidth = 100
    const maxWidth = 640
    function animate() {
        curWidth = curWidth + 1
        div1.style.width = `${curWidth}px`
        if(curWidth <= maxWidth) {
            window.requestAnimationFrame(animate)
        }
    }
    animate()

https://www.bilibili.com/video/BV1cW4y1h7VG?t=1.0

RAF

优化性能的考虑方面

  • 原则:多使用内存、缓存、减少计算、减少网络请求
  • 方向:加载页面更快,页面渲染更快,页面操作流畅度更好

Map 和 Object 的区别

须知:有序和无序

  • 有序:操作慢
  • 无序:操作快,但无序
  • map、数组是有序的,对象是无序的
  • 结合两者优点:二叉树
  • Map 和 Object 的区别
  • API 不同,Map 可以以任意类型为 key
  • Map 是有序结构
  • Map 操作同样很快

基本 API:

  • 增改:set
  • 删:delete
  • 长度:size

示例:基本 API(set、delete、size)

    const m = new Map([
        ['key1', 'hello'],
        ['key2', 100],
        ['key3', {x: 100}]
    ])
    console.log('原始 m', m)
    m.set('name', '张三')
    console.log('添加张三', m)
    m.set('key1', 'hello world')
    console.log('修改 key1', m)
    m.delete('key2')
    console.log('删除 key2', m)
    console.log('有无 key3', m.has('key3'))
    m.forEach((value, key) => console.log('遍历结果', key, value))
    console.log('m 的长度', m.size)

ac7dda191fbe44de80d3bcb5e2f18471.png

  • Map 可以以任意类型为 key
  • 对象只能以字符串作为 key
    const m = new Map([
        ['key1', 'hello'],
        ['key2', 100],
        ['key3', {x: 100}]
    ])
    // Map 以任意类型为 key
    const o = {name: 'xxx'}
    m.set(o, 'object key')
    function fn() {}
    m.set(fn, 'fn key')
    console.log(m)

269e164266f240ad923bc4e5febd333e.png

object 有多快?

示例:查找第500万个元素的用时(object)

    const obj = {}
    for(let i = 0; i < 1000 * 10000; i++) {
        obj[i + ''] = i
    }
    console.time('obj find')
    obj['5000000']
    console.timeEnd('obj find')
    console.time('obj delete')
    delete obj['5000000']
    console.timeEnd('obj delete')

af7ac6e4528243ce84c6ca56c3185355.pngmap 有多快?

示例:查找第500万个元素的用时(map)

    const m = new Map()
    for(let i = 0; i < 1000 * 10000; i++) {
        m.set(i + '', i)
    }
    console.time('map find')
    m.has('500000')
    console.timeEnd('map find')
    console.time('map delete')
    m.delete('500000')
    console.timeEnd('map delete')

37d4bec6092b4d76b146143ddc3a8d1e.png

可见,都在一个数量级上,相差无几

Set 和 数组的区别

  • API 不同
  • Set 元素不能重复
  • Set 是无序结构,操作很快
  • 示例:set 的 API
  • add 添加
  • delete 删除
  • has 是否有
  • size 长度
    const set = new Set([10, 20, 30])
    console.log('初始值', set)
    set.add(40)
    console.log('添加40', set)
    set.delete(10)
    console.log('删除10', set);
    set.has(30)
    let length = set.size
    console.log('长度', length);
    set.forEach(val => console.log('遍历set', val))

4d65617fd38c492cb4f7c0b1ec425dee.png

示例:数组去重

  • set 元素是不能重复的(数组去重)
    const arr = [10, 20, 20, 30, 10]
    console.log(new Set(arr))

b969a7418ed44c64af3ca4998a2b078d.png

示例:数组有多慢?set 有多快?

  • 新增一条数据
    // arr 有多慢?
    const arr = []
    for(let i = 0; i < 100 * 10000; i++) {
        arr.push(i)
    }
    console.time('arr unshift')
    arr.unshift('a')
    console.timeEnd('arr unshift')
    // set 有多快?
    const set = new Set()
    for(let i = 0; i < 100 * 10000; i++) {
        set.add(i)
    }
    console.time('set add')
    set.add('a')
    console.timeEnd('set add')

a700176e05ac4d2aa4d1ce995a324031.png

  • 查找 500000,这个元素
    // arr 有多慢?
    const arr = []
    for(let i = 0; i < 100 * 10000; i++) {
        arr.push(i)
    }
    console.time('arr find')
    arr.includes(500000)
    console.timeEnd('arr find')
    // set 有多快?
    const set = new Set()
    for(let i = 0; i < 100 * 10000; i++) {
        set.add(i)
    }
    console.time('set find')
    set.has(50000)
    console.timeEnd('set find')

6f1f1f001f234e6fb21ad7dbe694cb21.png

WeakMap 和 WeakSet

  • 弱引用,防止内存泄露
  • WeakMap 只能用对象作为 key,WeakSet 只能用对象作为 value
  • 没有 forEach 和 size,只能用 add delete has
  • WeakMap:
  • WeakMap 弱引用,可防止内存泄露
  • 没有 forEach,size(因为和 key有关,对象不知道什么时候就没了),只能 has delete add

示例:

    const wMap = new WeakMap()
    function fn() {
        const obj = {name: '张三'}
        wMap.set(obj, 'name info') // 只能用对象作为 key
    }
    fn()

b3a8bec6e08e434d8bb47c727af34d56.png

WeakMap 使用场景(避免内存泄露):

    // WeakMap 场景(避免内存泄露)
    const wMap = new WeakMap()
    const userInfo = {name: '张三'}
    const cityInfo = {city: '北京'}
    // 建立一种关联关系,而且两者保持独立,不影响彼此的销毁逻辑
    wMap.set(userInfo, cityInfo) 
    let res = wMap.get(userInfo) 
    console.log(res)
    console.log(userInfo)

c07c171026f44d43972ab6230ba12eef.png

WeakSet


WeakMap 弱引用,可防止内存泄露,只能用对象作为 value

没有 forEach,size(因为和 key有关,对象不知道什么时候就没了),只能 has delete add

reduce 的使用

传统方法求和


封装一个 sum 函数

初始化 sum,通过 forEach 遍历数组

返回 sum

    function sum(arr) {
        let sum = 0
        arr.forEach(item => sum += item)
        return sum
    }
    const arr = [1, 2, 3]
    let res = sum(arr)
    console.log(res) // 6

reduce 传递的参数

  • sum 当前和
  • curVal 当前值
  • index 索引
  • arr 数组
    const arr = [1, 2, 3]
    const res = arr.reduce((sum, curVal, index, arr) => {
        console.log('reduce function ....')
        console.log('sum', sum)
        console.log('curVal', curVal)
        console.log('index', index)
        console.log('arr', arr)
    // 返回值,作为下一次执行时的第一个参数 sum 的值
        return sum + curVal 
    }, 0)
    console.log(res)

7de5e5557c5544739a26c53e7f7cf639.png

reduce 求和

    const arr = [1, 2, 3]
    const res = arr.reduce((sum, curVal) => sum + curVal, 0)
    console.log(res) // 6

reduce 计数

    // 计数
    const arr = [10, 20, 30, 20, 30, 10]
    const n = 10
    const count = arr.reduce((count, val) => {
        return val === n ? count + 1 : count
    }, 0)
    console.log('count', count) // count 2

输出字符串

示例:map 写法

    // 输出字符串
    const arr = [
        {name: '张三', age: '20'},
        {name: '李四', age: '21'},
        {name: '王五', age: '22'},
    ]
    const str = arr.map(item => {
        return `${item.name} - ${item.age}`
    }).join('\n')
    console.log(str)

c60382798a334ae7872c0d136f6ff3b8.png

示例:reduce 写法

    // 输出字符串
    const arr = [
        {name: '张三', age: '20'},
        {name: '李四', age: '21'},
        {name: '王五', age: '22'},
    ]
    const str = arr.reduce((s, item) => {
        return `${s}${item.name} - ${item.age}\n`
    }, '')
    console.log(str)

c60382798a334ae7872c0d136f6ff3b8.png

不积跬步无以至千里 不积小流无以成江海

相关文章
|
20天前
|
前端开发 JavaScript 安全
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第27天】本文介绍了HTTP/2和HTTPS在前端性能调优中的应用。通过多路复用、服务器推送和头部压缩等特性,HTTP/2显著提升了Web性能。同时,HTTPS确保了数据传输的安全性。文章提供了示例代码,展示了如何使用Node.js创建一个HTTP/2服务器。
34 3
|
1月前
|
前端开发 JavaScript 测试技术
前端工程化:构建高效、可维护的现代Web应用
【10月更文挑战第5天】前端工程化:构建高效、可维护的现代Web应用
|
1月前
|
前端开发 测试技术 持续交付
前端工程化:构建高效、可维护的现代Web应用
【10月更文挑战第5天】前端工程化:构建高效、可维护的现代Web应用
|
5天前
|
消息中间件 前端开发 JavaScript
探索微前端架构:构建现代Web应用的新策略
本文探讨了微前端架构的概念、优势及实施策略,旨在解决传统单体应用难以快速迭代和团队协作的问题。微前端允许不同团队独立开发、部署应用的各部分,提升灵活性与可维护性。文中还讨论了技术栈灵活性、独立部署、团队自治等优势,并提出了定义清晰接口、使用Web组件、状态管理和样式隔离等实施策略。
|
8天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
25 2
|
17天前
|
监控 前端开发 JavaScript
探索微前端架构:构建可扩展的现代Web应用
【10月更文挑战第29天】本文探讨了微前端架构的核心概念、优势及实施策略,通过将大型前端应用拆分为多个独立的微应用,提高开发效率、增强可维护性,并支持灵活的技术选型。实际案例包括Spotify和Zalando的成功应用。
|
21天前
|
前端开发 安全 应用服务中间件
前端性能调优:HTTP/2与HTTPS在Web加速中的应用
【10月更文挑战第26天】随着互联网的快速发展,前端性能调优成为开发者的重要任务。本文探讨了HTTP/2与HTTPS在前端性能优化中的应用,介绍了二进制分帧、多路复用和服务器推送等特性,并通过Nginx配置示例展示了如何启用HTTP/2和HTTPS,以提升Web应用的性能和安全性。
20 3
|
21天前
|
前端开发 JavaScript API
前端框架新探索:Svelte在构建高性能Web应用中的优势
【10月更文挑战第26天】近年来,前端技术飞速发展,Svelte凭借独特的编译时优化和简洁的API设计,成为构建高性能Web应用的优选。本文介绍Svelte的特点和优势,包括编译而非虚拟DOM、组件化开发、状态管理及响应式更新机制,并通过示例代码展示其使用方法。
36 2
下一篇
无影云桌面