JavaScript面试真题

513 阅读4分钟

var 和 let const 的区别

  1. var是es5语法,let const是es6语法;var有变量提升
  2. var let是变量,可修改;const是常量,不可修改
  3. let const有块级作用域,var没有

typeof 返回哪些类型

  1. number string boolean symbol undefined
  2. object
  3. function

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

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

手写深度比较,模拟lodash isEqual

const isObject = function(obj) {
    return typeof obj === 'object' && obj != null
}

const isEqual = function(obj1, obj2) {
    if(!isObject(obj1) || !isObject(obj2)) {
        return obj1 === obj2
    }

    if(obj1 === obj2) {
        return true
    }

    const obj1Keys = Object.keys(obj1)
    const obj2Keys = Object.keys(obj2)
    if(obj1Keys.length !== obj2Keys.length) {
        return false
    }

    for(let key in obj1) {
        const result = isEqual(obj1[key], obj2[key])
        if(!result) {
            return false
        }
    }
    
    return true
}

split() 和 join() 的区别

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

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

  1. pop 删除并返回数组最后一个元素
  2. shift 删除并返回数组第一个元素
  3. push 数组末尾添加n个元素,返回length
  4. unshift 数组开头添加n个元素,返回length

数组的API有哪些是纯函数

  1. 不改变原数组(没有副作用)
  2. 返回一个数组
  3. 纯函数:concat、map、filter、slice
  4. 非纯函数:pop、shift、push、unshift|forEach|some、every|reduce

数组 slice 和 splice 的区别

slice纯函数

const arr = [10, 20, 30, 40, 50]
const arr1 = arr.splice()
const arr2 = arr.splice(1, 3)
const arr3 = arr.splice(2)
const arr4 = arr.splice(-2)

splice非纯函数

const arr = [10, 20, 30, 40, 50]
const arr1 = arr.splice(1, 3, 'a', 'b')
const arr2 = arr.splice(1, 2)
const arr3 = arr.splice(1, 0, 'a', 'b')

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

[10, 20, 30].map(parseInt)

[10, 20, 30].map((item, index) => {
    return parseInt(item, index)
})

ajax 请求 GET 和 POST 的区别

  1. GET一般用于查询操作,POST一般用于提交操作
  2. GET参数拼接在url上,POST放在请求体内
  3. 安全性:POST易于防止CSRF攻击

函数call 和 apply 的区别

fn.call(this, a, b, c)
fn.apply(this, arguments)

事件代理(委托)是什么

const box = document.getElementById('box')
const body = document.body

bindEvent(box, 'click', (event) => {
    event.stopPropagation() // 注释这行,体会事件冒泡
    alert('激活')
})

bindEvent(body, 'click', (event) => {
    alert('取消')
})

查找、删除、添加、移动DOM节点的方法

如何减少DOM操作

  • 缓存 DOM 查询结果
  • 多次 DOM 操作,合并到一次插入

解释 jsonp 原理,为何它不是真正的 ajax

<script>
    window.fn = function(data) {
        return data
    }
</script>

<script src="http://localhost:9000/jsonp.js?username=leslie&callback=fn"></script>

document load 和 ready 的区别

window.addEventListener('load', function() {
    // 页面的全部资源(包括图片、视频等)加载完才会执行
})

document.addEventListener('DOMContentLoaded', function() {
    // DOM 渲染完即可执行,此时图片、视频可能还未加载完
})

== 和 === 的区别

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

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

  • 函数声明 function fn() {...}
  • 函数表达式 const fn = function() {...}
  • 函数声明会在代码执行前预加载,函数表达式不会

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

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

关于 this 的场景题

const user = {
    count: 1,
    getCount: function() {
        return this.count
    }
}

console.log(user.getCount()) 
const fn = user.getCount
console.log(fn())

关于作用域和自由变量的场景题

// 作用域
let i
for(i = 0; i <= 3; i++) {
    setTimeout(() => {
        console.log(i)
    })
}

// 自由变量
let a = 100
function fn() {
    alert(a)
    a = 10
    alert(a)
}
fn()
alert(a)

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

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

手写字符串 trim 方法,保证浏览器兼容性

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

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

function max() {
    const nums = Array.prototype.slice.call(arguments)
    let max = 0
    nums.forEach(val => {
        if(val > max) {
            max = val
        }
    })
    return max
}

如何实现JS继承

  • class 继承
  • prototype 继承

如何捕获JS程序中的异常

try {
    // todo
}catch(error) {
    console.error(error)
}finally {
    // todo
}

window.error = function(message, source, lineNum, colNum, error) {
    // 1、对于跨域的 js(如CDN),不会有详细的报错信息
    // 2、对于压缩的 js,还要配合sourceMap反查到未压缩代码的行、列
}

什么是JSON

  • json是一种数据格式,本质是一段字符串
  • json格式和 js 对象结构一致,对 js 语言更友好
  • window.JSON是一个全局对象:JSON.stringify、JSON.parse

获取当前页面 url 参数

  • 传统方式:location.search
  • 新API:URLSearchParams

将 url 参数解析为 JS 对象

const queryToObject = function() {
    const obj = {}
    const params = location.search.substr(1)
    params.split('&').forEach(item => {
        const arr = item.split('=')
        const key = arr[0]
        const val = arr[1]
        obj[key] = val
    })
    return obj
}

手写数组 flatern,考虑多层级

const flat = function(arr) {
    const isDeep = arr.some(item => item instanceof Array)
    if(!isDeep) {        
        return arr
    }

    const result = Array.prototype.concat.apply([], arr)
    
    return flat(result)
}

数组去重

const unique = function(arr) {
    const uniqueArr = []
    for(let val of arr) {
        if(uniqueArr.indexOf(val) < 0) {
            uniqueArr.push(val)
        }
    }
    return uniqueArr
}

const unique = function(arr) {
    const newArr = new Set(arr)
    return [...newArr] 
}

介绍一下 RAF requestAnimationFrame

  • 要想动画流畅,更新频率要 60帧/秒,即16.67ms更新一次试图
  • setTimeout要手动控制频率,而 RAF 浏览器会自动控制
  • 后台标签或隐藏 iframe 中,RAF会暂停,而setInterval 依然执行

前端性能如何优化?一般从哪几个方面考虑?

原则:多使用内存、缓存、减少CPU计算、减少网络请求

方向:加载页面、页面渲染、页面操作流畅度

- 让加载更快

  • 减少资源体积:压缩代码
  • 减少访问次数:合并代码,SSR 服务端渲染,缓存
  • 使用更快的网络:CDN

- 让渲染更快

  • CSS 放在 head,JS 放在 body 最下面
  • 尽早开始执行 JS,用DOMContentLoaded 触发
  • 懒加载(图片懒加载)
  • 对 DOM 查询进行缓存
  • 频繁 DOM 操作,合并到一起插入 DOM 结构
  • 节流 debounce、防抖 throttle