Appearance
JavaScript面试题
第1章 JS基础-变量类型和计算【不会变量,别说你会JS】
本章介绍变量的类型和计算的知识点和题目,包括值类型和引用类型区别,类型判断,深拷贝等。变量和类型是一个任何一门语言的基础,不了解的话,会被认为是 JS 语法不过关。 共 4 节 (43分钟)
1-1 JS 值类型和引用类型的区别 (13:02)
变量类型和计算
- 题目
- typeof能判断哪些类型
- 何时使用===何时使用==
- 值类型和引用类型的区别
- 手写深拷贝
- 知识点
- 变量类型
- 值类型vs引用类型
- typeof运算符
- 深拷贝
- 变量计算
- 变量类型
- 解答
值类型
js
//值类型
let a =100
let b = a
a =200
console.log(b) //100
//引用类型
let a = {age:20}
let b = a
b.age = 21
console.log(a.age) // 21
深层分析
值类型在内存中的存储方式
引用类型在内存中的存储方式
- 引用类型都存储在堆中,变量存储的是引用类型的内存地址
常见的值类型
js
const a //undefined
const s = 'abc'
const n = 100
const b = true
const s = Symbol('s')
常见引用类型
js
const obj = {x:100}
const arr = ['a','b','c']
const n = null //特殊引用类型,指针指向空地址
//特殊引用类型,但不用于存储数据,所以没有"拷贝,复制函数"这一说法
function fn(){}
1-2 手写 JS 深拷贝 (16:13)
typeof运算符
- 识别所有值类型
- 识别函数
- 判断是否是引用类型(不可再细分,再细分需要用原型链的intanceof方法)
js
//判断所有值类型
let a; typeof a //'undefined'
const str = 'abc'; typeof str //'string'
const n = 100; typeof n //'number'
const b = true; typeof b //'boolean'
const s = Symbol('s'); typeof s //'symbol'
//能判断函数
typeof console.log //'function'
typeof function(){} //'function'
//能识别引用类型(不能再继续识别)
typeof null //'object'
typeof ['a','b'] //'object'
typeof {x:100} //'object'
深拷贝
深拷贝与浅拷贝的区别
- 浅拷贝:只是拷贝一层,更深层次对象级别的只拷贝了地址
- 深拷贝:层层拷贝,每一级别的数据都会拷贝
js
const obj = {
age: 20,
name: 'xxx',
address: {
city: 'beijing'
},
arr: ['a','b','c']
}
const obj2 = obj1
obj2.address.city = "shanghai"
console.log(obj1.address.city) //"shanghai"
//深拷贝
/***
@param {Object} obj要拷贝的对象
*/
function deepClone(obj = {}){
if(typeof obj !== 'object' || obj == null){
// obj是null或者不是对象和数组,直接返回
return obj
}
//初始化返回结果
let result
if(obj instanceof Array){ //判断数组
result = []
}else{
result = {}
}
//处理结果
for(let key in obj){
if(obj.hasOwnProperty(key)){// 保证key不是原型的属性
//递归调用
result[key] = deepClone(obj[key])
}
}
return result
}
const obj3 = deepClone(obj1) //深拷贝
obj3.address.city = "shanghai"
console.log(obj3.address.city) //"shanghai"
console.log(obj1.address.city) //"beijing"
思考深拷贝的堆栈图
1-3 变量计算 - 注意某些类型转换的坑 (09:48)
字符串拼接
js
const a = 100 + 10 // 110 加法
const b = 100 + '10' // '10010'
const c = true + '10' // 'true10'
const d = 100 + parseInt('10') // 110 加法
== 运算符
js
100 == '100' //true
0 == '' //true
0 == false // true
false == '' //true
false == '' // true
null = undefined //true
// 什么情况下用==
// 除了 == null之外,其他一律用 === ,例如
const obj = {x:100}
if(obj.a == null ){}
//相当于:
//if(obj.a === null || obj.a === undefined){}
if语句和逻辑运算
truly变量: !!a===true
的变量
falsely变量:!!a===false
的变量
js
//以下是falsely变量 除此之外都是truely变量
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false
if语句判断的不是true 和 false 实际上判断的是truly变量和falsely变量
js
//truly变量,以下if语句会执行
const a = true
if(a){
// ...
}
const b = 100
if(b){
// ...
}
//falsly变量 ,以下if语句不会执行
const c = ''
if(c){
//...
}
const d = null
if(d){
//...
}
let e
if(e){
//...
}
逻辑判断
js
console.log(10&&0) //0 fasly直接返回
console.log(''||'abc') //'abc' truly直接返回
console.log(!window.abc) //true
1-4 变量类型相关的面试题 (03:51)
题目
- typeof能判断哪些类型
- 何时使用===何时使用==
- 值类型和引用类型的区别
- 手写深拷贝
typeof能判断哪些类型
- 识别所有值的类型
- 识别函数
- 判断是否是引用类型(不可再细分)
何时使用===何时使用==
除了
== null
其他一律用===
值类型和引用类型的区别
出场景题
jsconst obj1 = {x:100,y:200} const obj2 = obj1 let x1 = obj1.x //这里是干扰,值类型,可以删除 obj2.x = 101 x1 = 102 ////这里是干扰,值类型,可以删除 console.log(obj1) //{x:101,y:200}
手写深拷贝
- 注意判断值类型和引用类型
- 注意判断是数组还是对象
- 递归
第2章 JS基础-原型和原型链【三座大山之一,必考!!!】
本章介绍原型、原型链和 class 相关的知识点和题目。包括 class ,继承,原型,原型链,instanceof。原型是“JS 三座大山”之一,原型和原型链也是必考知识点。
2-1 JS 原型的考点和面试题 (04:17)
原型作用是啥:
- es6之前js是用原型来实现继承的
- es6是用class来继承的
3个题目:
- 如何准确判断一个变量是不是数组
- 手写一个简易的jquery,考虑插件和扩展性
- class原型本质,怎么理解
知识点:
- class和继承
- 类型判断instanceof
- 原型和原型链
2-2 如何用 class 实现继承 (14:50)
class有哪些东西
- constructor 构建
- 属性
- 方法
class-demo.js
js
class Student{
constructor(name ,number){
this.name = name
this.number = number
}
sayHi(){ //定义方法
console.log(`姓名${this.name},学号${this.number}`)
}
}
//通过new 类声明对象实例
const xiaomi = new Student('小米',100)
console.log(xiaomi.name)
console.log(xiaomi.number)
xiaomi.sayHi()
//通过new 类声明对象实例
const apple = new Student('苹果',200)
console.log(apple.name)
console.log(apple.number)
apple.sayHi()
继承
- extends
- super
- 扩展或重写方法
代码演示
js
class People {
constructor(name){
this.name = name
}
eat(){
console.log(`${this.name} eat something`)
}
}
//子类
class Student extends People{
constructor(name, number){
super(name) //获取父类属性
this.number = number
}
sayHi(){
console.log(`姓名${this.name},学号${this.number}`)
}
}
//子类
class Teacher extends People{
constructor(name, number){
super(name) //获取父类属性
this.major = major
}
teach(){
console.log(`${this.name}教${this.major}`)
}
}
//通过new 类声明对象实例
const apple = new Student('苹果',200)
console.log(apple.name)
console.log(apple.number)
apple.sayHi()
apple.eat()
//通过new 类声明对象实例
const ai = new Student('小爱老师',前端)
console.log(ai.name)
console.log(ai.major)
ai.teach()
ai.eat()
2-3 如何理解 JS 原型(隐式原型和显示原型) (09:10)
类型判断 instanceof 来判断是否是子类
js
xiaomi instanceof Student //true
xiaomi instanceof People //true
xiaomi instanceof Object //true Object是所有引入类型的父类
[] instanceof Array //true Array是[]的父类
[] instanceof Object //true
{} instanceof Object //true
原型
js
//class实际上是函数,可见是语法糖
typeof People //'function'
typeof Student
//隐式原型和显示原型
console.log(xiaomi.__proto__) //隐式原型只有在实例中
console.log(Student.prototype) //显示原型在类中
console.log(xiaomi.__proto__===Student.prototype)
原型关系
- 每个class都有显示原型prototype
- 每个实例都有隐式原型
__proto__
- 实例的
__proto__
指向class的prototype
基于原型的执行规则
- 获取属性xiaomi.name活执行方法xiaomi.sayHi()时
- 先在自身属性和方法中寻找
- 如果找不到择取
__proto__
寻找
2-4 instanceof 是基于原型链实现的 (08:57)
原型链是指带有继承关系的类的原型
console.log(Student.prototype.__proto__) //Student类是People类的继承也是某种程度的实例
console.log(People.prototype) //eat函数
console.log(Student.prototype.__proto__===People.prototype)
hasOwnProperty()方法,判断是否是自己的属性
js
xiaomi.hasOwnProperty('name') //true
xiaomi.hasOwnProperty('sayHi') //false sayHi是Student的方法 是xiaomi通过原型链去寻找的
如何找到hasOwnProperty找到原型链
那么instanceof就是看你是否能找到对应顶层的类型是否相等,实现原理就是根据原型链
2-5 JS 原型本章相关的面试题 (12:30)
题目
如何准确判断一个变量是不是数组
手写一个简易的jquery,考虑插件和扩展性
class原型本质,怎么理解
class的原型本质
- 原型和原型链的图示
- 属性和方法的执行规则
手写一个简易的jquery,考虑插件和扩展性
js
class JQuery{
constructor(selector){
const result = document.querySelectorAll(selector)
const length = result.length
for(let i = 0;i< length;i++){
this[i] = result[i] //获取每一个dom
}
this.length = length
this.selector = selector
}
get(index){
return this[index]
}
each(fn){ //遍历
for(let i = 0;i<this.length;i++){
const elem = this[i]
fn(elem)
}
}
on(type, fn){ //监听
return this.each(elem=>{
elem.addEventListener(type, fn ,false)
})
}
//扩展很多DOM操作
}
//html
<p>1</p>
<p>2</p>
<p>3</p>
//定义一个jquery对象
const $p = new JQuery('p')
console.log($p)
$p.get(1)
$p.each((elem)=>console.log(elem.nodeName))
$p.on('click',()=>alert('clicked'))
考虑插件扩展性
- 方式1:使用插件(原型)
- 方式2:复写机制(继承)
js
//插件
JQuery.prototype.dialog = function(info){
alert(info)
}
$p.dialog('abc')//弹出abc
//造轮子
class myJQuery extends JQuery{
constructor(selector){
super(selector)
}
//扩展自己的方法
addClass(classname){
}
style(data){
}
}
第3章 JS基础-作用域和闭包【三座大山之二,不会闭包,基本不会通过】
本章介绍作用域和闭包的知识点和题目。包括作用域,自由变量,闭包,this 等部分。作用域是“JS 三座大山”之二,不知道闭包的话,面试通过概率不大。
3-1 什么是作用域?什么是自由变量? (07:11)
因为存在多个函数调用所以作用域很重要
题目
- this的不同应用场景,如何取值
- 手写bind函数
- 实际开发中闭包的应用场景,举例说明
- 场景题
知识点
- 作用域和自由变量
- 闭包
- this
作用域
一个变量的合法的使用范围,如果超出使用范围就会报错
种类:
- 全局作用域
- 函数作用域
- 块级作用域(es6新增)
js
//es6块级作用域
if(true){
let x = 100
}
console.log(x) //会报错
自由变量:(没有定义的变量)
- 一个变量在当前作用域没有定义,但被使用了
- 向上级作用域,一层一层依次寻找,直到找到为止
- 如果到全局作用域都没找到,则报错xx is not defined
3-2 什么是闭包?闭包会用在哪里? (07:46)
闭包
- 作用域的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被返回
1.closure.js
js
//1.函数作为返回值
function create(){
let a = 100;
return function(){
console.log(a)
}
}
const fn = create()
const a = 200
fn() //求该结果 为100
//2.函数作为返回值被返回
function print(fn){
const a = 200
fn()
}
const a = 100
function fn(){
console.log(a)
}
print(fn) //返回100
总结:
闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找不是在执行的地方
3-3 this 有几种赋值情况 (04:35)
1.this在5种场景中调用
- 作为普通函数
- 使用call apply bind
- 作为对象方法被调用
- 在class方法中调用
- 箭头函数中被调用
总结:
this取什么值是在函数执行的时候确定的不是在函数定义的时候确定的
js
function fn1(){
console.log(this)
}
function fn2(a,b){ //有参数的函数
console.log(this)
console.log(a)
console.log(b)
}
fn1() //window
//call:改变this指向
fn1.call({x:100}) //{x:100}
//apply:改变this指向,和call基本上一致,唯一区别在于传参方式,是用数组
fn2.apply({x:100},[1,2]) //{x:100} 1 2
fn2.call({x:100},1,2) //{x:100} 1 2
//bind:作用和call作用是相同的改变this指向,但是是返回一个新的函数
const fn2 = fn1.bind({x:200})
fn2() //{x:200}
//对象方法中
const zhangsan = {
name: '张三',
sayHi(){
//this即当前对象
console.log(this)
},
wait(){
// this===window
setTimeout(function(){ //setTimeout调用的是window的方法
console.log(this)
})
}
}
//对象方法中,箭头函数
const zhangsan1 = {
name: '张三',
sayHi(){
//this即当前对象
console.log(this)
},
wait(){
// this即当前对象
setTimeout(()=>{ //箭头函数中this永远返回上级作用域
console.log(this)
})
}
}
//类中的this
class People{
constructor(name){
this.name = name
this.age = 20
}
sayHi(){
console.log(this)
}
}
const zhangsan = new People('张三')
zhangsan.sayHi() //zhangsan对象
3-4 作用域相关的面试题 (11:47)
题目
- this的不同应用场景,如何取值
- 作为普通函数
- winow
- 使用call apply bind
- 返回指向对象
- 作为对象方法被调用
- 返回当前对象
- 如果是setTimeout返回window
- 在class方法中调用
- 返回new出来的对象
- 箭头函数中被调用
- 返回上一级作用域
- 作为普通函数
- 手写bind函数
bind-demo.js
js
//bind示例
function fn1(a,b,c){
console.log('this',this)
console.log(a,b,c)
return 'this is fn1'
}
const fn2 = fn1.bind({x:100}, 10, 20, 30)
const res = fn2() //返回出了{x:100} 10 20 30
console.log(res)
js
//手写bind
Function.prototype.bind1 = function(){
//将参数拆解为数组
const args = Array.prototype.slice.call(arguments)
//获取this(数组第一项)
const t = args.shift()
//fn1.bind(..)中的fn1
const self = this
//返回一个函数
return function(){
return self.apply(t,args)
}
}
实际开发中闭包的应用场景,举例说明
- 隐藏数据
- 做一个简单的cache工具
js
//闭包隐藏数据,只提供API
function createCache(){
const data = {} //闭包中的数据,被隐藏,不被外界访问
return {
set:function(key,val){
data[key] = val
},
get:function(key){
return data[key]
}
}
}
const c = createCache()
c.set('a',100)
console.log(c.get('a'))
//这里只能通过set函数来修改data没有其他方法
应用:
jq的事件绑定,比如一个列表多个li绑定对应不同的事件
例题
绑定函数不会立即执行的情况,需要闭包来解决循环数值相同问题
3-5 补充 - 原型中的 this (02:18)
js
xiaoluo.sayHi() //执行父类中定义的sayHi方法
xiaoluo.__proto__.sayHi.call(xiaoluo) //等效于xiaoluo.sayHi()
第4章 异步【三座大山之三,必考!!!】
本章介绍异步的知识点和题目。包括异步和同步的区别,异步应用场景,以及 Promise 。异步是“JS 三座大山”之三,所有公司的 JS 面试,100% 会考察异步和 Promise 。
4-1 同步和异步有何不同 (09:31)
异步和单线程
题目
- 同步和异步的区别是什么?
- 手写用promise加载一张图片
- 前端使用异步的场景有哪些?
- 场景题
js//setTimeout 笔试题 是按什么顺序打印 console.log(1) setTimeout(function(){ console.log(2) },1000) console.log(3) setTimeout(function(){ console.log(4) },0) console.log(5)
知识点
- 单线程和异步
- 应用场景
- callback hell和Promise
解答
单线程和异步
- js是单线程语言,只能同事做一件事儿
- 浏览器和nodejs已支持js启动进程,如 web work
- js和dom渲染共用同一个线程,因为js可修改dom结构
需求
- 遇到等待(网络请求,定时任务)不能卡住
- 需要异步
- 回调callback函数形式
js
//异步 程序会立即执行 100 300 再等待1s后执行200 不会阻塞进程
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
//同步 程序 100 弹出200 卡住了 300不会出来
console.log(100)
alert(200)
console.log(300 )
异步和同步
- 基于js是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
4-2 异步的应用场景有哪些 (03:28)
应用场景
- 网络请求,如ajax图片加载
- cpu是空闲的,需要异步
- 定时任务,如setTimeout
js
// ajax
console.log('start')
$.get('./data1.json',function(data1){
console.log(data1) //这里异步,等到加载完才触发,没有停下来等待
})
console.log('end')
//图片加载
console.log('start')
let img = document.createElement('img')
img.onload = function(){ //这里相当于异步,等到加载完成才触发
console.log('loaded')
}
img.src = '/xxx.png'
console.log('end')
定时器
js
//setTimeout 一次性
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
//setInterval 循环执行
console.log(100)
setInterval(function(){
console.log(200)
},1000)
console.log(300)
4-3 promise的基本使用 (05:55)
callback hell回调地狱
js
//获取第一份数据
$.get(url1,(data1)=>{
console.log(data1)
//获取第二份数据
$.get(url2,(data2)=>{
console.log(data2)
//获取第三份数据
$.get(url3,(data3)=>{
console.log(data3)
})
})
})
避免回调地狱,有了promise语法
js
//用promise写法来写上述功能
//定义promise的函数
function getData(url){
return new Promise((resolve,reject)=>{
$.ajax({
url,
success(data){
resolve(data)
},
error(err0){
reject(err)
}
})
})
}
const url1 = '/data1.json'
const url2 = '/data2.json'
const url3 = '/data3.json'
getData(url1).then(data1=>{
console.log(data1)
return getData(url2)
}).then(data2=>{
console.log(data2)
return getData(url3)
}).then(data3=>{
console.log(data3)
}).catch(err=>console.error(err))
4-4 JS 异步相关的题 (12:38)
题目
- 同步和异步的区别是什么?
- 基于js是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
- 手写用promise加载一张图片
js
const url = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
function loadImg(src){
return new Promise((resolve,reject)=>{
const img = document.creatElement('img')
img.onload = ()=>{
resolve(img)
}
img.onerror = ()=>{
reject(new Error(`图片加载失败 ${src}`))
}
img.src = src
})
}
//写法2
function loadImg(src){
const p = new Promise((resolve,reject)=>{
const img = document.creatElement('img')
img.onload = ()=>{
resolve(img)
}
img.onerror = ()=>{
reject(new Error(`图片加载失败 ${src}`))
}
img.src = src
})
return p //返回promise对象
}
loadImg(url).then(img=>{
console.log(img.width)
return img
}).then(img=>{
console.log(img.height)
}).catch(ex=>console.error(ex))
const url2 = 'https://news-bos.cdn.bcebos.com/mvideo/log-news.png'
loadImg(url).then(img1=>{
console.log(img1.width)
return img1 //返回普通对象
}).then(img1=>{
console.log(img1.height)
return loadImg(url2) //promise实例 返回的是resolve(img)中的img
}).then(img2=>{
console.log(img2.height)
}).catch(err=>console.error(err))
- 前端使用异步的场景有哪些?
- 网络请求,如ajax图片加载
- 定时任务,如setTimeout
- 场景题
js
//setTimeout 笔试题 是按什么顺序打印
console.log(1)
setTimeout(function(){
console.log(2)
},1000)
console.log(3)
setTimeout(function(){
console.log(4)
},0)
console.log(5)
//答案
1 3 5 4 2
小结
- 单线程和异步,异步和同步的区别
- 异步不会阻塞
- 前端异步应用场景
- promise解决callback hell
4-5 JS基础部分的考点总结 (03:33)
第5章 JS 异步进阶【想要进大厂,更多异步的问题等着你】
JS 的特色就是异步编程,所有有很多关于异步的考点,本章都会讲解。如 event loop、promise、async-await、微任务和宏任务。学不会这些,就不算是精通 JS ,也无法进大厂。 共 18 节 (156分钟)
5-1 本章考点介绍
js异步-进阶
- 之前讲解js异步,在于初阶的应用
- 本章在于js异步的原理和进阶
- 对初学者优点难度,尽量深入浅出
本章主要内容
- event loop
- promise进阶
- async/await
- 微任务/宏任务
5-2 看⼏个异步的⾯试题
问答题
- 请描述event loop(事件循环/事件轮询)的机制.可画图
- 什么是宏任务和微任务,两者有什么区别
- promise有哪3种状态?如何变化
场景题-promise then和catch的连接
js
//第一题
Promise.resolve().then(()=>{
console.log(1)
}).catch(()=>{
console.log(2)
}).then(()=>{
console.log(3)
})
//第二题
Promise.resolve().then(()=>{
console.log(1)
throw new Error('error1')
}).catch(()=>{
console.log(2)
}).then(()=>{
console.log(3)
})
//第三题
Promise.resolve().then(()=>{
console.log(1)
throw new Error('error1')
}).catch(()=>{
console.log(2)
}).catch(()=>{
console.log(3)
})
场景题-async/await
js
async function fn(){
return 100
}
(async function fn(){
const a = fn() //??
const b = await fn() //??
})
(async function(){
console.log('start')
const a = await 100
console.log('a',a)
const b = await Promise.resolve(200)
console.log('a',a)
const c = await Promise.reject(300)
console.log('c',c)
})()//执行完毕打印哪些内容
场景题-promise和setTimeout的顺序
js
console.log(100)
setTimeout(()=>{
console.log(200)
})
Promise.resolve().then(()=>{
console.log(300)
})
console.log(400)
场景题-外加async/await的顺序问题
js
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function (){
console.log('setTimeout')
},0)
async1()
new Promise(function (resolve){
console.log('promise1')
resolve()
}).then(function(){
console.log('promise2')
})
console.log('script end')
5-3 什么是event loop
event loop(事件循环/事件轮询)
- js是单线程运行的
- 异步要基于回调来实现的
- event loop就是异步回调的实现原理
js如何执行?
- 从前到后,一行一行执行
- 如果某一行执行报错,则停止下面代码的执行
- 先把同步代码执行完,再执行异步
示例
js
console.log('Hi') //同步代码
setTimeout(function cb1(){
console.log('cb1') //异步代码
},5000)
console.log('Bye') //同步代码
//Hi Bye cb1
event loop过程
- 初学者可能会感觉难,尽量深入浅出
- 第一遍讲解,感觉不懂不要停下,会重复讲解3遍
- 不要抠细节,不要扩大范围,核心是event loop过程
5-4 event loop 的执行过程
示例
js
console.log('Hi') //同步代码
setTimeout(function cb1(){
console.log('cb1') //异步代码
},5000)
console.log('Bye') //同步代码
//Hi Bye cb1
参与event loop的5个角色
- browser console
- 浏览器控制台
- Call Stack
- 执行语句栈
- Web APIs
- web api是js语法之外的浏览器的api,如settimeout
- Event loop
- 事件轮询,不断循环执行每一个事件
- Callback Queue
- 回调函数队列
总结event loop过程
- 同步代码,一行一行放在call stack执行
- 遇到异步,先在webAPis中记录一下,等待(定时,网络请求等)
- 时机到了,就会移动到Callback Queue
- 如果Call Stack为空(即同步代码执行完)Event loop开始工作
- 轮询查找callback queue,如有则移动到call stack执行
- 然后继续轮询查找(永动机一样)
5-5 DOM事件和event loop的关系
5-6 Promise有哪三种状态
5-7 Promise的then和catch如何影响状态的变化
5-8 Promise关于then和catch的⾯试题
5-9 async-await语法介绍-part1
5-10 async-await和Promise有什么关系
5-11 async-await是语法糖,异步的本质还是回调函数
5-12 for-of的应⽤场景
5-13 什么是宏任务和微任务
5-14 event-loop和DOM渲染的关系
5-15 为什么微任务⽐宏任务执⾏更早
5-16 微任务和宏任务的根本区别
5-17 解答JS异步的⾯试题
5-18 本章考点总结
第6章 JS-Web-API-DOM【学会DOM,才能具备网页开发的基础】
本章介绍 DOM 操作的知识点和题目。包括 DOM 结构,常用 DOM 操作,DOM 性能优化等。DOM 是网页结构的基础,学会 DOM 操作才可以做网页开发。 共 6 节 (42分钟)
6-1 从JS基础到JS-Web-API (03:20)
- js基础知识,规定语法(ECMA)
- js web api(w3c)
- 前者是后者的基础,两者结合才能真正实际应用
js基础知识
- 变量的类型和计算
- 原型和原型链
- 作用域和闭包
js web api
- dom
- bom
- 事件绑定
- ajax
- 存储
6-2 DOM的本质是什么 (06:56)
前言
- vue和react框架应用广泛,封装dom操作
- 但dom操作一直是前端基础,必备知识
- 只会vue而不懂dom操作的程序员不会长久
dom操作(document object model)
- 题目
- dom是哪种数据结构
- dom操作常用的api
- attr和property的区别
- 一次性插入多个dom节点,考虑性能
- 知识点
- dom本质
- dom节点操作
- dom结构操作
- dom性能
- 解答
DOM本质
HTML是由xml进化而来
xml是树
dom是html语言文件解析出来的一棵树
6-3 DOM节点操作 (13:33)
DOM节点操作
- 获取dom节点
- attribute
- property
获取dom节点
获取dom节点,通过dom节点设置dom的样式
- property 形式
- attribute函数
js
const div1 = document.getElementById('div1')
console.log('div1', div1)
const divList = document.getElementsByTagName('div') // 集合
console.log('divList.length', divList.length)
console.log('divList[1]', divList[1])
const containerList = document.getElementsByClassName('container') // 集合
console.log('containerList.length', containerList.length)
console.log('containerList[1]', containerList[1])
const pList = document.querySelectorAll('p')
console.log('pList', pList)
const pList = document.querySelectorAll('p')
const p1 = pList[0]
// property 形式
p1.style.width = '100px'
console.log( p1.style.width )
p1.className = 'red'
console.log( p1.className )
console.log(p1.nodeName)
console.log(p1.nodeType) // 1
// attribute
p1.setAttribute('data-name', 'imooc')
console.log( p1.getAttribute('data-name') )
p1.setAttribute('style', 'font-size: 50px;')
console.log( p1.getAttribute('style') )
property和attribute区别
- property:
- 修改对象属性,不会体现到html结构中
- attribute:
- 修改html属性,会改变html结构
- 两者都有可能引起dom重新渲染,尽量使用property,attribute一定会重新渲染因为html结构改变了
6-4 DOM结构操作 (08:34)
dom结构操作
- 新增/插入节点
- 获取子元素列表,获取父元素
- 删除子元素
新增/插入节点
js
// 新建节点
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入节点
div1.appendChild(newP)
// 移动节点 (针对已有节点p1,则是移动节点)
const p1 = document.getElementById('p1')
div2.appendChild(p1)
获取子元素列表,获取父元素
js
// 获取父元素
console.log( p1.parentNode )
// 获取子元素列表
const div1ChildNodes = div1.childNodes
console.log( div1.childNodes ) //这里包含了p标签元素 也包含了文本元素,需要排除文件元素
//Array.prototype.slice.call(div1.childNodes)是将div1.childNodes变成数组类型
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
if (child.nodeType === 1) { //nodeType为1是p标签
return true
}
return false
})
console.log('div1ChildNodesP', div1ChildNodesP)
删除子元素
js
div1.removeChild( div1ChildNodesP[0] )
6-5 如何优化 DOM 操作的性能 (07:07)
dom性能
- dom操作非常昂贵,避免频繁的dom操作
- 对dom查询做缓存
- 将频繁操作改为一次性操作
dom查询做缓存
js
//不缓存dom查询结果
for(let = 0; i < document.getElementsByTagName('p').length; i++){
//每次循环,都会计算length,频繁进行dom查询
}
//缓存dom查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length;
for(let i = 0; i< length; i++){
//缓存length,只进行一次dom查询
}
将频繁操作改为一次性操作
js
const list = document.getElementById('list')
// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment()
for (let i = 0; i < 20; i++) {
const li = document.createElement('li')
li.innerHTML = `List item ${i}`
// 先插入文档片段中
frag.appendChild(li)
}
// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag)
console.log(list)
6-6 DOM 操作相关的面试题 (02:03)
- 题目
- dom是哪种数据结构
- 树(DOM树)
- dom操作常用的api
- dom节点操作
- dom结构操作
- attribute和property区别
- 一次性插入多个dom节点,考虑性能
- dom是哪种数据结构
第7章 JS-Web-API-BOM【内容虽然不多,但是你不能不会】
本章介绍 BOM 操作的知识点和题目。本章内容虽然不多,但不可不会。 共 1 节 (6分钟)
7-1 BOM 操作相关的面试题 (05:57)
BOM操作(Browser Object Model)
题目
- 如何识别浏览器类型
- 分析拆解url各个部分
知识点
- navigator
- 浏览器的信息
- screen
- 屏幕的信息
- location
- url信息
- history
- 前进后退的信息
navigator和screen
js//navigator(判斷使用的是否是谷歌浏览器)但是不是很准确,浏览器的agent可能包含所有的浏览器标识 const ua = navigator.userAgent const isChrome = ua.indexOf('Chrome') console.log(isChrome) //screen(获取屏幕宽度) console.log(screen.width) console.log(screen.height)
location和history
js//location console.log(location.href) //获取整个网址 console.log(location.protocol) // 'http:' 'https:' console.log(location.host) //获取域名 console.log(location.pathname) // 获取路径 '/learn/199' console.log(location.search) //获取查询参数 console.log(location.hash) //获取#内容 //history history.back() //后退 history.forward() //前进
- navigator
第8章 JS-Web-API-事件【事件不会,等于残废,必考!必考!】
本章介绍事件绑定的知识点和题目。包括事件绑定,事件冒泡机制,事件代理。事件能让网页和鼠标、键盘进行交互,初级 JS 面试必考。 共 3 节 (29分钟)
8-1 事件绑定和事件冒泡 (11:59)
题目
- 编写一个通用的事件监听函数
- 描述事件冒泡的流程
- 无限下拉的图片列表,如何监听每个图片的点击?
知识点
- 事件绑定
- 事件冒泡
- 事件代理
事件绑定
js
const btn = document.getElementById('btn1')
btn.addEventListener('click',event=>{
console.log('clicked')
})
定义一个通用事件绑定
js
//通用事件绑定
function bindEvent(elem, type, fn){
elem.addEventListener(type, fn)
}
const a = document.getElementById('link1')
bindEvent(a, 'click', e=>{
e.preventDefault() //阻止默认行为
alert('clicked')
})
event怎么用
- event.traget
- event.preventDefault()
- event.stopPropagation()
- 阻止事件冒泡
js
function bindEvent(elem, type, fn){
elem.addEventListener(type, fn)
}
const a = document.getElementById('link1')
bindEvent(a, 'click', e=>{
console.log(e.target) //打印出a标签的dom元素
e.preventDefault() //阻止默认行为
})
事件冒泡
子元素触发的事件,会一层一层传给父元素
但event.target的元素只有一个就是触发的元素
html
html结构
<body>
<div id="div1">
<p id="p1">
激活
</p>
<p id="p2">
取消
</p>
<p id="p3">
取消
</p>
<p id="p4">
取消
</p>
</div>
<div id="div2">
<p id="p5">
取消
</p>
<p id="p5">
取消
</p>
</div>
</body>
js
<script>
const p1 = document.getElementById('p1')
const body = document.body
bindEvent(p1,'click',e=>{
e.stopPropagation() //注释这行,体会事件冒泡 阻止冒泡
alert('激活')
})
bindEvent(body,'click',e=>{ //给body绑定取消就不用每个都给取消元素绑定了,为什么给body绑定可以代替所有的取消元素呢,因为点击了任何body中的元素,那么就算该元素没有绑定事件,一层层往上传递也可触发body的监听事件
alert('取消')
})
</script>
8-2 什么是事件代理(面试必考) (12:26)
事件代理
应用场景:有很多元素的列表,每个元素都有绑定对应的事件
html
html
<div>
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
</div>
<button>
点击增加一个 a 标签
</button>
js
<script>
const div1 = document.getElementById('div1')
div1.addEventListener('click',e=>{ //通过事件代理的方法就不用单独给每个a标签绑定一个事件 e.target是事件代理的关键
e.preventDefault() //阻止事件默认行为 阻止a标签跳转
const target = e.target
if(e.nodeName === 'A'){
alert(target.innerHTML)
}
})
</script>
优点
- 代码简洁
- 减少浏览器内存占用
- 不要滥用
- 适用在瀑布流每个元素事件监听
定义一个通用事件绑定(支持事件代理)
js
function bindEvent(elem, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
elem.addEventListener(type, event => {
const target = event.target
if (selector) {
// 代理绑定
if (target.matches(selector)) { //matches是dom元素是否符合于一个css选择器
fn.call(target, event) //call.(thisOject, arg1 ,arg2 ...) event是参数
}
} else {
// 普通绑定
fn.call(target, event)
}
})
}
// 普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', function (event) {
// console.log(event.target) // 获取触发的元素
event.preventDefault() // 阻止默认行为
alert(this.innerHTML)
})
// 代理绑定
const div3 = document.getElementById('div3')
bindEvent(div3, 'click', 'a', function (event) { //注意这个不能用箭头函数,因为用箭头函数时this是window
event.preventDefault()
alert(this.innerHTML)
})
bindEvent(div3, 'click', 'a', (event)=> { //如果要使用箭头函数就不要用this,用参数
event.preventDefault()
alert(event.target.innerHTML)
})
8-3 DOM 事件相关的面试题 (03:39)
问题解答
- 编写一个通用的事件监听函数
- 描述事件冒泡的流程
- 基于dom树形结构
- 事件会顺着触发元素往上冒泡
- 应用场景:事件代理
- 无限下拉的图片列表,如何监听每个图片的点击?
- 事件代理
- 用e.target获取触发元素
- 用matches来判断是否是触发元素
第9章 JS-Web-API-Ajax【每个工程师必须熟练掌握的技能】
本章介绍 ajax 相关的知识点和题目。包括 XMLHttpRequest ,同源策略,跨域方式,以及常用插件介绍。我们早就进入了动态网页时代,而当下的前后端分离开发方式,更加要求每个工程师必须熟练掌握 ajax 。 共 6 节 (54分钟)
9-1 ajax 的核心API - XMLHttpRequest (19:23)
ajax
- 题目
- 手写一个简易的ajax
- 跨域的常用实现方式
- 知识点
- XMLHttpRequest
- 状态码
- 跨域:同源策略,跨域解决方案
- 解答
XMLHttpRequest
js
//get请求
const xhr = new XMLHttpRequest()
xhr.open("GET","/data/test.json",false) // /data/test.json是请求地址 false是异步请求
xhr.onreadystatechange = function(){ //监听返回结果
//这里的函数异步执行,可参考之前js基础中的异步模块
if(xhr.readyState === 4){
if(xhr.status === 200){
alert(xhr.responseText)
console.log(
JSON.parse(xhr.responseText) //将字符串转换为json对象
)
}else{
console.log("其他情况")
}
}
}
xhr.send(null) //发送数据,因为此时是get请求无需发送数据
//post请求
const xhr = new XMLHttpRequest()
xhr.open("POST","/login",true) // /data/test.json是请求地址 true是异步请求
xhr.onreadystatechange = function(){ //监听返回结果
//这里的函数异步执行,可参考之前js基础中的异步模块
if(xhr.readyState === 4){
if(xhr.status === 200){
alert(xhr.responseText)
console.log(
JSON.parse(xhr.responseText) //将字符串转换为json对象
)
}else if(xhr.status === 404){
console.log("404 not found")
}else{
console.log("其他情况")
}
}
}
const postData = {
userName: 'zhangsan',
password: 'xxxx'
}
xhr.send(JSON.stringify(postData)) //将对象转为字符串 发送数据
xhr.readyState状态的含义
- 0-(未初始化)还没有调用send()方法
- 1-(载入)已调用send()方法,正在发送请求
- 2-(载入完成)send()方法执行完成,接受到全部响应内容
- 3-(交互)正在解析响应内容
- 4-(完成)响应解析完成,可以在客户端调用
xhr.status
- 2xx-表示成功处理请求,如200
- 3xx-需要重定向,浏览器直接跳转,如301(永久跳转) 302(临时跳转) 304(缓存)
- 4xx-客户端请求错误,如404 403
9-2 什么是浏览器的同源策略 (08:57)
跨域
- 什么是跨域(同源策略)
- jsonp
- cors(服务端支持)
同源策略
- ajax请求时,浏览器要求当前网页和server必须同源(安全)
- 同源: 协议, 域名, 端口 三者必须一致
- 前端:
http://a.com:8080
;serve:http://b.com/api/xxx
加载 图片 css js可无视同源策略
<img src="跨域的图片地址">
- 图床
<img />
可以用于统计打点,可使用第三方统计服务
<link href=跨域的css地址>
- cdn
- cdn一般是外域
<script src="跨域的js地址"></script>
- cdn
- jsonp
总结
- 所有的跨域都必须经过server端允许和配合
- 未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
9-3 实现跨域的常见方式 - jsonp 和 CORS (10:32)
jsonp
- 访问http://imooc.com,服务端一定返回个html文件吗?
- 服务器可以任意动态拼接数据返回,只要符合hrml格式要求
- 同理
<script src="http://imooc.com/getData.js">
<script>
可以绕过跨域限制- 服务器可以任意动态拼接数据返回
- 所以,
<script>
可以获得跨域的数据,只要服务端愿意返回
本地请求jsonp演示
html
<script>
//預先定义好需要执行的函数,等待jsonp传递回来函数,就执行
window.abc = function (data) {
console.log(data)
}
</script>
//jsonp
<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
//jsonp传递回来的函数 表示abc函数立即执行
abc(
{ name: 'xxx' }
)
jquery实现jsonp
js
$.ajax({
url: 'http://localhost:8002/jsonp.js',
dataType: 'jsonp',
jsonpCallback: 'callback',
success: function(data){
console.log(data)
}
})
cors-服务器设置http header
js
//第二个参数填写允许跨域的域名称,不建议写 '*'
response.setHeader("Access-Control-Allow-Origin",'http://localhost:8011');
response.setHeader("Access-Control-Allow-Headers",'X-Requested-With');
response.setHeader("Access-Control-Allow-Methods",'PUT,POST,GET,DELETE,OPTIONS');
//接受跨域的cookie
response.setHeader("Access-Control-Allow-Credentials","true");
9-4 ajax 相关的面试题 - part1 (06:26)
题目
- 手写一个简易的ajax
- 跨域的常用实现方式
js
function ajax(url){
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest()
xhr.open("GET",url,true) // /data/test.json是请求地址 true是异步请求
xhr.onreadystatechange = function(){ //监听返回结果
//这里的函数异步执行,可参考之前js基础中的异步模块
if(xhr.readyState === 4){
if(xhr.status === 200){
alert(xhr.responseText)
console.log(JSON.parse(xhr.responseText)) //将字符串转换为json对象
}else if(xhr.status === 404){console.log("404 not found")
}else{console.log("其他情况")
}
}//end function
xhr.send(null); //必须要写,不然没有请求
}) //end promise
return p
}
//ajax使用
const url = '/data/test.json';
ajax(url)
.then(res=>console.log(res))
.catch(err=>console.log(err))
9-5 ajax 本章相关的面试题 - part2 (02:40)
跨域实现方式
- jsonp
- cors
知识点
- XMLHttpRequest
- 状态码: readyState status
- 跨域:同源策略(如何绕过),JSONP,CROS
9-6 实际项目中 ajax 的常用插件 (05:58)
jquery的ajax
js
$(function(){
//请求参数
var list = {};
//
$.ajax({
//请求方式
type : "POST",
//请求的媒体类型
contentType: "application/json;charset=UTF-8",
//请求地址
url : "http://127.0.0.1/admin/list/",
//数据,json字符串
data : JSON.stringify(list),
//请求成功
success : function(result) {
console.log(result);
},
//请求失败,包含具体的错误信息
error : function(e){
console.log(e.status);
console.log(e.responseText);
}
});
});
//$(function(){ }是什么意思
$(function(){
// do something
});
等价于
$(document).ready(function(){
//do something
})
等价于
$().ready(function(){
//do something
})
这个就是jq ready()的方法就是Dom Ready,他的作用或者意义就是:在DOM加载完成后就可以可以对DOM进行操作。
一般情况先一个页面响应加载的顺序是:域名解析-加载html-加载js和css-加载图片等其他信息。
fetch
js
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
axios
js
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
第10章 JS-Web-API-存储【内容虽然不多,但不可不会】
本章介绍存储的知识点和题目。包括 cookie、localStorage 和 sessionStorage 。本章内容虽然不多,但不可不会。 共 2 节 (13分钟)
10-1 如何理解 cookie (08:15)
题目
- 描述cookie localStorage sessionStorage区别
知识点
- cookie
- localStorage和sessionStorage
cookie
- 本身用于浏览器和server通讯
- 被借用到本地存储来
- 可用document.cookie = '..'来修改
js
//在控制台上输入
console.log(document.cookie) //返回cookie字符串 ""
document.cookie = 'a=100';
document.cookie = 'b=200';
document.cookie = 'a=300';//必须一个一个加cookie,否则会自动截取
console.log(document.cookie) //"b=200; a=300"
cookie的缺点
- 存储大小,最大存储是4kb
- http请求时需要发送到服务端,增加请求数据量
- 本来cookie就是为了浏览器和server通讯,只不过是在h5以前没有本地存储方式,只能借用cookie来存储
- 只能用document.cookie= "..."来修改,太简陋
10-2 localStorage SessionStorage 和 cookie 的区别 (04:14)
- HTML5专门为存储而设计的,最大可存5M
- API简单易用setItem getItem
- 不会随着http请求被发送出去
js
localStorage.setItem('a',100)
localStorage.getItem('a')
sessionStorage.setItem('b',200)
sessionStorage.getItem('b')
localStorage 和 sessionStorage
- localStorage数据会永久存储,除非代码或手动删除
- sessionStorage数据只存在于当前会话,浏览器关闭则清空
- 一般用localStorage会更多一些
描述cookie localStorage sessionStorage区别
- 容量
- API易用性
- 是否跟随http请求发送出去
第11章 http 面试题【前后端分离的时代,网络请求是前端的生命线】
前端工程师做出网页,需要通过网络请求向后端获取数据,因此 http 协议是前端面试的必考内容。本章讲解 http 协议常考的知识点,如状态码、header、method、缓存等。特别是 http 缓存策略,非常重要。共 10 节 (91分钟)
11-1 http的几个面试题 (04:22)
http的重要性
- 前端工程师开发界面
- 需要调用后端接口,提交/获取 数据--http协议
- 要求事先掌握好ajax
题目
- http常见状态码有哪些
- http常见的header有哪些
- 什么是Restful API
- 描述一下http缓存机制(重要)
11-2 http常见的状态码有哪些(10:00)
知识点
- 状态码分类
- 1** 服务器收到请求
- 2** 请求成功 如200
- 3** 重定向 如302 这里不行,其他行
- 4** 客户端错误 如404
- 5** 服务端错误 如500
- 常见的状态码
- 200 成功
- 301 永久重定向 配合location浏览器自动处理(不会再访问这个地址)
- 如老域名到期了,永久重定向到新域名
- 302 临时重定向 配合location浏览器自动处理
- 短链接
- 百度搜索
- 304资源未被修改
- 请求过了,再请求一遍缓存
- 404资源未找到
- 403没有权限
- 500服务器错误
- 504网关超时
- 服务器内部问题
- 关于协议和分类
- 就是一个约定
- 要求大家都跟着执行
- 不要违反规范
11-3 什么是Restful-API (11:46)
http methods
- 传统的methods
- 现在的methods
- Restful API
传统的methods
- get 获取服务器的数据
- post像服务器提交数据
- 简单的网页功能,就这两个操作
现在的methods
- get 获取数据
- post 新建数据
- patch/put 更新数据
- delete删除数据
Restful API
- 一种新的api设计方法(早已推广使用)
- 传统api设计:把每个url当作一个功能
- Restful API:吧每个url当做唯一的资源
如何设计成一个资源
- 尽量不用url参数
- 传统api设计: /api/list?pageIndex=2
- Restful API设计: /api/list/2
- 用method表示操作类型
- 传统api设计
- post请求 /api/create-blog
- post请求 /api/updat-blog?id=100
- get请求 /api/get-blog?id=200
- Restful API设计
- post请求 /api/blog
- patch请求 /api/blog/100
- get请求 /api/blog/100
- 传统api设计
11-4 http哪些常见header (12:54)
http headers(请求头和响应头)
- 常见的Request Headers
- 常见的Response Headers
- 演示
Request Headers
- Accept浏览器可以接受的数据格式
- Accept-Encoding 浏览器可接受的压缩算法 如gzip
- Accept-Languange 浏览器可接受的语言 如zh-CN
- Connection: keep-alive 一次TPC连接可重复使用
- cookie
- HOST 请求的域名
- User-Agent(简称 UA)浏览器信息
- Content-type发送数据的格式,如application/json
Response Headers
- Content-type 返回数据格式,如application/json
- Content-length 返回数据大小 多少字节
- Content-Encoding 返回数据的压缩算法 giz
- Set-Cookie 服务端向客户端设置cookie
自定义header
axios可以在header的请求头上加自定义字段
json
headers: {'X-Requested-With': 'XMLHttpRequest'}
缓存相关的headers
- Cache-Control Expires
- Last-Modified If-Modified-Since
- Etag If-None-Match
11-5 http为何需要缓存 (08:21)
http缓存
- 关于缓存的介绍
- http缓存策略(强制缓存+协商缓存)
- 刷新操作方式,对缓存的影响
关于缓存
- 什么是缓存
- 为啥要缓存
- 让请求尽可能减少,加速页面加载
- 哪些资源可以被缓存? -静态资源 (js css img)
- 使用webpack配置静态资源的文件名hash值为固定,可以达到缓存的作用
11-6 cache-control是什么意思-http强制缓存 (10:06)
cache-control的含义
- 初次请求,服务器判断改资源可以被缓存(如css,js,img)Response Headers会带上一个Cache-Control
- 在Response Headers中(由后端控制)
- 控制强制缓存的逻辑
- 例如Cache-Control:max-age=31536000 (单位是秒) 在客户端缓存时间一年
- 在networks中看到请求的size为disk cache表明该请求是缓存
强制缓存
强制缓存成功状况
cache-control的值
- max-age
- 缓存最大过期时间
- no-cache
- 不用强制缓存,交給服务端,服务端怎么判断缓存不管
- no-store(不常见)
- 不用强制缓存,不让服务端做缓存
- private
- 最终用户做缓存
- public
- 中间的代理也可以做缓存
关于Expires
- 同在Response Headers中
- 同为控制缓存过期
- 已被Cache-Control代替
11-7 Etag和Last-Modified是什么意思-http协商缓存 (15:27)
协商缓存(又称对比缓存)
- 服务器端缓存策略
- 服务端来判断一个资源是不是可以被缓存 但不是缓存在服务端,服务端收到请求可以直接返回客户端直接用缓存资源,就不请求了
- 服务器判断客户端资源,是否和服务端资源一样
- 一致则返回304,否则返回200和最新资源
资源标识
- 在ResponseHeaders中,有两种
- Last-Modified资源最后修改时间
- Etag资源的唯一标识(一个字符串,类似人类的指纹)
Last-Modified
Etag
请求成功读取缓存
Last-Modified和Etag
- 会优先使用Etag
- Last-Modified只能精确到秒级
- 如果资源重复生成,内容不变,则Etag更精确
http缓存流程图
11-8刷新页面对http缓存的影响 (05:00)
三种刷新操作
- 正常操作:地址栏输入url,跳转链接,前进后退
- 手动刷新:F5,点击刷新按钮,点击菜单刷新
- 强制刷新:ctrl+F5
不同刷新操作,不同的缓存策略
- 正常操作:强制缓存有效,协商缓存有效
- 手动刷新:强制缓存失效,协商缓存有效
- 强制操作:强制缓存失效,协商缓存失效
小结
- 强制缓存Cache-Control
- 协商缓存Last-Modified和Etag,304状态码
- 完整流程图
14-9 http考点总结 (03:22)
第12章 开发环境
本章介绍开发环境相关的知识点和题目。包括 git ,调试工具,抓包工具,webpack 和 babel ,以及 linux 常用命令。熟练使用开发环境的各个工具,才能证明你真的做过前端开发,真的有项目经验,否则只能被认定为菜鸟小白。
12-1 前端开发常用的开发工具 (04:56)
12-2 什么是 git (05:40)
协助工具
12-3 git 的常用命令有哪些 (09:48)
提交
推送
拉取
先拉后推送
12-4 git 常用命令演示 (14:10)
冲突合并
12-5 如何用 chrome 调试 js 代码 (06:41)
打断点方式
- 在源代码中输入debugger
- 在sources中点击某行,多个断点可以按开始直接跳到下一个断点
12-6 移动端 h5 如何抓包网络请求 (09:57)
抓包
用chorme手机模式
移动端h5页,查看网络请求,需要用工具抓包,没有chorme那么方便,也可以
抓包工具
window
- fiddle
- wireshark
charlse
抓包作用
查看网络请求
网址代理(利用抓包工具实现)
https(不能用抓包工具直接看,因为是加密的,利用抓包工具的ssl proxying就可以)
抓包过程
手机和电脑连同一个局域网
将手机代理到电脑上
手机浏览网页,即可抓包
12-7 如何配置 webpack (17:23)
webpack和babel解决的问题
- es6模块化,浏览器暂不支持
- es6语法,浏览器不支持
- 压缩代码,整合代码,以让网页加载更快
shell
npm init webpack-demo
npm i webpack webpack-cli -D
在文件中新建webpack.config.js
js
module.exports = {
mode: 'development', // production 就可以压缩
entry: path.join(__dirname, 'src', 'index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
}
}
在文件中package.json,新增build
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
},
执行
shell
npm run build
下载插件
shell
npm i html-webpack-plugin -D
npm i webpack-dev-server -D
配置webpack.config.js的启动项目
js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', // production 就可以压缩
entry: path.join(__dirname, 'src', 'index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html'), //找到模板
filename: 'index.html' //产出的文件名
})
],
devServer: {
port: 3000,
static: {
directory: path.join(__dirname, 'dist'),
},
compress: true //压缩
}
}
新增package.json
json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server"
},
12-8 如何配置 babel (08:36)
为了转换es6
下载
shell
npm install @babel/core @babel/preset-env babel-loader -D
新建.babelrc
json
{
"presets": ["@babel/preset-env"] //这是预先写好了许多babel的配置
}
12-9 ES6 模块化规范是什么 (07:11)
a.js 单个导出
js
export function fn(){
console.log('fn')
}
export const name = 'a'
export const obj = {
name: 'zhangsan'
}
b.js 一起导出
js
function fn(){
console.log('fn')
}
const name = 'a'
const obj = {
name: 'zhangsan'
}
//es5导出写法
export {
fn:fn,
name:name,
obj:obj
}
//es6导出写法
export {
fn,
name,
obj
}
c.js 只导出一个
js
写法1
export default {
name : 'xxxx'
}
写法2
const obj = {
name : 'xxx'
}
export default obj
index.js
js
import {fn ,name ,obj} from './a' //es6模块化解构赋值
import {fn ,name ,obj} from './b' //es6模块化解构赋值
import xxx from './c'
fn()
console.log(name)
console.log(obj)
12-10 如何配置 webpack 生产环境 (05:26)
新建webpack.prod.js
js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'production',
entry: path.join(__dirname, 'src', 'index.js'),
output: {
filename: 'bundle.[contenthash].js', //根据代码内容做出文件名变不变
path: path.join(__dirname, 'dist')
},
module: {
rules: [ //配置babel
{
test:/\.js$/, //匹配规则
loader: 'babel-loader',
include: path.join(__dirname,'src'),
exclude: /node_modules/
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html'), //找到模板
filename: 'index.html' //产出的文件名
})
],
}
配置package.json
json
"scripts": {
"build2": "webpack --config webpack.prod.js",
},
12-11 前端用到的 linux 常用命令有哪些 (16:28)
linux命令
- 测试机,为了复现bug
登录测试机
shell
ssh work@192.168.10.21
ls //查看文件
ls -a //查看文件全部
ll //以列表的形式去看
clear //清除屏幕
mkdir abc //创建文件夹
rm -rf abc //删除文件
cd dist //进入文件夹
mv index.html index1.html //修改文件名
mv index1.html ../ //移动到上级目录
cp a.js a1.js //拷贝文件
rm a1.js //删除文件
touch 1.js //新建文件
vim d.js //新建并用vim打开
输入 i 可以写入
输入esc
输入:wq 可保存退出
cat 1.js //输出内容
head 1.js //输出开始内容
tail 1.js //输出末尾内容
grep "babel" package.json // 查找babel关键字在package.json中
12-12 开发环境的考点总结 (01:55)
第13章 运行环境
本章介绍运行环境相关的知识点和题目。包括浏览器加载和渲染机制,性能优化,web 安全。网页在浏览器加载和运行,这些内容必须掌握,也是面试常考。
13-1 JS 上线之后在什么哪里运行? (03:44)
运行环境
- 浏览器中运行
- server中也有nodejs运行
运行过程
下载网页代码,渲染出页面,执行js
保证代码在浏览器中
- 稳定且高效
运行环境涉及到3个方面
- 网页加载过程
- 性能优化
- 安全
13-2 网页是如何加载并渲染出来的 (09:46)
加载过程:
- dns解析:域名->ip地址
- 浏览器根据ip地址向服务器发起http请求
- 服务器处理http请求,并返回给浏览器
渲染过程1:
- 根据html代码生成dom tree
- 根据css代码生成cssom
- 将dom tree和csssom整合行程Render Tree
渲染过程2:
- 根据render tree渲染页面
- 遇到
<script>
则暂停渲染,优先加载并执行js代码,完成再继续 - 直至把Render Tree渲染完成
13-3 网页加载和渲染的示例 (07:32)
示例1:
当没有css时的html(直接渲染)
示例2:
有css的html(渲染dom后渲染cssom)
示例3:
有js的html(为什么会script停止渲染,因为js中可能存在修改dom的语句,要重新渲染)
建议:
将<script>
放在最后
示例4:
html中有img标签(直接渲染dom,再在之后渲染img标签)
window.onload和DOMContentLoaded的哪种方式好:
使用DOMContentLoaded是更好的选择
js
window.addEventListener('load',function(){
//页面的全部资源加载完才会执行,包括图片,视频登
})
document.addEventListener('DOMContentLoaded',function(){
//DOM渲染完即可执行,此时图片,视频还可能没有加载完
})
13-4 网页加载和渲染相关的面试题 (03:10)
- 从输入url到渲染出页面的整个过程
注意两个点:
- 下载资源
- 各个资源下载过程
- 渲染页面
- 结合html css js 图片等
window.onload和DOMContentLoaded的区别
window.onload资源全部加载完才能执行,包括图片
DOMContentLoaded DOM渲染完成即可,图片可能尚未下载
13-5 前端性能优化有哪些方式 (11:52)
没有标准答案,但要求尽量全面
某些细节可能单独提问:手写防抖,节流
只关心核心点
性能优化原则
- 多使用内存,缓存或其他方法
- 减少cpu计算量,减少网络加载耗时
- 用空间换时间
从何入手
- 让加载更快
- 减少资源体积:
- 压缩代码
- 减少访问次数:
- 合并代码
- SSR服务器端渲染
- 缓存
- 使用更快的网络
- CDN
- 减少资源体积:
- 让渲染更快
- css放在header,js放在body最下面
- 尽早开始执行js,用DOMContentLoaded触发
- 懒加载(图片懒加载,上滑加载更多)
- 对DOM查询进行缓存
- 频繁DOM操作,合并到一起插入DOM结构
- 节流throttle防抖debounce
13-6 前端性能优化的示例 (09:40)
资源合并(webpack的功能)
缓存(需要配置webpack,让文件名不变)
cdn
ssr:
- ssr指的是服务器端渲染:将网页和数据一起加载,一起渲染.(技术原理就是先前的JSP ASP PHP,现在就是vue React SSR)
- 非ssr(前后端分离):先加载网页,再加载数据,再渲染数据
懒加载:
html
<img id="img1" src="preview.png" data-realsrc="abc.png">
//一开始请求的是假的图片如缩略图,到了页面一定位置后再加载新的图片
<script>
var img1 = document.getElementById('img1')
img1.src = img1.getAttribute('data-realsrc')
</script>
缓存DOM查询:
js
//不缓存DOM查询结果
for(let i = 0; i < document.getElementsByTagName('p').length;i++){
//每次循环,都会计算length,平法进行DOM查询
}
//缓存DOM查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length
for(let i = 0; i < length; i++){
//缓存length,只进行一次dom查询
}
多个dom操作一起插入都dom结构中
js
const listNode = document.getElementById('list')
//创建一个文档片段,此时还没有插入到DOM数中
const frag = document.createDocumentFragment()
//执行插入
for(let x = 0; x < 10; x++){
const li = document.createElement("li")
li.innerHTML = "List item " + x
frag.appendChild(li)
}
//都完成后再插入DOM树
listNode.appendChild(frag)
尽早开始js执行
js
window.addEventListener('load',function(){
//页面的全部资源加载完才会执行,包括图片,视频登
js代码
})
document.addEventListener('DOMContentLoaded',function(){
//DOM渲染完即可执行,此时图片,视频还可能没有加载完
js代码
})
13-7 手写防抖 debounce (15:37)
指频发操作最后才触发
应用场景
- 监听一个输入框,文字变化后触发change事件
- 直接用keyup事件,则会频频触发change事件
- 防抖:用户输入结束或暂停时,才会触发change事件
没有进行防抖的监听操作
js
const input1 = document.getElementById('input1')
input1.addEventListener('keyup',()=>{
//模拟触发change事件
console.log(input1.value)
})
防抖的监听操作
js
const input1 = document.getElementById('input1')
let timer = null
input1.addEventListener('keyup',()=>{
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
//模拟触发change事件
console.log(input1.value)
//清空定时器
timer = null
},500)
})
封装debounce 函数,可以让多个input使用
js
const input1 = document.getElementById('input1')
function debounce(fn,delay=500){
//timer是在闭包中,这样就隐藏了,外部拿不到
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn()//传入参数的情况下fn.apply(this,arguments)
timer = null
},delay)
}
}
input.addEventListener('keyup',debounce(function(){
console.log(input1.value)
}),600)
13-8 手写节流 throttle (11:25)
节流指频发操作保持一个频率连续触发
应用场景
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置
- 直接用drag事件,则会频频触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每隔100ms触发一次
没有进行节流的拖拽
js
const div1 = documenet.getElementById('div1')
div1.addEventListener('drag',function(e){
console.log(e.offsetX,e.offsetY)
})
进行节流的拖拽
js
const div1 = documenet.getElementById('div1')
let timer = null
div1.addEventListener('drag',function(e){
if(timer)return //看timer是否有值,定时器是否启动
timer = setTimeout(()=>{
console.log(e.offsetX,e.offsetY)
timer = null
},500)
})
将节流封装成函数
js
const div1 = documenet.getElementById('div1')
let timer = null
function throttle(fn, delay = 100){
let timer = null
return function(){
if(timer){
return
}
timer = setTimeout(function(){
fn.apply(this,arguments)
timer = null
},delay)
}
}
div1.addEventListener('drag',throttle(function(e){
console.log(e.offsetX, e.offsetY) //此语句是传到throttle函数,参数要传到返回的函数中需要fn.apply(this,arguments)
}, 200) )
13-9 如何预防 xss 攻击 (06:54)
web安全常见的攻击
- xss攻击
- xsrf攻击
xss攻击原理
一个博客网站,嵌入script,获取cookie
js将获取到的cookie,发送到服务器(服务器配合跨域)
cookie中可能存账号密码
预防xss攻击
替换特殊字符,不让脚本执行
13-10 如何预防 xsrf 攻击 (03:49)
购物中,调用支付接口,xxxx.com/pay/id=200
发一个电子邮件,<img src="xxxx.com/pay/id=10">
这样此时在登录状态就会直接购买了id=10的商品
预防:
- 使用post接口
- 增加验证