2021年前端基础面试题 13-14 存储和http

3种存储

cookie 主要是设计用来做server和浏览器通讯,所以并不适合做本地存储
缺点:最大容量4k,每次http请求都会带上,使用document.cookie =”方式修改!api太简陋
localstorage 和 sessionstorage 每个域名最大5m,不会放入hhtp请求中,使用setItem(使用方便

http状态码

1xx 服务端收到请求
2xx 请求成功
3xx 重定向
4xx 客户端错误
5xx 服务端错误

常见的状态码

200 成功
301 永久重定向
302 临时重定向
304 资源未被修改
404 未找到
403 无权限
500 错误
504 超时

http methods

get 查
post 增
patch put 改
delete 删

restful api

相对于传统的api设计, restful不会把url设计成一个功能,而是一个资源。
例如restful不使用url参数
api/list?index=2
api/list/2

常见的header

request header

accept 接受的数据格式
accept-encoding 接受的压缩格式如gzip
accept-language
connection keep-alive
cookie
host
user-agent
content-type application/json

response header

content-type application-json
content-length
content-encoding 服务器返回的压缩格式
set-cookie 服务器通过这个来设置cookie

http缓存

通常静态资源都会被缓存,比如js,css,images 。
为了避免静态资源被缓存通常在webpack打包时会在静态资源文件名后面加上hash字符串,例如index.3hjdjyu73hd8.js

强制缓存

服务器会在response header之中返回catch-control 来控制缓存的逻辑,例如max-age=3500000(秒)
no-catch 客户端无需缓存
no-store 客户端和服务器都不缓存,不常见
expires 是老标准,已经被catch-control取代

协商缓存

服务端判断请求的资源是否需要缓存,例如发现请求资源没有变化,则返回304告诉客户端无需请求。
服务端通过下发资源标识给客户端,客户端下次访问的时候带上资源标识返回给服务端。服务端再根据资源标识来判断此资源是否过期。
客户端下发资源标识分为两种:
last-modified 资源最后修改时间(秒)
e-tag 唯一标识 优先级最高
对应客户端请求的资源标识
if-modified-since
if-none-match

2021年前端基础面试题 12 AJAX

手写简易的ajax

const xhr = new XMLHttpRequest();
// method:要是用的HTTP方法,url:请求的主体,async(可选):false为同步,true为异步,默认为同步
xhr.open('GET', '/api', false);
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            alert(xhr.responseText);
        };
    };
};
xhr.send(null); //如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null。

xhr.readyState

0 – (未初始化)还没有调用send()方法
1 – (载入) 已调用 send()方法,正在发送请求
2 – (载入完成) send()方法执行完成,已经接受到全部响应内容
3 – (交互)正在解析响应内容
4 – (完成)响应内容解析完成,可以在客户端调用

status 状态码

301 永久重定向
302 临时重定向
301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

304 缓存
404 未找到
403 无权限
5xx 服务端报错

若想设置POST请求版,则需更改

xhr.open里的参数为xhr.open(‘POST’, url, true/false);
xhr.send里的参数为xhr.send(p);,

浏览器同源策略 协议 域名 端口 三者缺一不可

jsonp实现跨越
window.callback=fn
callback({data:1})

2021年前端基础面试题 9-11 DOM BOM

DOM (document object model)
BOM (browser object model)

DOM的数据结构是树型结构

attribute 是修改DOM标签,会改变HTML结构
P.setAttribute(‘style’,’width:100px’)
property 是修改Js对象,性能更好,推荐用这种方式
P.style.width =100px
2者都会触发DOM渲染

插入子节点

divEl.appendChild(P)
注意如果P存在,会变成移动节点

获取父节点

P.parentNode

获取子元素

divEl.childNodes
注意childNodes中会包含nodeType为3的text类型节点,需要过滤出nodeType等于1的b元素标签类型

nodeList转数组

Array.prototype.slice.call(NodeList)

删除子元素

divEl.removeChild(P1)

DOM操作性能优化注意点

将循环插入或修改DOM的操作放入一个临时片段中,遍历完成后再统一插入到容器节点中,说白了就是减少不必要的渲染次数。

判断ua的方法

这个自行在网络上搜索吧,可能随着技术更新每年都不一样

location属性

