前端基础知识艾宾浩斯记忆(anki)
chapter01 变量类型和计算
知识点
- 变量类型
- 变量计算
- truely和falsely变量
变量类型
Q: 基本类型有哪些
A:
- Null
- Boolean
- Number
- String
- Undefined
- Symbol(ES6加入)
Q: 引用类型有哪些?
A:
- 对象
- 数组
- 函数
- Date
- RegExp(正则)
Q: 原始类型与引用类型的区别
A:
大部分语言都可以分为两种类型: 值类型和引用类型,引用类型也是为了提升代码的执行效率
值类型是存在栈内存中的。可以想象下图所示。值类型是不可改变的。也就是复制给一个新的变量的话,新变量改变,老的变量不会改变。

引用类型存在堆栈内存中的。可以想象下图所示,有点相当于地址簿,栈中存储的只是一个内存的地址,真正的数据存在这个内存地址中。引用类型是可以改变的。也就是复制给一个新的变量的话,新变量改变,老的变量也会改变。 
Q: typeof 能判断哪些类型?
A: 基本类型,除了null都能准确识别,null会被识别为Object。 引用类型,只能识别出function,其它都会被识别为Object。
变量计算
Q: 如何进行字符串拼接?
A:
可以使用 + 符号
const a = 10 + 10 // 20
const b = 10 + '10' // '1010'
const c = undefined + 10 //NaN
const d = undefined + '10' // 'undefined100'Q: == 与 === 的区别
A:
== 会进行自动类型转换
===不会自动类型转换
推荐平时使用用 ===
Q: 什么是 falsely 变量?
A:
falsely 变量就是双非之后为 false 的变量
!! NaN === falseif 判断和逻辑运算,都是判断 truely 和 falsely 变量而不是直接判断 true 和 false
例如:
'' && '10' // ''
100 || NaN // 100Q: 怎样区分 truely 和 falsely 变量
A:
以下为 falsely 变量:
- 0
- NaN
- ''
- null
- undefined
- false
其它都是 truely 变量
Q: 深拷贝和浅拷贝的区别
A: 浅拷贝和深拷贝的区别主要是针对引用类型。 如果是深拷贝,复制后的变量改变,不会影响老的变量。 浅拷贝,只是复制地址,复制后的变量改变,也会影响老的变量。
题目
Q: 手写深拷贝
A:
function deepClone(entry) {
let result = null
// step1: 如果入参是值类型就返回
if(typeof entry !== 'object' || entry == null) {
// 递归真正执行的语句
return entry
}
// Step2: 定义result 类型
if(entry instanceof Array) {
result = []
} else {
result = {}
}
// Step3:
for(let key in entry) {
// 对原型的处理
if(entry.hasOwnProperty(key)){
// 此处递归
result[key] = deepClone(entry[key])
}
}
return result
}chapter02 作用域和闭包
知识点
- 编译阶段和执行阶段
- 作用域和自由变量
- 闭包
- this
编译阶段和执行阶段
Q: 编译阶段和执行阶段的区别 js是解释型的语言,代码执行有两个阶段,编译阶段和执行阶段。在执行阶段之前,会先预解析代码,为执行代码做准备。
编译阶段 声明前置 词法作用域 根据规范抛出特定的错误 生成运行时需要的代码。
执行阶段 解析被使用的代码 生成 AST 执行上下文 动态作用域 抛出所有的语法错误
Q: 什么是声明前置?
A: 就是在编译阶段,函数声明和变量声明都会放在最前面。但只是声明放在前面,变量不会赋值,函数不会执行。如果名字相同,函数声明会覆盖变量声明。
例如以下代码:
console.log( a )
var a = 1
function a(){}
console.log( a )可以等价于:
var a
function a(){}
console.log( a ) // function a(){}
a = 1
console.log( a ) // 1作用域
Q: 什么是作用域?
A:
就是变量作用的范围,如图所示即为作用域(这里图要换)

作用域在代码预解析阶段就确定了
Q: 作用域分几种类型?
A:
- 全局作用域
- 函数作用域
- 块级作用域(ES6,let,const)
Q: 什么是暂时性死区?
A: let,const 声明的变量,存在暂时性死区。在当前块级作用域中,let,const 声明的变量,在赋值之前,都不能对该变量进行额外的访问与操作,否则就会报错。(引用)
Q: 什么是自由变量?自由变量的值是什么时候定义?怎么寻找?
A:
- 一个变量在当前作用域没有定义,但被使用了
- 自由变量时在定义函数的时候,值就确定了(一定是在定义的时候,而不是执行的时候)
- 函数在执行的过程中,先从自己内部找变量
- 如果找不到,再从
创建当前函数所在的作用域去找, 以此往上 - 如果找不到,会报错 xx is not defined
闭包
Q: 什么是闭包?
A:
如果在函数里面可以访问外面的变量,那么这个函数 + 这些 变量 = 闭包
闭包在代码解析时就能确定。

