前端基础高频面试题
var和let、const的区别
- var是es5语法,let const是es6语法,var存在变量提升
- var和let是变量,可修改;const是常量,不可修改
- let const有块级作用域,var没有
let i
for (i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i) // 4 4 4
}, 0)
}
typeof返回哪些类型
- undefined string number boolean symbol
- object(注意,typeof null === 'object')
- function
列举强制类型转换和隐式类型转换
- 强制转换:parseInt parseFloat toString等
- 隐式转换:if、逻辑运算、==、+拼接字符串
手写深度比较,模拟lodash isEqual
// 判断是否是对象或者数组
function isObject(obj) {
return typeof obj === "object" && obj !== null
}
function isEqual(obj1, obj2) {
// 值类型
if (!isObject(obj1) || !isObject(obj2)) {
return obj1 === obj2
}
// 地址相同的对象
if (obj1 === obj2) {
return true
}
// 取出key
const obj1Key = Object.keys(obj1)
const obj2Key = Object.keys(obj2)
if (obj1Key.length !== obj2Key.length) {
return false
}
// 以obj1为基准 和obj2递归比较
for (const key in obj1) {
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
return true
}
pop、push、shift、unshift、splice和concat、map、filter、slice
注意纯函数和非纯函数的区别
const arr = [10, 20, 30, 40]
// ------------------非纯函数-------------------------------------------------------
// 删去最后一个元素返回,修改原数组
// const popRes = arr.pop()
// console.log(popRes, arr) // 40 [10, 20, 30]
// 删去开头一个元素返回,修改原数组
// const shiftRes = arr.shift()
// console.log(shiftRes, arr)
// 向数组最后push添加 返回数组长度 修改原数组
// const pushRes = arr.push(50)
// console.log(pushRes, arr)
// 向数组开头添加一个元素 返回数组长度 修改原数组
// const unshiftRes = arr.unshift(4)
// console.log(unshiftRes, arr)
// 数组切片 start开始位置 count切割数量 后面接的是替换的值 返回值为切割出来的值
// const spliceRes = arr.splice(1, 2, 'a')
// console.log(spliceRes, arr)
// ------------------纯函数-------------------------------------------------------
// 1. 不改变原数组 2.返回一个数组
// 用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
// const arr1 = arr.concat(50)
// console.log(arr1, arr)
// 返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
// const arr2 = arr.map(num => num * 2)
// console.log(arr2, arr)
// 返回一个新数组,数组元素为原始数组过滤处理的值
// const arr3 = arr.filter(num => num > 25)
// console.log(arr3, arr)
// 返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
// const arr4 = arr.slice(1, 4)
// console.log(arr4, arr)
split()和join()的区别
split分割 join连接
let testStr = 'air-hua-byte'
const str1 = testStr.split('-')
console.log(str1) // ['air', 'hua', 'byte']
const str2 = str1.join('-')
console.log(str2) // air-hua-byte
[10, 20, 30].map(parseInt)返回结果是什么
返回结果为:[10, NaN, NaN]
// 拆解式子 parseInt第二个参数为2-36之间的整数,表示被解析字符串的基数。
[10, 20, 30].map((num, index) => {
return parseInt(num, index)
})
ajax请求get和post的区别
- GET在浏览器回退时是无害的,而POST会再次提交请求
- GET产生的URL地址可以被Bookmark(书签),而POST不可以
- GET请求会被浏览器主动cache,而POST不会,除非手动设置
- GET请求只能进行url编码,而POST不会,除非手动设置
- GET请求参数会被完整保留在浏览器历史记录,而POST参数不会被保留
- GET请求在URL传输参数是有长度限制的,而POST没有
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
- GET参数通过URL传递,POST放在Request body中
函数call和apply的区别
第二个参数中call
是参数列表(不是单一数组),而apply
是函数参数所组成的数组
事件代理(委托)是什么
“事件代理”即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
更多内容参考事件冒泡、事件捕获和事件委托。
闭包是什么,有何特性、有何影响
闭包:
-
闭包可以使用在它作用域外面定义的变量
-
闭包可以存在定义该变量的函数作用域中
-
产生场景:函数作为返回值,函数作为参数被传递
优点:
- 避免全局变量的污染
- 相当于产生一个私有成员,其他用不了这个变量
缺点:
- 常驻内存,增加内存使用量
- 使用不当会容易造成内存泄漏
更多内容参考:作用域和闭包
如何阻止事件冒泡和默认行为
event.stopProppagation()
event.preventDefault()
如何减少DOM操作
- 缓存DOM查询结果(循环查询)
- 多次DOM操作,合并一次(循环插入)
解释jsonp的原理,为何不是真正的ajax
jsonp的原理
:浏览器可以动态插入一段js代码并执行
为什么不是真正的ajax:
-
ajax核心
:通过XMLHttpRequest
获取非本页内容 -
jsonp的核心
:加载script标签调用服务器js脚本 -
浏览器的同源策略和跨域(script和img可以越过跨域)
document load和ready的区别
load需要全部资源加载完才会执行,包括图片、视频等
ready渲染完即可执行,此时图片、视频可能还没有加载完
函数声明和函数表达式的区别
函数声明 function fn() {...}
会进行预处理加载(类似var变量提升)
函数表达式 const fn = function(){...}
需要先定义在使用
new Object() 和Object.create()区别
const obj1 = {
name: 'airhua',
score: {
math: 95,
english: 90
},
sum() {
return this.score.math + this.score.english
}
}
const obj2 = new Object(obj1)
console.log(obj2 === obj1)
const obj3 = Object.create(obj1)
console.log(obj1 === obj3)
从上面例子总结以下:
- new Object()等同于{},原型为Object.prototype
- Object.create({...})等于指定原型
this场景题
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3
手写字符串trim方法,保证浏览器兼容性
if (!String.prototype.trim) {
String.prototype.trim = function () {
// /^\s+/ :匹配以空白格开头的 /\s+$/ :匹配以空白格结尾
return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
}
如何获取多个数字中的最大值
// 手写方法
const max = (...argumnets) => {
let maxNum = 0
const arr = Array.prototype.slice.call(argumnets)
arr.forEach(num => {
if (num > maxNum) maxNum = num
});
return maxNum
}
console.log(max(10, 60, 80, 4, 6, 500))
// Math.max
console.log(Math.max(10, 60, 80, 4, 6, 500))
如何捕获js程序的异常
try...catch
- 针对代码写比较好
window.onerror(message, source, lineNum, colNum, error)
- 全局报错信息、
- 但是对于跨域的js和CDN引入不会见擦汗
- 压缩js需要配合sourceMap反查出错行和列
将url参数解析成js对象
普通方法
function queryToObj() {
const res = {}
// 去掉 ?
const search = location.search.substr(1)
search.split('&').forEach(paramStr => {
const arr = paramStr.split('=')
const key = arr[0]
const value = arr[1]
res[key] = value
})
return res
}
使用URLSearchParams
function queryToObj() {
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((value, key) => {
res[key] = value
})
return res
}
手写数组flatern
写完一个后查了一下,惊人发现这个很多实现方式,具体就没一个一个去看了,这里放一个链接,有空还可以补一补:面试官连环追问:数组拍平(扁平化) flat 方法实现
// 1
function flat(arr) {
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr
}
const res = Array.prototype.concat.apply([], arr)
return flat(res)
}
// 2
function flat(arr) {
return [].concat(...arr.map(item => {
return Array.isArray(item) ? flat(item) : item
}))
}
const res = flat([1, 2, [54, 85, [8, 9]]])
console.log(res)
数组去重
// 普通方式
function unique(arr) {
let res = []
arr.forEach(item => {
if (res.indexOf(item) < 0) {
res.push(item)
}
});
return res
}
// ES6
function unique(arr) {
let res = new Set(arr)
return [...res]
}
requestAnimationFrame
属于异步宏任务,执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。下面可以来看看setTimeout实现动画和requestAnimationFrame的区别,setTimeout可能出现掉帧情况。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
#app {
background-color: red;
height: 100px;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script>
const app = document.getElementById('app')
let curWidth = 100
const maxWidth = 640
// function animate() {
// curWidth = curWidth + 3
// app.style.width = curWidth + 'px'
// if (curWidth < maxWidth) {
// setTimeout(animate, 16.7)
// }
// }
function animate() {
curWidth = curWidth + 3
app.style.width = curWidth + 'px'
if (curWidth < maxWidth) {
window.requestAnimationFrame(animate)
}
}
animate()
</script>
</html>