hash 设置或返回从井号 (#) 开始的 URL(锚)。
host 设置或返回主机名和当前 URL 的端口号。
hostname 设置或返回当前 URL 的主机名。
href 设置或返回完整的 URL。
pathname 设置或返回当前 URL 的路径部分。
port 设置或返回当前 URL 的端口号。
protocol 设置或返回当前 URL 的协议。
search 设置或返回从问号 (?) 开始的 URL(查询部分)。

History 对象属性

length 返回浏览器历史列表中的 URL 数量。

History 对象方法

back() 加载 history 列表中的前一个 URL。
forward() 加载 history 列表中的下一个 URL。
go() 加载 history 列表中的某个具体页面。

如何阻止超链接跳转 e.preventDefault
如何为下拉加载的图片列表添加点击事件, 事件代理

2021年前端基础面试题 8 异步编程

1. event loop 画图说明

  • JS 是单线程运行
  • 异步是基于回调来实现
  • event loop 就是异步回调的实现原理

Call Stack 同步代码

代码执行时放入Call Stack 执行完成后移出

Web APIs 浏览器的接口

setTimeout()是web Api的定时器方法,不是es6的语言
执行setTimeout时,将它放入Web APIs里面等待5秒,后面到第5秒出发时会放入Callback Queue

接下来执行第三行代码打印Bye,同样也是执行时放入Call Stack 执行后移出Call Stack,这时整个代码同步的部分都已经执行完了。

Event Loop 事件轮询

5秒后 Web APIs里面的回调函数被触发,将cb方法放入了 CallBack Queque队列中

Event Loop 轮询CallBack Queue 发现有cb() 则放入Call Stack中执行并移出

cb()方法中有console.log() 则先执行console.log 然后执行完移出,最终再把cb()移出

Promise相关

promise的三种状态

resolved只能触发then,reject只能触发catch,例如下面的代码

Promise.resolve(100).then(data=>{
    console.log(data) // 100
})

catch 中如果正常返回,则后续状态为resolved,否则为rejected

面试题:then和catch的链式调用

Promise.resolve().then(()=>{
    console.log(1) // 1 
    throw new Error('err') // then里面有报错会触发后续的catch
}).catch(()=>{
    console.log(2) // 2 // catch中没有报错会触发后续的then
}).then(()=>{
    console.log(3) // 3
})

async/await

!(async  function (){
await  1
})()

async 返回Promise
await 等于promise的then
await 200 等于 await Promise.resolve(200)

宏任务和微任务

宏任务:settimeout ajax dom事件
微任务:promise await
宏任务在dom渲染后执行,微任务反之
微任务是es6语法 不会放到webapi区域,所以先执行

2021年前端基础面试题 1-7

1 CSS

line-height 的继承机制
父元素行高20px:子元素行高直接继承20px
父元素2或者1.5:子元素行高就是font-size的2倍或者1.5倍
父元素200%:子元素行高是200%乘以父元素的font-size而不是乘子元素的font-size

flex画筛子三

box  {
    display: flex;
    justify-content: sapce-between;
}

item:nth-child(2)  {
    align-self :  center ;
}

item:nth-child(3){
    aligin-self:flex-end;
}

响应式布局

rem vw vh vmax vmin 通过媒体查询的方式动态的设置跟字体的大小

rem的计算机制

(N)rem = html的font-size *( N)

例1:

默认的html的font-size是16px,div的font-size设置为1rem就是16*1=16px
为了便于理解,我们用F表示html的font-size,用R表示rem,用P表示px。
从上面的结果就可以得到公式 P = F * R
F = 16px
R = 1
P = 16 *1
结果P就是16px

例2:

如果html的font-size设置为100px
目标像数要求为16px
那div的font-size设置0.16rem
rem就是目标像数除以根元素
得到公式 R = P / F (rem = 目标px / html的font-size)
第三个公式
F = P / R

2 JS

变量类型,事件机制

值类型,引用类型,手写深拷贝,事件冒泡,事件代理

原型、原型链 、类 、继承

画出原型链的类图,解释supper,es5继承方式

// 构造函数 
function Parent1(name) {
  this.name = name;
  this.age = 12;
}
function Child1() {
  Parent1.call(this,'son');
  this.type = 'child1';
}
console.log( new Child1().name); //son

闭包

作用域,自由变量,手写bind

Function.prototype.myBind = function (){
    const args = Array.prototype.slice.call(arguments)
    const t = args.shift() // 第一项为传入的this
    const that = this
    return function (){
        retrun that.apply(t, args)    
    }
}

实际的应用场景
隐藏数据,比如jquery的自定义事件也是这样隐藏对象数据,只暴露方法

事件流,事件循环

事件流分为3个阶段:
(1)捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;
(2)目标阶段:真正的目标节点正在处理事件的阶段;
(3)冒泡阶段:事件从目标节点自下而上向Document节点传播的阶段。

webpack构建流程

初始化 – 配置
编译 – loader,plugin
输出 – chunk

数组求和

// Accumulator (acc) (累计器)
// Current Value (cur) (当前值)
// Current Index (idx) (当前索引)
// Source Array (src) (源数组)
var sum = [0, 1, 2, 3].reduce(function(accumulator, currentValue) {
  return accumulator + currentValue
}, 100) // 从100开始累加
console.log(sum) // 106

使用promise 封装一个图片加载的方法

function loadImg(src) {
    const p = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img')
            img.onload = () => {
                resolve(img)
            }
            img.onerror = () => {
                const err = new Error('img load err' + src)
                reject(err)
            }
            img.src = src
        }
    )
    return p
}

