var 和 let const 的区别
- var是es5语法,let const是es6语法;var有变量提升
- var let是变量,可修改;const是常量,不可修改
- let const有块级作用域,var没有
typeof 返回哪些类型
- number string boolean symbol undefined
- object
- function
列举强制类型转换和隐式类型转换
- 强制:parseInt、parseFloat、toString等
- 隐式: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 分别做什么
- pop 删除并返回数组最后一个元素
- shift 删除并返回数组第一个元素
- push 数组末尾添加n个元素,返回length
- unshift 数组开头添加n个元素,返回length
数组的API有哪些是纯函数
- 不改变原数组(没有副作用)
- 返回一个数组
- 纯函数:concat、map、filter、slice
- 非纯函数: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 的区别
- GET一般用于查询操作,POST一般用于提交操作
- GET参数拼接在url上,POST放在请求体内
- 安全性: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