Q: 闭包的两种情况
A:
函数作为参数
jsfunction create(fn) { let a = 200 fn() } function fn() { console.log(a) } create(fn) // 200函数作为返回值被返回
jsfunction create() { let a = 100 return function(){ console.log(a) } }
this
Q: this是在什么时候被确定的? A: this 的指向,是在执行上下文的创建过程中,被确定的。
明确了这一点,我们就很容易知道,只有在函数调用时,this 的指向才会被确定。
Q: this的不同场景,怎么取值?
A:
做this题的思路就是转换为.call函数,然后看第一个参数是什么。第一个参数,就是最后执行那个函数的点之前的对象。如果没有,就是Undefied,在严格模式下是undefined,非严格模式下是window
fn() {
console.log(this)
}
相当于 fn.call(undefined) 如果是undefined就是window
fn() // 普通函数,this为window
----------------------------------------------
obj = {
fn: fn
}
obj.fn() // 作为对象方法,this的值为这个对象
----------------------------------------------
const fn2 = fn.bind({a:1})
fn2() // 使用bind/call/apply的话,this即为传入的{a:1}
----------------------------------------------
const b = ()=> {console.log(b)}
obj2 = {
b:b
}
obj2.b()// 箭头函数的this继承父级,这里会打印出window
----------------------------------------------
const c = new fn() // new之后,this会绑定在实例c上Q: call, bind, apply的区别
A: call,bind, apply 都会绑定当前作用域下的this call,apply和Bind的区别的区别是,bind方法会返回一个绑定当前this的新函数,但不会执行,call, apply会执行。
call, apply主要就是传参的格式不同,call是把参数,一个一个传入,apply是通过一个数组传入
题目
Q: bind 方法有什么作用?手写 bind 函数
A:
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被 bind 的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
// 模拟 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)
}
}Q: 实际开发中的闭包场景,举例说明 (引用)
A:
- 隐藏数据
- 做一个简单的cache工具 //TODO: 这是什么意思
Q: 创建10个 <a> 标签,点击的时候弹出对应的对象(引用)
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)
}Q: 以下代码的执行结果(引用)
function create() {
let a = 200
return function(){
console.log(a)
}
}
let fn = create()
let a = 100
fn()A: 200
Q: 以下代码的执行结果(引用)
function print(fn) {
let a = 200
fn()
}
let a = 100
function fn() {
console.log(a)
}
print(fn)A: 100
Q: 以下题目中的 this 指向哪里?
const children = {
watching() {
setTimeout(function(){
console.log(this)
})
}
}A:
这里会打印出 window ,因为在 setTimeout 中,是一个普通函数的执行。
如果想指向这个对象,可以把这个函数变为箭头函数。
Q: 以下函数的执行结果
var app = {
fn1() {
setTimeout(function(){
console.log(this)
}, 10)
},
fn2() {
setTimeout(()=>{
console.log(this)
},20)
},
fn3() {
setTimeout((function(){
console.log(this)
}).bind(this), 30)
},
fn4: ()=> {
setTimeout(()=>{
console.log(this)
},40)
}
}
app.fn1()
app.fn2()
app.fn3()
app.fn4()A:
var app = {
fn1() {
function fn(){
console.log(this)
}
//过10ms 后执行
//fn.call(undefined) ,所以输出 Window
},
fn2() {
//过20ms 执行箭头函数
//箭头函数里面没资格有 自己的 this,借用 setTimeout 外面的 this,也就是 app
},
fn3() {
// 创建了一个新函数,这个新函数里面绑定了 外面的this,也就是 app
// 20 ms 后执行新函数,输出 this,也就是刚刚绑定的 app
}
fn4: ()=> {
//过40ms 执行箭头函数
//箭头函数里面没资格有 this,用 setTimeout 外面的 this
//setTimeout 所在的 fn4也是箭头函数,没资格拥有自己的 this,借用外面的 this ,也就是 Window
}
}chapter03 面向对象
- 原型与原型链
- new
- 继承(原型与class)
知识点
原型
Q: new 的几个阶段
A: 创建一个空对象obj 设置原型链 即 obj . _proto _ = 构造函数.prototype ; 让构造函数中的this指向obj 返回对象obj
Q: instanceof 的作用?
A:
判断属于哪个 class 或者哪个构造函数,可以用来判断数组。
class:
class Variety {}
let variety = new Variety();
alert( variety instanceof Variety ); // true构造函数:
function Variety() {}
alert( new Variety() instanceof Variety ); // trueQ: 简单介绍原型
A:
- 每个 class 都有 显式 原型 prototype 。
- 每个实例都有 隐式 原型
_proto_。 - 实例的
_proto_指向对应 class 的 prototype 。
原型链
Q: 简述原型,原型链的执行规则
A:
- 如果实例中有⾃身属性则直接使⽤⾃身的属性,如果没有则会在⾃身的隐式原型链中去找, ⼀直找到最顶级 Object 为⽌,如果都找不到,则最后返回:
null - 每个构造函数都有⼀个显式原型,且每个显式原型都会对应有⼀个隐式原型,⽽他的隐式原型的⽗级⼜有个显式原型,他们都是⼀级级地往上寻找的,然后⼀直找到树形结构的第⼀层(Object)为终点。
- instanceof是为判断此实例或构造函数是否是⽗级继承的
class
Q: 如何构建一个 class
A:
- constructor
- 属性
- 方法
题目
Q: 如何判断数组类型
A:
instanceof
Q: 如何用 class 实现继承?(简单代码实现)
A:
class Animal() {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} is eating`)
}
}
class Dog extends Animal() {
constructor(name, number) {
super(name)
this.number = number
}
run() {
console.log(`${this.name} is running`)
}
}chapter04 异步与同步
知识点
- 单线程和异步
- 应用场景
- 回调地狱和 Promise
- async/await
- eventHub
- 宏任务和微任务
Q: 同步与异步的区别?
A:
同步会阻塞后面程序的运行, 而异步不会阻塞后面程序的运行。
JS是单线程运行的,只能在同一时间内做一件事,所以JS需要异步
Q: 什么是回调函数?
A: 函数做为参数传入另一个函数,并且在该函数内被调动
例如:
function getImg(callback) {
var imgUrl = 'xxxx'
callback()
}Q: 什么是回调地狱? 怎样解决回调地狱的问题?
A: 主要的原因之一是因为回调地狱是代码嵌套,可以使用promise解决promise问题
例如:
$.post(url1, (data1) =>{
console.log(data1)
$.post(url2, (data2) => {
console.log(data2)
$.post(url3, (data3) => {
console.log(data3)
})
})
})Q: Promise 的基本写法
A:
function getData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 300);
})
}Q: Promise有几种状态
A: 三种:
1.pending // 正在执行 2.resolve // 成功 3.reject // 拒绝
Q: Promise then 和catch 会怎样改变Promise状态?
A:
- pending 不会触发任何 then catch 回调
- 状态变为 resolved 会触发后续的 then 回调
- 状态变为 rejected 会触发后续的 catch 回调
Q: 什么是Promise链?
A: Promise可以链式调用,并且上一个then中return的值,是下一个then的入参 例如
start()
.then(data => {
// promise start
console.log('result of start: ', data);
return Promise.resolve(1); // p1
})
.then(data => {
// promise p1
console.log('result of p1: ', data);
return Promise.reject(2); // p2
})Q: 请简单画出EventHub的流程?
A:
- 将 console.log("Hi") 推入调用栈,调用栈会执行代码
- 执行代码,控制台打印“Hi”,调用栈清空
- 执行 setTimeout,setTimeout由浏览器定义,不是ES6的内容;将定时器放到Web APIs中,到时间后将回调函数放到回调函数队列中
- 执行完了setTimeout, 清空调用栈
- console.log("Bye")进入调用栈,执行,调用栈清空
- 同步代码被执行完,,回调栈空,浏览器内核启动时间循环机制
- 五秒之后,定时器将cb1推到回调函数队列中
- 事件循环将cb1放入调用栈

Q: async/await 的基本用法?
A:
- async/await消灭了异步回调,和Promise相辅相成。
- 执行async 函数,返回的是Promise对象
- await 相当于 Promise 的 then
- try...catch 可以捕获异常,代替了Promise 的 catch
Q: 宏任务与微任务的区别? 宏任务与微任务有哪些?
A:
微任务: 其实是es规定的,就是其实是js代码中的一些异步方法(promise) 宏任务: 是w3c规定的一些方法,也就是浏览器中的一些异步方法(settimeout,ajax)
因为微任务是在DOM渲染之前执行的,而宏任务是在DOM渲染之后执行的,所以先执行微任务再执行宏任务。
Q: 宏任务,微任务题的解题思路
A:
- 先执行同步代码
- 再执行微任务代码(await 后面为微任务)
- 再执行宏任务代码
Q: 代码的执行步骤
A:
- 同步代码
- 执行微任务
- (尝试触发Dom渲染)
- 触发EventLoop,执行宏任务
题目
Q: 手写用Promise 加载一张图片(异步)
A:
function loadImg(src) {
const p = 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
}
)
return p
}Q: 手写promise
A:
class Promise2 {
succeed = null
fail = null
state = 'pending'
constructor(fn) {
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
setTimeout(() => {
this.state = 'fulfilled'
this.succeed(result)
})
}
reject(reason) {
setTimeout(() => {
this.state = 'rejected'
this.fail(reason)
})
}
then(succeed, fail) {
this.succeed = succeed
this.fail = fail
}
}Q: 以下如图所示程序的执行结果: 
A: 1,3, 5, 4, 2
Q: 以下代码的执行结果
Promise.resolve().then(() => {
console.log(1)
}).catch(() => {
console.log(2)
}).then(() => {
console.log(3)
})A: 1,3
Q: 以下代码的执行结果
Promise.resolve().then(() => { // 返回 rejected 状态的 promise
console.log(1)
throw new Error('erro1')
}).catch(() => { // 返回 resolved 状态的 promise
console.log(2)
}).then(() => {
console.log(3)
})A: 1,2,3
Q: 以下代码的执行结果
Promise.resolve().then(() => { // 返回 rejected 状态的 promise
console.log(1)
throw new Error('erro1')
}).catch(() => { // 返回 resolved 状态的 promise
console.log(2)
}).catch(() => { // 这里是catch
console.log(3)
})A: 1,2
Q: promise题分析的一些注意点
A: await 后面记得也是微任务 
初始化promise是,函数会立即执行 
chapter05 JS-Web-API
知识点
DOM
- DOM本质
- DOM节点操作
- DOM结构操作
- DOM性能
Q: DOM的本质
A: 了解HTML首先要描述xml的概念 xml, 是一种可扩展的标志性结构语言,是一棵树 html是一种特定的xml,规定了一些标签的名称 html是树型结构 dom的本质是从Html语言解析出的一颗树 可以认为 DOM 就是 JS 能识别的 HTML 结构,一个普通的 JS 对象或者数组。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>hello world</div>
</body>
</html>Q: 获取节点有哪些方法
A:
const a1 = document.getElementById('a1') // 通过元素id选择
const titleList = document.getElementsByClassName('.title') // 通过class选择
const pList = document.getElementsByTagName('p') // 选择所有p元素。返回的是一个集合
const divList= document.querySelectorAll('div') //TODO这里应该不对 选择所有p元素。返回的是一个集合getElementsByTagName方法返回的是HTMLCollection对象。
querySelectorAll方法返回的是NodeList对象。
两者的区别:
- getElementsByTagName 获取的是动态集合,querySelector获取的是静态集合。
- getElementsByTagName更快
Q; 什么是property?(引用小册)
A:
property: dom是一个对象。通过getxxxxbyxxx方法获取到对象以后,可以通过js修改一些属性,这些属性就是property。property是JS范畴的属性,符合JS的标准 如以下代码,style就是property
var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width) // 获取样式Q: 什么是attribute?(引用小册)
A:
attribute:
property 的获取和修改,是直接改变 JS 对象,而 attribute 是直接改变 HTML 的属性,两种有很大的区别。attribute 就是对 HTML 属性的 get 和 set,和 DOM 节点的 JS 范畴的 property 没有关系。
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'juejin')
p.getAttribute('style')
p.setAttribute('style', 'font-size:30px;')Q: attribute和property的区别(引用课程)
A:
- property:修改对象属性,不会体现到html结构中
- attribute: 修改html属性,会改变html结构
- 两者都有可能引起DOM重新渲染
Q: 新增/插入节点
A:
// 创建节点,然后新增节点
const div = document.getElementById('div')
const span1 = document.createElement('span')
div.appendChild(span1)
// 如果选择已有节点,就是移动节点
const span2 = document.getElementById('span')
div.appendChild(span2)Q: 删除节点
A:
const ele = document.getElementById('title')
const child = ele.childNodes
ele.removeChild(child[0])Q: 获取子元素列表
A:
const ele = document.getElementById('title')
const child = ele.childNodesQ: 获取父元素
A:
const ele = document.getElementById('title')
const parent = ele.parentNodeQ: 优化Dom性能(引用)
- 避免频繁的操作Dom
- 可以对一些需要重复利用的做缓存
- 将频繁操作改为一次性操作
BOM
Q: 怎样获得用户用的哪个浏览器
A:
const uaInfo = navigator.userAgent
const isSafari = uaInfo.indexOf('Safari')Q: 如何获得屏幕的宽和高
A:
const screenWidth = screen.width
const screenHeight = screen.heightQ: location
A:
Q: 怎样让浏览器前进和后退
A:
history.back()
history.forward()题目
Q:一次性插入多个DOM节点,考虑性能
A:
利用document.createDocumentFragment(),把多次的操作转换为一次操作
const fragment = document.createDocumentFragment()
// 把三个节点放入fragment中
fragment.appendChild(span1)
fragment.appendChild(span2)
fragment.appendChild(span3)
// 放完之后,整体放入DOM树中
div.appendChild(fragment)chapter06 事件绑定
知识点
- 事件绑定
- 事件冒泡
- 事件代理
Q: 如何进行事件绑定?
A:
const btn = document.getElementById('button')
btn.addEventListener('click', event => {
alert('clicked')
})Q: 什么是事件冒泡
A: 事件会像冒泡一样,顺着触发元素,一层一层往上冒 就是在上级能监听到,bindEvent其实只是监听 事件冒泡就是说,它的上级元素也能监听到那个事件
事件代理也是应用事件冒泡的机制
Q: 怎样阻止默认操作?
A: 阻止默认事件,例如阻止a的跳转
e.preventDefault // 阻止默认事件Q: 怎样阻止事件冒泡?
A:
e.stopPropagation() // 阻止事件冒泡Q: 什么是事件代理?
A: 事件代理,就是当不好确定事件绑定在哪里的时候, 可以统一绑定在父元素上 然后父元素上要加一些判断和操作, 一般用在瀑布流
它的好处是可以少写一些代码,因为不用每个标签都绑定事件, 所以也可以减少浏览器内存占用
题目
Q: 编写一个通用的事件监听函数
A:
function bindEvent(ele, type, selector, fn) {
// 如果是有三个参数的话,第三个参数就为fn
if(fn == null) {
fn = selector
selector =null
}
ele.addEventListener(type, event => {
const target = event.target
if(selector) {
// 代理绑定,用matched匹配元素
// 用call传入target绑定this
if(target.matches(selector)) {
fn.call(target, event)
} else {
fn.call(target, event)
}
}
})
}Q: 无限下拉的图片列表,如何监听每个图片的点击?
A:
- 使用事件代理
- 通过e.target获取触发元素
- 用matches来判断是否触发元素
chapter07 ajax
知识点
- XMLHttpRequest
- 状态码
- 跨域:同源策略, 跨域解决方案
Q: 实现一个get请求(引用)
A:
const xhr = new XMLHttpRequest()
xhr.open('GET', '/data/test.json', true)
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
console.log(xhr.responseText)
}
}
}
xhr.send(null)Q: 实现一个post请求(引用)
A:
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
console.log(xhr.responseText)
} else {
console.log('其它情况')
}
}
}
const postData = {
userName: 'zhangsan',
password: 'xxx'
}
// 转换为json格式
xhr.send(JSON.stringify(postData))Q: xhr.readyState有哪些状态?
A:
| 值 | 状态 | 描述 |
|---|---|---|
| 0 | UNSENT | (未初始化)代理被创建,但尚未调用 open() 方法。 |
| 1 | OPENED | (载入)open() 方法已经被调用。 |
| 2 | HEADERS_RECEIVED | (载入完成)send()方法已经被调用,并且头部和状态已经可获得。 |
| 3 | LOADING | 下载中; (交互)responseText 属性已经包含部分数据。 |
| 4 | DONE | (完成)下载操作已完成。 |
Q: 请求的状态码有哪些
A:
2xx - 成功请求, 例如:200
3xx - 重定向, 例如:301 永久重定向, 302临时重定向, 304 服务器表示资源未改变,浏览器会用自己本身的缓存的资源
4xx - 客户端错误,例如:404找不到,403客户端没有权限
5xx - 服务器端错误
Q: 什么是跨域?
A:
想要理解跨域,就要理解同源。 同源的意思是: 协议, 域名, 端口, 三者一致(必须在一个域下) ajax请求时,浏览器要求当前网页和server必须同源。 当不是同源的时候,需要使用跨域去解决这个问题
现在所有的跨域,都必须服务端的允许和配合, 未经服务端允许就实现跨域,说明有漏洞
Q: 有哪些标签可以无视同源策略? 有什么作用
A:
注意: 加载图片,css,js可无视同源策略
<img src='跨域的图片地址' />, 可用于统计打点,使用第三方统计服务<ilnk href=‘跨域的css地址’ />, 可使用CDN, CDN一般是外域<script src='跨域的js地址'></script>,可实现JSONP
Q: JSONP的原理?(引用)
A:
<script> 可以绕过跨域限制- 服务器可以任意动态拼接数据返回
其实就是引用一个外部的script, 但是这个script是服务器拼接的,这个拼接的js中一般是有一个函数,这个函数返回需要的数据。这样在页面的就能调用这个函数拿到数据了。
Q: cors是怎样解决跨域问题的?
A: cors就是服务端在返回的header里面加一下标记,这样来实现跨域
// 可以直接写为*
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");Q: 什么是Restful Api,与之前的method有什么区别?
A: Restful API 是一种新的API设计方法
- 传统API把每个url当做一个功能
- Restful API,把url当做唯一的资源,用不同的请求方法表示不同的含义 *


- delete表示删除 Q: http 有哪些headers?
A: request Headers Accept 浏览器可接受的数据格式 Accept-encoding 浏览器可接受的压缩算法,如gzip(服务器压缩,客户端解压,让传输的资源变小,速度更快) Accept-Language 浏览器可接收的语言,如zh-CN Connect:keep-alive 一次TCP连接重复使用 Cookie:同域每次请求资源都会把cookie带上 Host:请求的域名 User-agent:简称UA,浏览器信息,标识是什么浏览器,是什么系统,供给服务器分析 Content-type:发送数据的格式,如application/json(一般get请求是没有的)
Response Headers Content-type: 返回数据的格式,如application/json Content-length: 返回数据的大小,多少字节 Content-Encoding: 返回数据的压缩算法,如gzip Set-Cookie: 服务端需要通过该字段来修改浏览器中的cookie
Q: 为什么需要HTTP缓存?
A: ① 缓存是什么? 保存资源副本并在下次请求时直接使用该副本的技术。当Web缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。 ② 为什么需要缓存 减少不必要的网络请求,使得页面加载更快; 网络请求是不稳定,加大了页面加载的不稳定性; 网络请求的加载相比于cpu加载 & 页面渲染都要慢. ③ 哪些资源可以被缓存? 静态资源 js css img , 因为静态资源加上hash名打包后是不会修改的
Q: 什么是强制缓存?
A: 服务器觉得资源可以被缓存才会加Cache-Control (1) Cache-Control 在Response Headers 中,由服务器添加,客户端不可设置; 控制强制缓存的逻辑,再次请求发现有本地缓存,不使用网络,直接返回资源 max-age : 资源在多少秒内有效 no-cache : 不使用强制缓存,由服务端决定do what no-store : 不使用强制缓存,服务端也不缓存,直接由服务端返回新资源 private : 只允许最终用户缓存 pubilc : 用户和中间的代理都能缓存资源 

Q: 什么是协商缓存?
A: 




题目
Q: 手写一个简易的ajax
A:
function ajax(url) {
const p = new Promise((resolve, reject) => {
const xhr = XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
resolve(
JSON.parse(xhr.responseText)
)
} else if (xhr.status === 404) {
reject(new Error('404 not found'))
}
}
}
xhr.send(null)
})
return p
}Q: 跨域的常用实现方式
A:
Jsonp cors
Q: 简单实现一个Jsonp
A:
存储
性能优化
vue
Q: watch 和 computed 和 methods 区别是什么?
A: a.computed 和 methods 相比,最大区别是 computed 有缓存:如果 computed 属性依赖的属性没有变化,那么 computed 属性就不会重新计算。methods 则是看到一次计算一次。 b.watch 和 computed 相比,computed 是计算出一个属性(废话),而 watch 则可能是做别的事情(如上报数据)
Q: v-show与v-if区别是什么
A: v-if 是动态添加,当值为false 时,是完全移除该元素,即dom 树中不存在该元素。 v-show 仅是隐藏/ 显示,值为false 时,该元素依旧存在于dom 树中。 若其原有样式设置了display: none 则会导致其无法正常显示。
Q: 列表遍历时key有什么用
A: 我的理解就是,使用key的话,等于给元素加了另一个唯一的标识,相当于增加了一个维度。这样,虚拟DOM在做diff的时候就能更快速的比较出哪里做了改变,用最短的路径,来改变元素的排列。 下面是答案: key的特殊attribute主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。如果不使用key, Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用key时,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。 有相同父元素的子元素必须有独特的key。重复的key会造成渲染错误。
Q: Vue 有哪些生命周期钩子函数?分别有什么用?
A: 创建-> 挂载-> 更新-> 销毁 • beforeCreate在实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用。 • created在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测(data observer), property和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el property目前尚不可用。 • beforeMount在挂载开始之前被调用:相关的render函数首次被调用。 • mounted实例被挂载后调用,这时el被新创建的vm.$el替换了。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.Sel也在文档内。 • beforeUpdate数据更新时调用,发生在虚拟DOM打补丁之前。这里适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器。 • updated由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。 • beforeDestroy实例销毁之前调用。在这一步,实例仍然完全可用。 • destroyed实例销毁后调用。该钩子被调用后,对应Vue实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。 
Q: vue如何实现组件间通信
A:
- 父子组件:使用 v-on 通过事件通信
- 爷孙组件:使用两次 v-on 通过爷爷爸爸通信,爸爸儿子通信实现爷孙通信
- 任意组件:使用 eventBus = new Vue() 来通信,eventBus.$on 和 eventBus.$emit 是主要API
- 任意组件:使用 Vuex 通信
Q: data为什么是函数?
A: 组件要复用,每个组件使用的数据要隔离。用函数返回数据则保证每个组件使用的数据都是新的。
Q: Vue数据响应式怎么做到的?
A: Vue的响应式是通过Object.defineProperty对数据进行劫持,并结合观察者模式实现。 Vue利用Object.defineProperty创建一个observer来劫持监听所有的属性,把这些属性全部转为getter和setterₒ Vue中每 个组件实例都会对应一个watcher实例,它会在组件渲染的过程中把使用过的数据属性通过getter收集为依赖。之后当依赖项 的setter触发时,会通知watcher,从而使它关联的组件重新渲染。 Vue不能检测到对象属性的添加或删除,解决方法是手动调用Vue.set或者this.$set
Q: Vuex怎么用的?
A: Vuex是一个声为Vue.js应用程序开发的状态管理器 State :全局数据 Getter:由State的变化派生的数据 Mutation:对State的直接修改 Action: Action提交的是mutation,而不是直接变更状态。可以包含任意异步操作。 Module:将整个状态管理划分更细的粒度。
Q: VueRouter你怎么用的?
A: Vue Router是Vue.js官方的路由管理器。用在单页应用里。 原理: Hash模式,监听hash的change,决定渲染对应的模块 History模式,HTML5的API: pushState和replaceState,这两个API可以改变url的同时不会发送请求。这样就可以监听url 变化来实现更新页面部分内容的操作。 说出核心概念的名字和作用:History模式/导航守卫/路由懒加载 说出常用 API: router-link/router-view/this.$router.push/this.$router.replace/this.$route.params this.$router.push(7user-admin') this.$route.params
React
// TODO 其实这里可以用自己的话再写一遍,这样就是一篇文章 Q: React的生命周期
A:
Mounting:已插入真实 DOM
componentWillMount 在渲染前调用,在客户端也在服务端。
componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。 Updating:正在被重新渲染
shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。
componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。 Unmounting:已移出真实 DOM
componentWillUnmount在组件从 DOM 中移除之前立刻被调用。
Q: 为什么setSate有异步更新 A: React在执行setState之后,要执行render、diff、更新DOM等一系列操作,性能开销是比较大的。加入异步更新、更新合并等策略能优化性能。
组件里等事件处理程序,如 onClick={this.handleClick} 里面等setState是异步更新。声明生命周期函数里等setState也是异步更新。如果需要多次更新需要用异步设置等语法,如
this.setState((state, props) => {
return {count: state.count + props.count }
})其他地方,如setTimeout里,对原生绑定如 addEventListener 里,都是同步更新。
setState函数的第二个参数允许传入回调函数,在状态更新完毕后进行调用,譬如:
this.setState({
load: !this.state.load,
count: this.state.count + 1
}, () => {
console.log(this.state.count);
console.log('加载完成')
});Q: 受控组件与非受控组件是什么
A:
<FInput value={x} onChange={fn}/> 受控组件
<FInput defaultValue={x} ref={input}/> 非受控组件区别受控组件的状态由开发者维护,非受控组件的状态由组件自身维护(不受开发者控制),用ref获取组件的值
Q: shouldComponentUpdate有什么用?
A: 用于在没有必要更新 UI 的时候返回 false,以提高渲染性能
Q: 必考:React 如何实现组件间通信?
A:
1. 父子靠 props 传函数 2. 爷孙可以穿两次 props 3. 任意组件用 Redux(也可以自己写一个 eventBus) Q: 必考:shouldComponentUpdate 有什么用?
A:
1. 要点:用于在没有必要更新 UI 的时候返回 false,以提高渲染性能 2. 参考:harettp://taobaofed.org/blog/2016/08/12/optimized-react-components/
Q: 必考:虚拟 DOM 是什么?
A:
1. 要点:虚拟 DOM 就是用来模拟 DOM 的一个对象,这个对象拥有一些重要属性,并且更新 UI 主要就是通过对比(DIFF)旧的虚拟 DOM 树 和新的虚拟 DOM 树的区别完成的。 2. 参考:http://www.alloyteam.com/2015/10/react-virtual-analysis-of-the-dom/
Q: 必考:什么是高阶组件?
A:
1. 要点:文档原话——高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。 2. 举例:React-Redux 里 connect 就是一个高阶组件,比如 connect(mapState)(MyComponent) 接受组件 MyComponent,返回一个具有状态的新 MyComponent 组件。
Q: React diff 的原理是什么?
A:
看你记忆力了:https://imweb.io/topic/579e33d693d9938132cc8d94
Q: 必考 Redux 是什么?
A:
1. 背下文档第一句:Redux 是 JavaScript 状态容器,提供可预测化的状态管理。重点是『状态管理』。 2. 说出核心概念的名字和作用:Action/Reducer/Store/单向数据流 3. 说出常用 API:store.dispatch(action)/store.getState()
5. Redux
Redux 是一个 数据管理中心,可以把它理解为一个全局的 data store 实例。它通过一定的使用规则和限制,保证着数据的健壮性、可追溯和可预测性。它与 React 无关,可以独立运行于任何 JavaScript 环境中,从而也为同构应用提供了更好的数据同步通道。
核心理念:
- 单一数据源: 整个应用只有唯一的状态树,也就是所有 state 最终维护在一个根级 Store 中;
- 状态只读: 为了保证状态的可控性,最好的方式就是监控状态的变化。那这里就两个必要条件:
- Redux Store 中的数据无法被直接修改;
- 严格控制修改的执行;
- 纯函数: 规定只能通过一个纯函数 (Reducer) 来描述修改;
大致的数据结构如下所示:

理念实现:
- Store: 全局 Store 单例, 每个 Redux 应用下只有一个 store, 它具有以下方法供使用:
getState: 获取 state;dispatch: 触发 action, 更新 state;subscribe: 订阅数据变更,注册监听器;
js// 创建 const store = createStore(Reducer, initStore)- Action: 它作为一个行为载体,用于映射相应的 Reducer,并且它可以成为数据的载体,将数据从应用传递至 store 中,是 store 唯一的数据源;
js// 一个普通的 Action const action = { type: 'ADD_LIST', item: 'list-item-1', } // 使用: store.dispatch(action) // 通常为了便于调用,会有一个 Action 创建函数 (action creater) funtion addList(item) { return const action = { type: 'ADD_LIST', item, } } // 调用就会变成: dispatch(addList('list-item-1'))- Reducer: 用于描述如何修改数据的纯函数,Action 属于行为名称,而 Reducer 便是修改行为的实质;
js// 一个常规的 Reducer // @param {state}: 旧数据 // @param {action}: Action 对象 // @returns {any}: 新数据 const initList = [] function ListReducer(state = initList, action) { switch (action.type) { case 'ADD_LIST': return state.concat([action.item]) break defalut: return state } }注意:
- 遵守数据不可变,不要去直接修改 state,而是返回出一个 新对象,可以使用
assign / copy / extend / 解构等方式创建新对象; - 默认情况下需要 返回原数据,避免数据被清空;
- 最好设置 初始值,便于应用的初始化及数据稳定;
- Store: 全局 Store 单例, 每个 Redux 应用下只有一个 store, 它具有以下方法供使用:
Q: connect 的原理是什么?
A:
react-redux 库提供的一个 API,connect 的作用是让你把组件和store连接起来,产生一个新的组件(connect 是高阶组件) 参考:https://segmentfault.com/a/1190000017064759
webpack 相关
Q: webpack的一些关键概念:
A: JavaScript 的 模块打包工具 (module bundler)。通过分析模块之间的依赖,最终将所有模块打包成一份或者多份代码包 (bundler),供 HTML 直接引用。实质上,Webpack 仅仅提供了 打包功能 和一套 文件处理机制,然后通过生态中的各种 Loader 和 Plugin 对代码进行预编译和打包。因此 Webpack 具有高度的可拓展性,能更好的发挥社区生态的力量。
Entry: 入口文件,Webpack 会从该文件开始进行分析与编译; Output: 出口路径,打包后创建 bundler 的文件路径以及文件名; Module: 模块,在 Webpack 中任何文件都可以作为一个模块,会根据配置的不同的 Loader 进行加载和打包; Chunk: 代码块,可以根据配置,将所有模块代码合并成一个或多个代码块,以便按需加载,提高性能; Loader: 模块加载器,进行各种文件类型的加载与转换; Plugin: 拓展插件,可以通过 Webpack 相应的事件钩子,介入到打包过程中的任意环节,从而对代码按需修改;
Q: 有哪些常见的Loader?他们是解决什么问题的?
A: file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去 source-map-loader:加载额外的 Source Map 文件,以方便断点调试 image-loader:加载并且压缩图片文件 babel-loader:让babel来处理最新的js(ts、jsx)语法 css-loader:加载 CSS,支持模块化、压缩、文件导入等特性 style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
Q: 有哪些常见的Plugin?他们是解决什么问题的?
A: HtmlWebpackPlugin: 用于创建最终使用的HTML文件。可自动生成,也可以使用模版创建。 UglifyjsWebpackPlugin: 用于压缩js文件 TerserWebpackPlugin:用于压缩js,更新,支持es6语法 ExtractTextWebpackPlugin: 将所有的入口 chunk中引用的 *.css,移动到独立分离的 CSS 文件
Q: Loader 和 Plugin 有什么差别
A: Loader直译为"加载器"。Webpack将一切文件视为模块,Loader让Webpack拥有了加载和解析非JavaScript文件的能力。 Plugin直译为"插件"。Plugin可以扩展Webpack的功能。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
Q: 如何提高Webpack的构建速度
A: 优化 loader 配置,配置test 、 include 、 exclude terser-webpack-plugin,开启缓存和多进程压缩 thread-loader 为每个loder开辟单独的进程 cache-loader 给loader设置缓存