前端基础面试题
说说 JS 的数据类型都有哪些
基本类型
- String
- Number
- Boolean
- null
- undefined
- symbol
引用类型
- object
说说 Http 状态码
1** 信息,服务器收到请求,需要请求者继续执行操作(101,升级为 websocket 协议)
2** 成功,操作被成功接收并处理(206,部分内容,分段传输)
3** 重定向,需要进一步操作以完成请求(301、302 重定向;304 命中缓存)
4** 客户端错误,请求包含语法错误或无法完成请求(401,要求身份验证;403,服务器理解客服端需求,但是禁止访问)
5** 服务器错误,服务器在处理请求的过程中发生了错误
说说 ajax 状态码 ajax 一定是异步的吗?
ajax 不一定是异步的,可以通过 open 方法的第三个参数来配置(默认为 true,异步)
状态码:
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了
说说 ajax 是什么?优势?劣势?应该注意的问题?
ajax 是一种和后台通信的标准。全称是 Asynchronous Javascript And XML(异步 javascript 和 XML)。
优势:
- 无需刷新页面请求数据,可以使产品更快、更小、更友好
- 可以把以前服务端的任务转嫁到客户端来处理,减轻服务器负担,节省带宽
- 浏览器支持好,无需插件
劣势:
- 不支持浏览器的回退按钮
- 安全性存在问题,能够在用户不知情的情况下发送请求
- 暴露了 http 交互细节
- 对搜索引擎(网络爬虫)的支持比较弱
- 程序不容易调试
注意的问题:
- 浏览器兼容性问题,这个问题 jQuery 等库已经帮我们封装好了
- 跨域问题,不同域之间不允许通过ajax进行访问,可以参考阮一峰老师的 跨域资源共享 CORS 详解
- 为了更快的速度和对搜索引擎友好,首页尽量不要用 ajax 而是服务端渲染(当然这看分场景)
- ajax适合增删改查操作
你把下面的表达式的打印结果写出来
1.toString() //Uncaught SyntaxError: Invalid or unexpected token
true.toString() //"true"
[].toString() //""
{}.toString() //Uncaught SyntaxError: Unexpected token .
null.toString() //Uncaught TypeError: Cannot read property 'toString' of null
undefined.toString() //Uncaught TypeError: Cannot read property 'toString' of undefined
NaN.toString() //"NaN"
这些需要刻意背一下,其中1和 {}
是语法错误。null 和 undefined 是因为没有 toString
方法,可以使用 call
来借用
1..toString() //"1"
(1).toString() //"1"
Number(1).toString() //"1"
({}).toString() //[object Object]
Object.prototype.toString.call(null) //[object Null]
Object.prototype.toString.call(undefined) //[object Undefined]
前端性能优化你了解哪些
内容层面
- 使用 CDN
- 单域名、多域名,单域名可以减少DNS查找次数,多域名可以增加浏览器并行下载数量,这需要权衡,一般同一个域下不要超过四个资源。
- 避免重定向(分场景)
- 避免 404
网络层面
- 利用缓存,可以参考另一篇文章 手写文件服务器,说说前后端交互
- 文件压缩(通过响应头 Accept-Encoding: gzip, deflate, br 告诉服务器你支持的压缩类型)
- 按需加载,提取公共代码,tree-shaking 等(都可以通过 webpack 来实现)
- 减少 cookie 大小
- 文件合并,通过 css 雪碧图合并图片
- 文件预加载、图片懒加载
渲染层间
- js 放底部,css 放顶部
- 减少 reflow 回流 和 repaint 重绘
- 减少 dom 节点
代码层面
- 缓存 dom 节点,减少节点查找,css 选择器层级优化
- 减少 dom 节点操作
- 合理使用 break、continue、return 等,优化循环
- 像 react 用到的事件委托、对象池等手段
说说浏览器的reflow和repaint
浏览器解析过程
- 解析html生成dom树
- 解析css
- 把css应用于dom树,生成render树(这里记录这每一个节点和它的样式和所在的位置)
- 把render树渲染到页面
reflow 回流
reflow 翻译为回流,指的是页面再次构建render树。每个页面至少发生一次回流,就是第一次加载页面的时候
此外,当页面中有任何改变可能造成文档结构发生改变(即元素间的相对或绝对位置改变),都会发生reflow,常见的有:
- 添加或删除元素(
opacity:0
除外,它不是删除) - 改变某个元素的尺寸或位置
- 浏览器窗口改变(resize事件触发)
repaint 重绘
repaint 翻译为重绘,它可以类比为上面的第四步,根据 render 树绘制页面,它的性能损耗比回流要小。每次回流一定会发生重绘。此外,以下操作(不影响文档结构的操作,影响结构的会发生回流)也会发生重绘:
- 元素的颜色、透明度改变
- text-align 等
浏览器优化
我们不太容易精确知道哪些操作具体会造成哪些元素回流,不同的浏览器都有不同的实现。但是确定是他们的的耗时是比较长的,因为涉及到大量的计算。
浏览器为了提升性能也对这个问题进行了优化。方案就是维护一个队列,把所有需要回流和重绘的操作都缓存起来,一段时间之后再统一执行。但是,有的时候我们需要获取一些位置属性,当我们一旦调用这些 api 的时候,浏览器不得不立即计算队列以保证提供的数据是准确的。例如以下操作:
- offsetTop, offsetLeft, offsetWidth, offsetHeight
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
- width,height
- getComputedStyle或者IE的currentStyle
注意问题
- 批量处理
- 使用 DocumentFragment 进行缓存,这样只引发一次回流
- 把频繁操作的元素先
display: null
,只引发两次回流 - cloneNode 和 replaceChild,只引发两次回流
- 不要频繁更改 style,而是更改 class
- 避免频繁调用 offsetTop 等属性,在循环前把它缓存起来
- 绝对定位具有复杂动画的元素,否则会引起父元素和后续大量元素的频繁回流
如何去除字符串首位空格?
//es6
' ab '.trim() //"ab"
//正则
' ab '.replace(/^\s*|\s*$/g,'') //"ab"
如何获取url中的查询字符串
function queryUrlParameter(str) {
let obj = {}
let reg = /([^?=&#]+)=([^?=&#]+)/g;
str.replace(reg, function () {
obj[arguments[1]] = arguments[2]
})
//如果加上hash
// reg = /#([^?&=#]+)/g
// if (reg.test(str)) {
// str.replace(reg, function () {
// obj.hash = arguments[1]
// })
// }
return obj
}
console.log(queryUrlParameter('http://www.baidu.com?a=1&b=2#12222')) //{ a: '1', b: '2'}
如何实现一个深拷贝、深比较
深拷贝
function clone(obj) {
if (obj == null || typeof obj !== 'object') return obj
let newObj = null
// 时间对象有特殊性
if (obj.constructor === Date) {
newObj = new obj.constructor(obj)
} else {
obj.constructor()
}
for (let key in Object.getOwnPropertyDescriptors(obj)) {
newObj[key] = clone(obj[key])
}
return newObj
}
深比较
function deepCompare(a, b){
if(a === null
|| typeof a !== 'object'
|| b === null
|| typeof b !== 'object'){
return a === b
}
const propsA = Object.getOwnPropertyDescriptors(a)
const propsB = Object.getOwnPropertyDescriptors(b)
if(Object.keys(propsA).length !== Object.keys(propsB).length){
return false
}
return Object.keys(propsA).every( key => deepCompare(a[key], b[key]))
}
如何实现函数节流和防抖
节流
function throttle(fn, delay) {
delay = delay || 50
let statTime = 0
return function () {
statTime === 0 && fn.apply(arguments)
let currentTime = new Date()
if (currentTime = statTime > delay) {
fn.apply(arguments)
statTime = currentTime
}
}
}
let throttleFn = throttle(fn)
throttleFn()
throttleFn()
throttleFn()
throttleFn()//只会执行一次
防抖
function debounce(fn, delay) {
delay = delay || 50
let timer = null
return function () {
let self = this
clearTimeout(timer)
timer = setTimeout(fn.bind(self, arguments), delay);
}
}
你给我写一个原生bind方法
Function.prototype._bind = function (context) {
let self = this
let args_1 = [].prototype.slice.call(arguments, 1)
return function () {
let args_2 = [].prototype.slice.call(arguments)
let args = args_1.concat(args_2)
return this.apply(context, args)
}
}
这只是对 bind 的一种简单实现,如果有兴趣了解更多可以参考 Javascript 中 bind() 方法的使用与实现
如何实现一个数组的展平
function (ary) {
return ary.toString().split(',')
}
这是一个投机取巧的方法,如果有兴趣可以搜索一下其他实现方法
如何添加、删除、移动、复制DOM节点
创建
- createTextNode() //创建文本节点
- createElement() //创建元素节点
- createDocumentFragment() //创建文档碎片
操作
- appendChild() //增加
- removeChild() //删除
- replaceChild() //替换
- insertBefore() //插入
查找
- getElementById()
- getElementByTagName()
- getElementByName()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论