相关推荐recommended
前端高频面试题 Day02
作者:mmseoamin日期:2023-11-30

面试题

var 和 let const 的区别

  • var 是 ES5 及之前的语法,let const 是 ES6 语法
  • var 和 let 是变量,可修改;const 是常量,不可修改
  • var 有变量提升,let const 没有
  • var 没有块级作用域,let const 有 (ES6 语法有块级作用域)
    // var 变量提升
    console.log('a', a)
    var a = 100
    // let 没有变量提升
    console.log('b', b)
    let b = 200
    
    // var 没有块级作用域
    for (var i = 0; i < 10; i++) {
        var j = 1 + i
    }
    console.log(i, j)
    // let 有块级作用域
    for (let x = 0; x < 10; x++) {
        let y = 1 + x
    }
    console.log(x, y)
    

    typeof 有哪些返回类型?

    // 判断所有值类型
    let a
    console.log(a) // 'undefined'
    const str = 'abc'
    typeof str  // 'string'
    const n = 100
    typeof n // 'number'
    const b = true
    typeof b // 'boolean'
    const s = Symbol('s')
    typeof s // 'symbol'
    

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

    • 强制 parseInt parseFloat
    • 隐式 if ,== , + 拼接字符串

      手写深度比较,如 lodash isEqual

      // 实现如下效果
      const obj1 = {a: 10, b: { x: 100, y: 200 }}
      const obj2 = {a: 10, b: { x: 100, y: 200 }}
      isEqual(obj1, obj2) === true
      
      // 判断是否是 object
      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. 先取出 obje2 obj2 的 keys,比较个数
          const obj1Keys = Object.keys(obj1)
          const obj2Keys = Object.keys(obj2)
          if (obj1Keys.length !== obj2Keys.length) {
              // keys 个数不相等,则不是全等
              return false
          }
          // 2. 以 obj1 为基准,和 obj2 依次递归比较
          for (let key in obj1) {
              // 递归比较
              const res = isEqual(obj1[key], obj2[key])
              if (!res) {
                  // 遇到一个不相等的,则直接返回 false
                  return false
              }
          }
          // 3. 都相等,则返回 true
          return true
      }
      

      split() 和 join() 的区别

      '1-2-3'.split('-')
      [1,2,3].join('-')
      

      数组的 pop push unshift shift 分别做什么

      注意以下几点

      前端高频面试题 Day02,前端高频面试题 Day02,第1张

      • 函数作用是什么?
      • 返回值是什么?
      • 对原数组是否造成影响?
      • 如何对原数组不造成影响?concat slice map filter

        【扩展】数组 API 的纯函数和非纯函数

        纯函数 —— 1. 不改变来源的数组; 2. 返回一个数组

        • concat
        • map
        • filter
        • slice
          const arr = [100, 200, 300]
          const arr1 = arr.concat([400, 500])
          const arr2 = arr.map(num => num * 10)
          const arr3 = arr.filter(num => num > 100)
          const arr4 = arr.slice(-1)
          

          非纯函数

          情况1,改变了原数组

          • push
          • reverse
          • sort
          • splice

            情况2,未返回数组

            • push
            • forEach
            • reduce
            • some

              数组 slice 和 splice 的区别?

              slice - 切片;splice - 剪接;

              // slice()
              const arr1 = [10, 20, 30, 40, 50]
              const arr2 = arr1.slice() // arr2 和 arr1 不是一个地址,纯函数,重要!!!
              // arr.slice(start, end)
              const arr1 = [10, 20, 30, 40, 50]
              const arr2 = arr1.slice(1, 4) // [20, 30, 40]
              // arr.slice(start)
              const arr1 = [10, 20, 30, 40, 50]
              const arr2 = arr1.slice(2) // [30, 40, 50]
              // 负值
              const arr1 = [10, 20, 30, 40, 50]
              const arr2 = arr1.slice(-2) // [40, 50]
              
              // arr.splice(index, howmany, item1, ....., itemX)
              const arr1 = [10, 20, 30, 40, 50]
              const arr2 = arr1.splice(1, 2, 'a', 'b', 'c') // [20, 30]
              // arr1 会被修改,不是纯函数,即有副作用
              

              [10, 20, 30].map(parseInt) 的结果是什么?

              // 拆解开就是
              [10, 20, 30].map((num, index) => {
                  return parseInt(num, index)
                  // parseInt 第二个参数是进制
                  // 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。
                  // 如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN
              })
              
              // 可以对比
              [10, 20, 30].map((num, index) => {
                  return parseInt(num, 10)
              })
              

              ajax 请求中 get 和 post 的区别

              • get 一般用于查询操作,post 一般用于提交操作
              • get 参数在 url 上,post 在请求体内
              • 安全性:post 请求易于防止 CSRF

                (post 代码演示:网页,postname)

                call 和 apply 的区别

                • fn.call(this, p1, p2, p3)
                • fn.apply(this, arguments)

                  事件委托(代理)是什么

                  const p1 = document.getElementById('p1')
                  const body = document.body
                  bindEvent(p1, 'click', e => {
                      e.stopPropagation() // 注释掉这一行,来体会事件冒泡
                      alert('激活')
                  })
                  bindEvent(body, 'click', e => {
                      alert('取消')
                  })
                  

                  闭包是什么,有什么特性,对页面有什么影响

                  • 回归作用域和自由变量
                  • 闭包的应用场景:函数作为参数被传入,函数作为返回值被返回
                  • 关键点:自由变量的查找,要在函数定义的地方,而不是执行的地方

                    对页面的影响

                    • 变量内存得不到释放,可能会造成内存积累(不一定是泄露)
                      // 自由变量示例 —— 内存会被释放
                      let a = 0
                      function fn1() {
                          let a1 = 100
                          function fn2() {
                              let a2 = 200
                              function fn3() {
                                  let a3 = 300
                                  return a + a1 + a2 + a3
                              }
                              fn3()
                          }
                          fn2()
                      }
                      fn1()
                      
                      // 闭包 函数作为返回值 —— 内存不会被释放
                      function create() {
                          let a = 100
                          return function () {
                              console.log(a)
                          }
                      }
                      let fn = create()
                      let a = 200
                      fn() // 100
                      // 闭包 函数作为参数 —— 内存不会被释放
                      function print(fn) {
                          let a = 200
                          fn()
                      }
                      let a = 100
                      function fn() {
                          console.log(a)
                      }
                      print(fn) // 100
                      

                      如何阻止事件冒泡和默认行为

                      event.stopPropagation()

                      event.preventDefault()

                      前端高频面试题 Day02,前端高频面试题 Day02,第2张

                      解释 jsonp 的原理,以及为什么不是真正的 ajax

                      • 浏览器的同源策略,什么是跨域?
                      • 哪些 html 标签能绕过跨域?
                      • jsonp 原理

                        document load 和 document ready 的区别

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

                        == 和 === 的不同

                        • == 会尝试进行类型转换
                        • === 严格相等

                          函数声明与函数表达式的区别?

                          const res = sum(10, 20)
                          console.log(res) // 30
                          // 函数声明
                          function sum(x, y) {
                              return x + y
                          }
                          
                          const res = sum(100, 200)
                          console.log(res) // 报错!!!
                          // 函数表达式
                          const sum = function(x, y) {
                              return x + y
                          }
                          

                          new Object() 和 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
                              }
                          })
                          const obj3 = Object.create({
                              a: 10,
                              b: 20,
                              sum() {
                                  return this.a + this.b
                              }
                          })
                          // 分别打印看结构
                          

                          示例二

                          const obj1 = {
                              a: 10,
                              b: 20,
                              sum() {
                                  return this.a + this.b
                              }
                          }
                          const obj2 = new Object(obj1)
                          console.log(obj1 === obj2) // true
                          const obj3 = Object.create(obj1)
                          console.log(obj1 === obj3) // false
                          const obj4 = Object.create(obj1)
                          console.log(obj3 === obj4) // false
                          // 然后修改 obj1 ,看 obj2 obj3 和 obj4
                          obj1.printA = function () {
                              console.log(this.a)
                          }
                          

                          对作用域上下文和 this 的理解,场景题

                          const User = {
                              count: 1,
                              getCount: function() {
                                  return this.count
                              }
                          }
                          console.log(User.getCount()) // what?
                          const func = User.getCount
                          console.log( func() ) // what?
                          

                          对作用域和自由变量的理解,场景题

                          let i
                          for(i = 1; i <= 3; i++) {
                            setTimeout(function(){
                                console.log(i)
                            }, 0)
                          }
                          // what?
                          

                          判断字符串以字母开头,后面可以是数字,下划线,字母,长度为 6-30

                          const reg = /^[a-zA-Z]\w{5,29}$/

                          • 查看正则表达式规则 https://www.runoob.com/regexp/regexp-syntax.html
                          • 查看常见正则表达式
                            /\d{6}/ // 邮政编码
                            /^[a-z]+$/ // 小写英文字母
                            /^[A-Za-z]+$/ // 英文字母
                            /^\d{4}-\d{1,2}-\d{1,2}$/ // 日期格式
                            /^[a-zA-Z]\w{5,17}$/ // 用户名(字母开头,字母数字下划线,5-17位)
                            /\d+\.\d+\.\d+\.\d+/ // 简单的 IP 地址格式
                            

                            以下代码,分别 alert 出什么?

                            let a = 100
                            function test() {  
                                alert(a)
                                a = 10
                                alert(a)
                            }
                            test()
                            alert(a)
                            // what?
                            

                            手写 trim 函数,保证浏览器兼容性

                            String.prototype.trim= function (){
                                return this.replace(/^\s+/,"").replace(/\s+$/,"")
                            }
                            

                            知识点:原型,this,正则

                            如何获取多个数值中的最大值?

                            Math.max(10, 30, 20, 40)
                            // 以及 Math.min
                            
                            function max() {
                                const nums = Array.prototype.slice.call(arguments) // 变为数组
                                let max = 0
                                nums.forEach(n => {
                                    if (n > max) {
                                        max = n
                                    }
                                })
                                return max
                            }
                            

                            如何用 JS 实现继承?

                            class 代码

                            程序中捕获异常的方法

                            try {
                                // todo
                            } catch (ex) {
                                console.error(ex) // 手动捕获 catch
                            } finally {
                                // todo
                            }
                            
                            // 自动捕获 catch(但对跨域的 js 如 CDN 的,不会有详细的报错信息)
                            window.onerror = function (message, source, lineNom, colNom, error) {
                            }
                            

                            什么是 JSON ?

                            首先,json 是一种数据格式标准,本质是一段字符串,独立于任何语言和平台。注意,json 中的字符串都必须用双引号。

                            {
                                "name": "张三",
                                "info": {
                                    "single": true,
                                    "age": 30,
                                    "city": "北京"
                                },
                                "like": ["篮球", "音乐"]
                            }
                            

                            其次,JSON 是 js 中一个内置的全局变量,有 JSON.parse 和 JSON.stringify 两个 API 。

                            获取当前页面 url 参数

                            自己实现

                            // const url = 'https://www.xxx.com/path/index.html?a=100&b=200&c=300#anchor'
                            function query(name) {
                                const search = location.search.substr(1) // 去掉前面的 `?`
                                const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i')
                                const res = search.match(reg)
                                if (res === null) {
                                    return null
                                }
                                return decodeURIComponent(res[2])
                            }
                            console.log( query('a') )
                            console.log( query('c') )
                            

                            新的 API URLSearchParams

                            const pList = new URLSearchParams(location.search)
                            pList.get('a')
                            

                            将 url 参数解析为 JS 对象?

                            自己编写

                            前端高频面试题 Day02,前端高频面试题 Day02,第3张

                            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
                            }
                            

                            新的 API URLSearchParams

                            function queryToObj() {
                                const res = {}
                                const pList = new URLSearchParams(location.search)
                                pList.forEach((val, key) => {
                                    res[key] = val
                                })
                                return res
                            }
                            

                            实现数组 flatern ,考虑多层级

                            function flat(arr) {
                                // 验证 arr 中,还有没有深层数组,如 [1, [2, 3], 4]
                                const isDeep = arr.some(item => item instanceof Array)
                                if (!isDeep) {
                                    return arr // 没有深层的,则返回
                                }
                                // 多深层的,则 concat 拼接
                                const res = Array.prototype.concat.apply([], arr) // 回归上文,apply 和 call 的区别
                                return flat(res) // 递归调用,考虑多层
                            }
                            flat([[1,2], 3, [4,5, [6,7, [8, 9, [10, 11]]]]])
                            

                            数组去重

                            要考虑:

                            • 顺序是否一致?
                            • 时间复杂度

                              ES5 语法手写。

                              // 写法一
                              function unique(arr) {
                                  const obj = {}
                                  arr.forEach(item => {
                                      obj[item] = 1 // 用 Object ,去重计算高效,但顺序不能保证。以及,非字符串会被转换为字符串!!!
                                  })
                                  return Object.keys(obj)
                              }
                              unique([30, 10, 20, 30, 40, 10])
                              
                              // 写法二
                              function unique(arr) {
                                  const res = []
                                  arr.forEach(item => {
                                      if (res.indexOf(item) < 0) { // 用数组,每次都得判断是否重复(低效),但能保证顺序
                                          res.push(item)
                                      }
                                  })
                                  return res
                              }
                              unique([30, 10, 20, 30, 40, 10])
                              

                              用 ES6 Set

                              // 数组去重
                              function unique(arr) {
                                  const set = new Set(arr)
                                  return [...set]
                              }
                              unique([30, 10, 20, 30, 40, 10])
                              

                              手写深拷贝

                              粘贴代码

                              【注意】Object.assign 不是深拷贝,可以顺带讲一下用法

                              • Object.assign(obj1, {...})
                              • const obj2 = Object.assign({}, obj1, {...})

                                介绍一下 RAF requestAnimationFrame

                                想用 JS 去实现动画,老旧的方式是用 setTimeout 实时刷新,但这样效率非常低,而且可能会出现卡顿。

                                • 想要动画流畅,更新频率是 60帧/s ,即每 16.6ms 更新一次试图。太慢了,肉眼会感觉卡顿,太快了肉眼也感觉不到,资源浪费。
                                • 用 setTimeout 需要自己控制这个频率,而 requestAnimationFrame 不用自己控制,浏览器会自动控制
                                • 在后台标签或者隐藏的