const url1 = 'xxx1'
const url2 = 'xxx2'
loadImg(usr1).then(img1 => {
    console.log(img1.width)
    return img1
}).then(img1 => {
    console.log(img1.height)
    return loadImg(url2)

}).then(img2 => {
    console.log(img2.width)
})

for of 和for in forEach的区别

for of 可以处理异步的遍历

const nums = [1, 2, 3]

// 先遍历完数组,先执行3次异步调用
nums.forEach(async (i) => {
    const res = await getList(i)
    console.log(res)
})

// 遍历移数组一次,执行一次异步调用,返回结果后再遍历数组下一项
!(async function () {
    for (let i of nums) {
        const res = await getList(i)
        console.log(res)
    }
})()

利用番茄工作法和子弹笔记管理时间

番茄工作法

  • 1个番茄时钟共有30分钟 25分钟工作时间+5分钟休息时间
  • 4个番茄时钟后休息15-30分钟
  • 4个番茄时钟内不可以中断,如果其中一个中断了,就要作废

子弹笔记

第一步 做计划

首先计划好1天中要做的事情
计划要做到很细,比如一本书要分成读完哪几章,又或者说,一个PPT要做完几页
根据优先级的顺序把重要并紧急的任务放到今日待办中

重要并紧急 必做
重要不紧急 做完必做的任务后有时间再做
不重要紧急 不做
不重要不紧急 不做

第二步 设定番茄时钟

记录好每一项子任务需要的番茄时钟数量,最后统计处一天总共用了多少个番茄时钟

第三步 做复盘记录

记录好每天的任务完成情况,比如每个任务用的番茄数量

第四步 分析

哪些任务用的番茄多,哪些用的少,是否有提升空间

第五 可视化

使用图标的方式分析以往的数据,更直观

如何阅读

核心概念

  • 阅读的层次
  • 书籍的分类
  • 心智和成长

第一章 阅读的艺术

知道和理解的区别 – 阅读的目标是为了获得咨询还是为了增进理解?

真正的阅读 — 是仅凭一己之力将书中的概念,从模糊到更清楚理解的一个过程。阅读也是一种理解能力的挑战。

学习 — 是指理解更多的东西,而不是记住更多咨询

指导型学习 — 可以理解为一种辅助型的学习,老师帮助和引导学生进行自我学习。就像医生辅助病人自我康复和农民辅助植物自我生长。

思考 — 主动和被动学习都需要思考,不过主动学习的时候思考的要更多一些。

老师可以直接回答你的问题,而书本不会,需要你自己去寻找答案,就像面对这个世界一样,很多事情需要你自己去寻找答案,老师不会一直都在,所以提升自我学习的能力就是本书的目的。

第二章 阅读的层次

为什么叫层次而不是种类?因为层次是渐进式的。

1. 基础阅读 — 理解为技术性阅读,是否认识那些文字及阅读速度。

2. 检视阅读 — 也叫略读,快速了解本书的类型和重点,比如先看一下章节目录。

3. 分析阅读 — 精读。增进理解和咀嚼消化一本书。本书的重点就是如何进行分析阅读,比如通过问题的方式。

4. 主题阅读 — 也叫比较阅读,读很多相关的书,找到本书的相同及不同之处。最终可以提出一个所有书里都没提出的概念。成为这个领域专家有自己的创新。

第三章 第一层:基础阅读

孩子可能在高中之前都是处于基础阅读的阶段,一般都有位老师在旁边辅助回答一些问题。
所以这个阶段算不上真正的阅读者。

第四章 第二层 检视阅读

首先需要了解这本书是否值的去阅读
一小时内翻阅序章,目录,索引,作者简介。看几页感兴趣的章节。

阅读一本难书的时候,可以跳过不理解的部分,现有个整体认识。
阅读速度要根据目标内容而变化。

在每行字句下快速滑动手指,引导目光的焦点来锻炼阅读的能力,同时也能增加集中力。

第五章 如何做一个自我要求的读者

阅读的核心就是提出问题
主题是什么 找到主题和次要问题
细节 每个概念
有道理吗?
跟你有什么关系?

如何学习

通过问题的形式使用赛博学习法

