面试准备
基础
拿到一个面试题,第一时间看到->考点 如何看到做不完的题海-》不变应万变(题可变,考点不变) 如何对待接下来的题目-》题目到知识点,再到题目
类型判断
typeof
typeof 能判断哪些类型?(考点 js变量类型)
instanceof
何时使用===何时使用== (考点:强制类型转换)
- 字符串拼接
- ==
- if语句
逻辑判断
值类型和引用类型的区别
window.onload 和DOMContentLoaded的区别? (考点:页面加载过程) 为什么样式放上面,js放下面
手写节流throttle。防抖debounce (考点:性能。体验优化)
promise 解决了什么问题? (考点:js异步,event loop) event loop
手写深拷贝
注意点:
- 注意判断值类型和引用类型,值直接返回,引用递归处理
- 注意判断是数组还是对象
const obj={
age:20,
name:'xxx',
address:{
city:'beijing'
},
arr:['a','b','c']
}
function deepclone(obj){
if(typeof obj!=='object' || obj==null){
return obj //obj是null,或者不是对象和数组,直接返回
}
let result
if(obj instanceof Array){
result =[]
}else{
result ={}
}
for(let key in obj){
//保证key不是原型的属性
if(obj.hasOwnProperty(key)){
result[key]=deepclone(obj[key])
}
}
return result
}
原型 && 原型链
class
子类使用extends 继承父类
子类使用super传入参数
classs Perple(){
constructor(name){
this.name=name
}
eat(){} //可以继承
static see(){} //不可继承私有
}
class Student extends Perple {
constructor(name,number){
super(name) //继承父类
this.number=number
}
sayHi(){ //子类自己的方法
console.log('姓名'+this.name+this.number)
}
}
const xialuo=new Student('ludan',100)
console.log(xialuo.name)
console.log(xialuo.number)
ludan.eat()
ludan.see()
总结:
- 每个class都有显示原型prototype
- 每个实例都有隐藏原型__proto__
- 实例的__proto__指向对应class的protorype
基于原型的执行规则
- 获取属性xialuo.name或执行方法xialuo.eat()时
- 先在自身属性和方法寻找
- 如果找不到则自动去隐式原型查找
原型链
如何准确判断一个变量是不是数组?
a instanceof Array
Object.prototype.toString.call(arg)==='[object Array]'
作用域和闭包
- this的不同应用场景,如何取值?
当做变通函数被调用
使用call apply bind
作为对象方法调用
在class的方法中调用昌当前实例本身
箭头函数找的是上级的作用域
- 实际开发中闭包的应用场景,举例说明
隐藏数据,只提供API
function createCache(){
const data={}
return {
set:function(key,val){
data[key]=val
},
get:function(){
return data[key]
}
}
}
const c=createCache()
c.set('a',100)
console.log(c.get('a'))
闭包
闭包应用的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被返回
自由变量
!!!所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方
作用域
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()
this
this是在确认是在执行的时候确认的
function fn1(){
console.log(this)
fn1()
fn1.call({x:100})
const fn2=fn1.bind({x:200})
fn2()
}
js创建10个a标签,点击的时候弹出对应的序号 (考点:作用域)
let i,a
for(i=0;i<10;i++){
a=document.createElement('a')
a.innerHTML=i+'<br>'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
修改把let 放for的块级作用域里
let a
for(let i=0;i<10;i++){
a=document.createElement('a')
a.innerHTML=i+'<br>'
a.addEventListener('click',function(e){
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
异步和单线程
题目
- js是单线程的最早操作DOM 同步
- 但是加入请求需要异步,
- 异步不会阻塞代码执行 (网络请求,如ajax图片加载,定时任务,如setTimeout)
- 同步会阻塞代码执行
promise 加载一张图片
function loadImg(src){
return new promise((resolve,reject)=>{
const img=document.createElement('img')
img.onload=()=>{
resolve(img)
}
img.onerror=()=>{
const err=new Error('图片加载失败 ${src}')
reject(err)
}
img.src=src
})
}
- 请描述Event loop(事件循环/事件轮询)的机制
- 什么是宏任务和微任务,两者有什么区别?
- promise有哪三种状态?如何变化 ?
场景题
js如何执行
- 从前到后,一行一行执行
- 如果某一行执行报错,则停止下面代码的执行
- 先把同步代码执行完,再执行异步代码
event loop过程1
- 同步代码,一行一行放在Call Stack执行
- 遇到异步,会先记录下,等待时机(定时,网络请求等)
- 时机到了,就移动到Callback Queue
- 如Call Stack为空(即同步代码执行完)Event loop开始工作
- 轮询查找Callback Queue,如有则移动到Call Stack执行
- 然后继续轮询查找(永动机一样)
promise
- 三种状态
pending resolved rejected
pending->resolved 或 peding->rejected
变化不可逆
- 状态的表现和变化
pending状态,不会触发then和catch
resolved状态,会触发后续的 then回调函数
rejected状态,会触发后续的catch回调函数
- then和catch对状态的影响
then 正常返回resolved,里面有报错则返回rejected
catch正常返回resolved,里面有报错则返回rejected
async/await
- 异步回调callback hell
- promise then catch链式调用,但也是基于回调函数
- async/await是同步语法,彻底消灭回调函数
async/await和Promise的关系
- 执行async函数,返回的是promise对象
async function fn1(){
//return 100 相当于 return Promise.resolve(100)
return Promise.resolve(200)
}
const res1=fn1() //执行async函数,返回的是一个 Promise对象
//console.log('res1',res1) //Promise对象
res1.then(data=>>{
console.log(data) //200
})对象
- await相当于promise的then
特殊情况的处理,以下这个就执行不了可以使用try catch捕获
后面跟函数形式
- try...catch可捕获异常,代替了promise的catch
for...of
- for...in(以及forEach for)是常规的同步遍历
- for...of 常用于异步的遍历
代码结果会同步出现 1 4 9
如果以下代码想实现先出1,过1秒出4,再过一秒出9

宏任务和微任务
- 微任务是ES6语法规定
- 宏任务是由浏览器规定的
宏任务微任务都有哪些
js Web Api--DOM
知识点:
DOM BOM ajax 事件绑定 存储
题目-DOM
- DOM是哪种数据结构
- DOM操作的常用API
- attr和property的区别
- 一次性插入多个DOM节点,考虑性能
DOM本质
DOM本质是一层层往下的树
获取DOM
DOM节点的property
DOM节点的attribute
const pList=document.querySelectorAll('p')
const p=pList[0]
p.getAttribute('data-name')
p.getAttribute('data-name','ludan')
property attribute区别
- property:修改对象属性,不会体现到html结构中
- attribute:修改html属性,会改变html结构
- 两者都有可能引起DOM重新渲染
DOM 结构操作
- 新增/插入节点
- 获取子元素列表,获取父元素
- 删除子节点
DOM性能
- DOM操作非常昂贵,避免频繁的DOM操作
- 对DOM查询做缓存
- 将频繁操作改一次性操作
BOM
navigator history screen location
题目
- 如何识别浏览器的类型
- 分析拆解url各个部分
navigator 和 screen
location 和 history
事件
题目
- 编写一个通用的事件监听函数
- 描述事件冒泡的流程
- 事件代理
-
件流分为三个阶段:
- 捕获事件阶段
- 处于目标阶段
- 冒泡事件阶段
-
无限下拉的图片列表,如何监听每个图片的点击?
封装事件函数
function bindEvent(elm,type,selector,fn){
if(fn==null){
fn=selector
selector=null
}
elem.addEventListener(type,(event)=>{
const target=event.target
if(selector){
//代理绑定
if(target.matches(selector)){ //判断是否符合选择器
fn.call(target,event)
}
}else{
//普通绑定
fn.call(target,event)
}
})
}
ajax
知识点
题目
-
手写一个简易的ajax
- 知识点:XMLHttpRequest
- 状态码
- 跨域:同源策略,跨域解决方案
POST
xhr.readyState
xhr.status
2XX-表示成功处理请求,如200
3XX-需要重定向,浏览器直接跳转,如301 302 304
4XX-客户端请求错误,如404 403
5XX-服务器端错误
- 跨域的常用实现方式
跨域 (同源策略)
- ajax请求时,浏览器要求当前网页和server必须同源(安全)
- 同源:协议。域名,端口 三者必须一致
- 前端 http://a.com:9090;server:https://b.com/api/xxx
- 加载图片css js可无视同源策略
<img src='' /> 可用于统计打点,可使用第三方统计服务
<link href='' />
<script>
link script可使用CDN,CDN一般都是外域
script可实现JSONP
- 所有的跨域,都必须经过server端允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
- JSONP
window.callback=function(data){
//这是我们跨域得到信息
console.log(data)
}
<script src='https://baidu.com/getData.js'></script>
<!--将返回 callback({x:100,y:200})-->
- CORS(服务端支持)
fetch请求
developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
存储
题目
- 描述cookie localStorage sessionStorage区别
cookie
缺点
localStorage sessionStorage
http
题目
-
http常见的状态码有哪些?
- 1xx 服务器收到请求
- 2xx 请求成功 200
- 3xx 重定向 如302
- 301 永久重定向(配合location浏览器自动处理)
- 302 临时重定向(配合location浏览器自动处理 tnnnnn【
- 同)
- 304 资源未被修改
- 4xx 客户端错误 如404
- 404 资源未找到
- 403 没有权限
- 5xx 服务端错误 如500
- 500 服务器错误
- 504 网关超时
-
http methods
- 传统的methods
- 现在的methods
- get获取数据
- post新建数据
- patch/put 更新数据
- delte删除数据
-
什么是Restful API
- 一种新的API设计方法
- 传统API设计:把每个url当做一个功能
- Restful API设计:把每个url当做一个唯一的资源
- 尽量不用url参数
- 用method表示操作类型
-
method 表示操作类型(传统API设计)
- post请求 /api/create-blog
- post请求 /api/update-blog?id=100
- get请求 /api/get-blog?id=100
-
http常见的header有哪些?
- 常见的Request Headers
- Accept 浏览器可接收的数据格式
- Accept-Encoding浏览器可接收的压缩算法,如gzip
- Accept-Languange浏览器可接收的语言,如zh-CN
- Connection:keep-alive 一次TCP连接重复使用
- cookie
- Host
- User-Agent (简称UA)浏览器信息
- Content-type 发送数据的格式,如application/json
- 常见的Response Headers
- Content-type 返回数据的格式,如application/json
- Content-length 返回数据的大小,多少字节
- Content-Encoding 返回数据的压缩算法,如gzip
- set-cookie
- 常见的Request Headers
-
自定义header
- 缓存相关的headers
- Cache-Control Expires
- Last-Modified If-Modified-Since
- Etag If-None-Match
http 缓存
- 描述一下 http的缓存机制(重要)
关于缓存
* 什么是缓存 (不变化的数据可以缓存起来,不用每次都重新加载)
* 为什么需要缓存(让网页加载更快)
* 哪些资源可以被缓存?一静态资源(js css img)
http缓存-强制缓存
Cache-Control
Cache-Control的值 :
max-age (可以设置缓存过期的最大时间)
no-cache(不用本地强制缓存,交给服务端处理)
no-store(我们不用本地缓存,不用服务端的缓存)
private (只允许最终用户做缓存)
public (只允许中间代码,路由做缓存)
Expires
同在Response Headers中
同为控制缓存过期
已被Cache-Control代替
hrrp缓存-协商缓存
- 服务器端缓存策略 (服务端会告诉前端这个数据没有变,使用前端缓存)
- 服务器判断客户端资源,是否和服务端资源一样
- 一致则返回304,否则返回200和最新的资源
在Response Headers中,有两种
Last-Modified资源的最后修改时间
Etag资源的唯一标识(一个字符串,类似人类的指纹)
会优先使用 Etag
last-Modified只能精确到秒级
如果资源被重复生成,而内容不变,则Etag 更精确
请求的事例
总结缓存
三种刷新操作,不同刷新,不同的缓存策略
- 正常操作:地址栏输入url,跳转链接,前进后退等
- 手动刷新:F5,点击刷新按钮,右击菜单刷新
- 强制刷新:Ctrl+F5
强制刷新对缓存影响
正常操作:强制缓存有效,协商缓存有效
手动刷新:强制缓存失效,协商缓存有效
强制刷新:强制缓存失效,协商缓存失效
关于开发环境
git 代码版本管理工具(大型项目需要多人协作开发,必须熟用git)
调试工具
抓包
webpack babel
linux 常用命令
git常用命令
git add .
git checkout xxx
git commit -m 'xxx'
git push origin master
git pull origin master
git branch
git checkout -b xxx
git merge xxx
git show 版本号 查看某次的修改
在commit之前
git checkout . 撤销所有修改
git checkout index.html //撤销本次修改
git checkout -b xxx 创建新分支
git fetch //拉取所有分支
忘记在master分支上改了怎么办
git stash 先把修改分支分在一边其他区域
git status
git checkout -b xxx 这时就可以切换分支了
切到新分支后把之前暂时存起来的修改放出来
git stash pop
接下来就是把修改的在当前分支做添加操作了
chrome 调试工具
Elements
Console
debugger
Network
Application
抓包
- 移动端h5页,查看网络请求,需要用工具抓包
- windows一般用fiddler
- Mac OS一般用charles
抓包过程
手机和电脑连同一个局域网
将手机代理到电脑上
手机浏览网页,即可抓包
抓包设置 charles为例
- 查看网络请求
- 网址代理
- https
- proxy/SSL Proxying Settings 开启 Enable SSl Proxing
手机下载安装证书
webpack 和 babel
- ES6 模块化,浏览器暂时不支持
- ES6语法,浏览器并不完全支持
- 压缩代码,整合代码,以让网页加载更快
babel
babel是把js高版本语法转成es5
ES6 模块化导出导入注意事项
第一想清楚是使用结构的方式,导出就不能用default
import {fn,a} from ‘./b’
b.js
export fn(){}
export const a=5
或者
function fn(){}
const a=5
export {
fn,
a
}
第二种情况下,不使用结构,default导出
import xxx from './b'
b.js
function fn(){}
const a=5
export default {
fn,
a
}
运行环境
- 运行环境即浏览器(server端有nodejs)
- 下载网页代码,渲染出页面,期间会执行若干js
- 要保证代码在浏览器中:稳定且高效
知识点
加载资源的形式
* html代码
* 媒体文件,如图片,视频等
* javascript css
加载资源的过程
* DNS解析:域名-》IP地址
* 浏览器根据IP地址向服务器发起http请求
* 服务器处理http请求,并返回浏览器
渲染页面的过程
* 根据HTML代码生成DOM Tree
* 根据CSS代码生成CSSOM
* 将DOM Tree和CSSOM整合形成Render Tree
* 根据Render Tree渲染页面
* 遇到<script>则暂停渲染,优先加载并执行js代码,完成再继续
* 直至把Render Tree渲染完成
题目
- 从输入url到渲染出页面的整个过程
- window.onload和 DOMContentLoaded的区别
网页加载过程
为什么把js放到最后
js文件如果放在上面或者中间,也会出现一边改一边渲染,js会阻止渲染让渲染整个页面时间加长
为什么把css放到header中
因为放到heder里直接把css直接生成CSSOM,然后和DOM Tree直接合并生成render Tree 如果放到body里,假如生成一个结构了,后面又修改css二次渲染
性能优化
性能优化原则
多使用内存,缓存或其他方法
减少CPU计算量,减少网络加载耗时
(适用于所有编程的性能优化--空间换时间)
让加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码,SSR服务器端渲染,缓存
- 使用更快的网络:CDN
让渲染更快
- CSS放在head,js放在body最下面
- 尽早开始执行js,用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多)
- 对DOM查询进行缓存
- 频繁DOM操作,合并到一起插入DOM结构
- 节流throttle 防抖debounce
缓存
- 静态资源加hash后缀,根据文件内容计算hash
- 文件内容不变,则hash不变,则url不变
- url和文件不变,则会自动触发http缓存机制,返回304
CDN
SSR
懒加载
()
缓存DOM查询
多个DOM操作一起插入到DOM结构
防抖 debounce
- 监听一个输入框的,文字变化后触发change事件
- 直接用keyup事件,则会频发触发change事件
- 防抖:用户输入结束或暂停时,才会触发change事件
原理利用定时器,每次开始就把上次的定时器操作清空,重新开始
function debounce(fn,delay=500){
const timer=null
return function(){
if(timer){
clearTimeout(timer)
}
timer=setTimeout(()=>{
fn.apply(this,arguments) //需要用this外面传入函数就不能用箭头函数
},delay)
}
}
节流 throttle
- 拖拽一个元素时,要随时拿的到该元素被拖拽的位置
- 直接用drag事件,则会频发触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每隔100ms触发一次
function throttle(fn,delay=500){
let timer=null
return function(){
if(timer){
return
}
timer=setTimeout(()=>{
fn.apply(this,arguments) //需要用this外面传入函数就不能用箭头函数
timer=null
},delay)
}
}
安全
- 常见的web前端攻击方式有哪些?
xss 跨站请求攻击
* 发表嵌入script脚本
* 获取cookie,发送到我的服务器(服务器配合跨域)
* 有人查看就可以轻松收割访问者的cookie
预防
把script括号用特殊字符替换
使用工具 npmjs.com/package/xss
xsrf 跨站请求伪造
面试真题
题1
- var和let const的区别
- var是ES5语法,let const 是ES6语法,var有变量提升
- var和let是变量,可修改,const是常量,不可修改
- let const有块级作用域,var没有
- typeof返回哪些类型
- 列举强制类型转换和隐匿类型转换
题2
- 手写深度比较,模拟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
}
//两个都是对象或者数组,而且不全等
//1先取出obj1 obj2的个数
const obj1Keys=Object.keys(obj1)
const obj2Keys=Object.keys(obj2)
if(obj1Keys.length!=obj2Keys.length){
return false
}
for(let key in obj1){
const res=isEqual(obj1[key],obj2[key])
if(!res){
return false
}
}
return true
}
const obj1={
a:100,
b:{
x:100,
y:200
}
}
const obj2={
a:100,
b:{
x:100,
y:200
}
}
console.log('isEqual=='+isEqual(obj1,obj2))
- split()和join()的区别
- 数组的pop push unshift shift 分别做什么
- 功能是什么?
- 返回值是什么
- 是否对原数组有影响
纯函数:1不改变源数组(没有副作用)
题目3
- 数组slice和splice的区别
- 功能区别(slice-切片,splice-剪接)
- 参数和返回值
- 是否纯函数?
- [10,20,30].map(parseint)返回结果是什么
- ajax请求get和post的区别
- get 一般用于查询操作,post一般用户提交操作
- get参数拼接在url上,post在请求体内(数据体积可更大)
- 安全性:post易于防止CSRF
题目4
- 函数call和apply的区别
- 事件代理委托是什么
- 闭包是什么,有什么特性,有什么负面影响
- 作为参数被传入,作为返回值被返回
- 自由变量的查找,要在函数定义的地方(而非执行的地方)
- 变量会常驻内存,得不到释放,闭包不要乱用
题目5
img script
题目7
- 函数声明和函数表达式的区别
- new Object()和Object.create()区别
- 关于this,使用场景
this值在执行的时候才能确定是谁
题目8
- 关于作用域和自由变量的场景题-1
- 判断字符串以字母开头,后面字母数字下划线,长度6-30
deerchao.cn/tutorials/regex/regex.html
- 关于作用域和自由变量的场景题-2