一、赛博学习法的12个问题

  1. 主要目的 — 有目的才能抓重点提升效率
  2. 写下已知内容 — 知道会什么也就知道了不会什么
  3. 主要内容 — 先有个整体了解看简介和目录然后再整体和细节之间来回穿梭
  4. 预测后面的内容 — 与作者互动,预测是否正确不是重点,而是保持清醒
  5. 抓专业知识 —  5个导读问题帮助更好的理解内容
  6. 提问 — 提出开放式问题帮助你更好的思考,最好的两类问题是假设和联想,能让大脑思考的是提问题而不是做答案
  7. 要点 — 必须要知道重点是什么,有主次之分
  8. 简短总结 — 也是抓住重点的一种方式
  9. 组织和关联 — 形成自己的思维理解方式
  10. 用图表分析 — 加深巩固
  11. 记忆点 — 提炼一些点用于帮助记忆
  12. 新旧知识关联 — 通过联想的方式加深印象

二、关于听课的优缺点

听课相对与自己阅读优点是老师已经吧重点组织好了,缺点是速度是老师控制的,容易跟不上。

关于听课的建议

  1. 预习
  2. 表现出认真的态度
  3. 把问题先记下来,如果老师后面没讲再找机会提问
  4. 记2份笔记 – 把自己的阅读笔记和上课笔记合并起来

三、理解是学习的本质

判断是否理解了xx的方法:

  1. 定义
  2. 举例
  3. 联想

四、总结

学校最应该教的应该是让学生获得独立思考的能力,而不是知识的灌输。

Generator函数设计的意思重点是把异步操作放到yeild表达式中,等调用next之后再执行,也是解决回调地狱的一种方案,其实后面es7的async await更好

生成器函数最基本的用法

// 定义个生成器函数,一般会在function后面加上一个星号来表示
let foo = function* () {
    for (let i = 0; i < 3; i++) {
        yield i
    }
}
// 将生成器函数肤质给一个变量,注意此时生成器函数内部的for循环并不会执行,可以在yield i 上面打印个log测试一下
let f = foo()
// 调用f下的next()方法,最后一次因为循环结束,所以没有值被返回
console.log(f.next()) // {value:0, done:false}
console.log(f.next()) // {value:1, done:false}
console.log(f.next()) // {value:2, done:false}
console.log(f.next()) // {value:undefined, done:true}

Generator不能被当做构造函数的方式被使用,换句话说就是不能被new,会报错foo is not a constructor
yield 关键字只能在Generator函数内部使用。例如将上面的for循环改成forEach则会因为作用域的问题报错

一个最典型的应用场景,写一个函数用来生成7得倍数

function* gen(i = 0) {
    while (true) {
        if (i % 7 === 0) {
            yield i
        }
        i++
    }
}
let g = gen()
console.log(g.next()) // 0
console.log(g.next()) // 7
console.log(g.next()) // 14

应用场景2:解决ajax的回调地狱。
例如有三个接口有依赖关系,常规的写法是嵌套的方式把下一个接口写到回调函数中,使用生成器可以用下面这种写法

function request(url,data = 0) {
    // 使用setTimeout来比喻ajax
    setTimeout(function () { 
        reportData.next(data + 1) // 每次都根据上一次的返回结果去请求下一个接口
    }, 1000)
}

function* gen() {
    let res1 = yield request('api/1')
    console.log(res1) // 1
    let res2 = yield request('api/2',res1)
    console.log(res2) // 2
    let res3 = yield request('api/3',res2)
    console.log(res3) // 3
}

let reportData = gen()
reportData.next() // 开始第一次

Proxy 使用

es5中对对象进行拦截使用 Object.defineProperty(),es6增加了new Proxy来增强代理拦截能力

1. 使用Proxy拦截对象,实现私有属性(下划线开头)禁止访问

let user = {
	name:’xxx’,
	age: 18,
	_gender: ‘male’	
}
user = new Proxy(user,{
	get(target, key) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key]
		}
	},
	set(target, key, val) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key] = val
		}
	}
	has (target, key) {
		if(key[0] === "_") { // key.startsWith(‘_’)
			return false;
		}
			return key in target;
	}
	deleteProperty(key){
		if(key.startsWith(‘_’){
			throw new Error(‘不可删除’)
		} else {
			delete target[key]
			return ture	
		}
	}
	ownKeys(target){
		return Object.keys(target).filter(key => !key.startsWith(‘_’))
	}
})

2. 使用Proxy拦截函数调用

let sum = (…args) =>{
	let num = 0
	args.forEach(item => {
		num += item
	})
	return num
}
sum = new Proxy(sum, {
	// 当前函数,上下文this,参数
	apply(target, ctx, agrs) {
		return target(…args) * 2
	}
})

3. 使用Proxy拦截类的构造函数

let User = class {
	constructor (name){
		this.name = name
	}
}
User = new Proxy(User, {
	// 当前类,参数,目标类
	constructor(target, args, newTarget){
		return new target(…args)
	}
})