框架相关
原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。现阶段较热门是React、Vue两大框架,两者工作原理上存在共通点,也存在一些不同点,对于校招来说,不需要两个框架都学得特别熟,一般面试官会针对你简历中写的框架进行提问。
在框架方面,生命周期、钩子函数、虚拟DOM这些基本知识是必须要掌握的,在学习的过程可以结合框架的官方文档
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
Vue框架
知识要点:
1. vue-cli工程
2. vue核心知识点
3. vue-router
4. vuex
5. http请求
6. UI样式
7. 常用功能
8. MVVM设计模式
React框架
知识要点:
1. 基本知识
2. React 组件
3. React Redux
4. React 路由
函数式:what:传入需要处理什么,但只关注结果,无法控制过程,不能中途打断退出,由于会做一些和循环无关的操作,性能也会消耗
命令式:how:对过程控制精细,无关代码少,性能比较高
for in
for in
:性能极差,速度比forEach慢很多,功能:迭代当前对象中所有可枚举的属性(私有属性大部分可枚举,公有属性部分可枚举(原型链))
for(let key in arr){}
特点:
- 遍历顺序以数字key优先
- 无法遍历Symbol属性key(解决办法:
if(typeof Symbol !== "undefined") Object.keys(obj).concat(Object.getOwnPropertySymbols(obj))
获取到obj中所有key) - 可以遍历到共有可枚举(解决办法:
if(!obj.hasOwnProperty(key)) break;
)
for of
迭代器:代表一种规范,实现了该规范就会有属性:Symbol.iterator
for of
原理是按照迭代器规范来遍历的
数组、Set、Map实现了,Object没有实现不可被迭代
原理:在需要迭代的对象上获取Symbol.iterator
属性,该属性对应一个函数,调用该函数返回一个对象,该对象包含next属性,是一个函数,每次迭代,调用next属性,返回一个对象:{done:false,value:xxx}
如果迭代完毕返回{done:true,value:undefined}
即可
// 迭代器规范
某个对象.[Symbol.iterator] = function(){
return {
next(){
if(终止条件){
return {done:true,value:undefined}
}
// ...
return {done:false,value:xxx}
}
}
}
类数组对象:{0:‘aa’,1:‘bb’,2:‘cc’,length:3}
如果想给 类数组 对象添加迭代器,可以不用自己写,直接使用Array的迭代器:
var a={0:'aa',1:'bb',2:'cc',length:3}
a[Symbol.iterator]=Array.prototype[Symbol.iterator]
for(let v of a){
console.log(v);
}
3 this分析
this代表执行主体,该指向和在哪创建或执行无关,也不是执行上下文
情况分五种:
- 函数前面有点,this就是前面的对象;函数前无点,this就是window(严格模式下是undefined)
- 事件绑定时,回调函数中的this是,被绑定元素本身
- 构造函数中的this指new出来的实例
- 箭头函数中的this绑定的是上下文中的this
- 可以基于Function.prototype.call/apply/bind去改变this指向
// 自己动手写一个call方法
Function.prototype.call = function (obj, ...params) {
let self = this,
key = Symbol("KEY"),
result;
obj == null ? (self = window) : null;
/^(object|function)$/.test(typeof obj) ? null : (obj = Object(obj));
obj[key] = self;
result = obj[key](...params);
delete obj[key];
return result;
};
function abc(p) {
console.log(this, p);
}
let obj = { name: "pp", age: 11 };
abc.call(obj, 1);
// 自己动手写bind方法
Function.prototype.bind = function (obj, ...args) {
let self = this;
return function (...args2) {
return self.apply(obj, args2.concat(args));
};
};
function a(...p) {
console.log(this, p);
}
let obj = { name: "pp", age: 11 };
a.bind(obj, 9)("abc");
鸭子类型(狸猫换太子)
Array.from() 可以通过以下方式来创建数组对象:
伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)
可迭代对象(可以获取对象中的元素,如 Map和 Set 等)
鸭子类型指的是:该对象大部分结构与另一个类型相同,则可以借用另一个类型已有的方法执行,如arguments
与数组类型很相似,所以可以借用数组的很多方法,如:
function func1(){
// use Array.prototype.slice
let args = Array.prototype.slice.call(arguments)
// use Array.prototype.forEach
[].forEach.call(arguments,x=>console.log(x))
}
func1(10,20,30)
4 HTTP网络层的前端性能优化
前端优化点:
- HTTP网络层优化
- 代码编译层优化(webpack)
- 代码运行层优化(html css js vue react)
- 安全优化(xss csrf)
- 数据埋点及性能监控
CRP:关键路径渲染
七层网络模型:
- 物理层:比特、电气特性
- 数据链路层:帧、MAC地址
- 网络层:报文
- 传输层:流量控制、错误检测
- 会话层
- 表示层
- 应用层
TCP/IP四层网络模型:
a. 网络接口层:ARP/RARP
b. 网络层:ICMP/IP
c. 传输层:TCP/UDP
d. 应用层:HTTP/DNS/FTP/SMTP
url从输入到展示结果经历
1. url解析
在前后端请求时需要对一些特殊字符进行编码,如:
%
等,前端有两种编码方式:
对整个url编码:encodeURI/decodeURI
:这种方式只对中文和空格进行编码
对参数编码:encodeURIComponent/decodeURIComponent
:对中文、空格、%、:、/ 都会编码
URI统一资源标识符
URL统一资源定位符
URN统一资源名称
这是一个URL(绝对地址):https://blog.csdn.net/qq_40321119/article/details/102856789
这是一个URN(相对地址):qq_40321119/article/details/102856789
这是一个URI:可以是URL,也可以是URN
2. 缓存
属于产品优化的重点,服务端返回缓存头字段相关配置,浏览器自动实现缓存机制
缓存分为:强缓存/协商缓存。
缓存位置:内存缓存、硬盘缓存。
检测步骤:先检测是否有强缓存,没有或失效就检测是否有协商缓存,没有就去获取最新数据
html页面一般不做缓存,js、css及其他资源会
- 强缓存:
Cache-Control:max-age=
表示多长时间后再次请求新数据HTTP1.1,Expires
指定过期时间HTTP1.0,优先级低于前者
打开新网页:(此时直接检查硬盘缓存,没有则发送请求新数据)
F5刷新:先查内存再查硬盘,都没有就请求数据
Ctr+F5:不使用缓存,请求头会加字段:Cache-control:no-cache
,直接请求最新数据
问题:如果页面更新,之前请求设置了强缓存还能获取最新数据吗?
答案:可以,页面更新后webpack打包的资源名称hash值被改变,浏览器发现新资源未做缓存,所以会重新请求。还有一种方法,如果不使用webpack,则将引入的资源加后缀也可以,后缀名不同也不走缓存。<script src='xxx.js?12345'></script>
- 协商缓存
Last-Modified (HTTP1.0) / ETag (HTTP1.1)
协商缓存就是强缓存失效后,浏览器携带缓存标志向服务器请求,服务器根据缓存标志决定是否使用缓存的过程
处理过程:
携带缓存标志:If-Modified-Since=Last-Modified 值
If-None-Match=ETag 值
服务器没更新:304状态,浏览器直接读缓存
服务器更新了:200状态及最新资源以及Last-Modified / ETag,将结果和缓存标志写入本地
Last-Modified 资源文件最后更新时间,秒
ETag 记录的是一个标识,更新资源文件会生成新的ETag
- LocalStorage数据缓存
一般使用该对象存储数据缓存,缓存格式建议:time:xxx, data:[]
请求时先检测本地缓存对象中是否存在该数据且未过期,否则就直接请求新的
3. DNS解析
域名解析,也是带缓存的
先是走本地hosts文件
没有则走本地DNS解析缓存
没有则走本地DNS服务
没有则走远程域名解析服务器
递归查询、迭代查询
递归
DNS优化:1 减少DNS请求次数(一个页面资源都放到相同的服务器上) 2 预获取prefech
第一种不推荐:实际往往会将不同资源放到不同服务器上,有利于提高资源服务器的性能利用,同时HTTP同源一次大概4-7个请求,资源分布后有利于实现高并发
注意:必须要使用Link标签!
4. 建立连接通道TCP三次握手
三次握手:
client:SYN=1 seq=x
server:SYN=1 ACK=1 seq=y ack=x+1
client:ACK=1 seq=x+1 ack=y+1
四次挥手:
client:FIN=1 seq=u
server:ACK=1 seq=v ack=u+1
服务器继续与客户端传递数据
server:FIN=1 ACK=1 seq=w ack=u+1
client:ACK=1 seq=u+1 ack=w+1
HTTP1.0、1.1、2.0区别
HTTP/1.0 每次请求建立一个TCP连接,用完即关闭,长连接默认关闭
HTTP/1.1 默认开启【长连接keep-alive】:若干请求会串行化处理,前一个请求超时会导致后面所有请求阻塞,新增断点续传(返回码206),新增支持host头域,如果没有会报400(通过host处理一台服务器上有多个共享IP地址的虚拟服务器,1.0默认一个IP对应一个服务器)
HTTP/2.0 新增【多路复用】:多个请求在同一个连接上并行执行,某个请求耗时严重不会影响其他请求
HTTP2.0相比于1.x新增特性
新的二进制格式binary format:1.x基于文本解析,需要考虑的场景很多,二进制不同,健壮性高
header压缩:1.x的header带有大量信息,每次都重复发送,2.0中使用encoder减少header大小,通讯双方各自缓存一份header fields表,避免重复传输,减少大小
服务端推送:可以在HTTP响应头中设置Link命令来让浏览器接收服务器的推送,无需多次请求(页面有个css请求,客户端收到css时,服务端会将js等文件也推送过来,客户端请求时从缓存中读取)
Link: </styless.css>; rel=preload; as=style, <example.png>; rel=preload; as=image
5 对象深拷贝
浅拷贝:拷贝后生成的对象不同,对象中的值指向原来的数据(只拷贝了一级)
深拷贝:拷贝后生成的对象和对象中的值都是新的
浅拷贝
数组浅拷贝方法:
...
展开运算符:let a = [...b]
concat
运算:let a = b.concat([])
slice
运算:let a = b.slice()
对象浅拷贝:
...
展开运算符:let a ={...b}
Object.assign
运算:let a = Object.assign({},b)
- 写循环赋值,
Object.keys(obj)
无法获取Symbol
类型的属性, 因为该属性不可枚举,所以需要再加上Object.getOwnPropertySymbols(obj)
获取所有obj上的属性
获取对象的Symbol属性名称:Object.getOwnPropertySymbols(obj)
深拷贝
实现原理:每次浅克隆一级,如果有下一级继续浅克隆下一级,实现所有深克隆
但是深拷贝中有个问题:循环引用:let a={};a.a=a
,此时循环拷贝会出现栈溢出问题,解决方法是使用缓存,即判断当前需要拷贝的对象是否之前拷贝过,如果拷贝过则直接从缓存中取,否则就创建一个新的
/\*\*
\* 深克隆(深拷贝)+ 解决深拷贝函数中循环引用时导致的栈溢出的问题
\* @param {object} origin
\* @param {\*} hashMap WeakMap数据,用于缓存克隆过的对象
\* @returns origin / 克隆的origin
\*/
function deepCloneCycle(origin, hashMap = new WeakMap()) {
let result = null;
if (hashMap.has(origin)) return hashMap.get(origin); // 查缓存字典中是否已有需要克隆的对象,有的话直接返回同一个对象(同一个引用,不用递归无限创建进而导致栈溢出了)
if (typeof origin === 'object' && origin !== null) { // 【类型判断】引用类型,进行递归拷贝(用typeof判断类型要剔除null的情况)
if (Object.prototype.toString.call(origin) === '[object Array]') {
// 【类型判断】数组类型,创建一个新数组
result = [];
hashMap.set(origin, result); // 哈希表缓存新值
// 【遍历赋值】
origin.forEach(el => {
result.push(deepCloneCycle(el, hashMap)); // 【递归】
});
} else {
// 【类型判断】对象类型,创建一个新对象
result = {};
hashMap.set(origin, result); // 哈希表缓存新值
for (const key in origin) {
// 【遍历赋值】对象这里特殊处理了,不遍历拷贝原型链上的属性
if (origin.hasOwnProperty(key)) {
result[key] = deepCloneCycle(origin[key], hashMap); // 【递归】
}
}
}
} else { // 【类型判断】原始类型直接返回
return origin;
}
return result;
}
当然对于对象上存在Symbol属性的还需要在遍历key的时候把Object.getOwnPropertySymbols(obj)
也算上去
6 对象merge合并
两个对象合并在业务中非常有用,一般替合并规则为:一个原对象A,一个新对象B,将B合并到A上,对于其中的某个属性:
1. A和B该属性为基本数据类型,B直接覆盖A的该属性
2. A该属性为对象,而B为基本数据类型,报错!
3. A是基本数据类型,B为对象,直接覆盖
4. A和B该属性都是对象,递归合并
5. 特殊的,对于属性是数组的,合并中看作基本数据类型!!
示例程序:
// 定义一个函数用于判断如惨是否为对象
function isObj(obj){
if(typeof obj === "object" && obj instanceof Object && !(obj instanceof Array)) return true;
return false;
}
// merge函数,将后者合并到前者
function mergeObj(objo,objn){
let isObjo = isObj(objo);
let isObjn = isObj(objn);
if(isObjo && !isObjn) Throw new TypeError("merge function param must be object!");
if(!isObjo && !isObjn) return objn
if(!isObjo && isObjn) return objn
Object.keys(objn).forEach(key=>{
objo[key] = mergeObj(objo[key],objn[key])
})
}
7 函数柯里化(bind&currying)
简单理解即:调用一个函数返回一个新函数
思想:利用闭包,将目标函数需要处理的一些参数预先处理,并返回该函数
借助工具:bind/call/apply
自己实现一个bind:
function bind(func,context,...args){
return function proxy(){
return func.call(context,...args)
}
}
对于IE8及以下的浏览器,由于不支持bind,需要自己定一个放在function原型上
示例函数:
~ function(proto){
function bind(context,...args){
let that = this;
return function(){
return that.call(context,...args)
}
}
proto.bind = bind;
}(Function.prototype)
8 AOP切面编程
POP:面向过程
OOP:面相对象
AOP:面相切面
ajax
1)ajax请求的原理/ 手写一个ajax请求?
2)readyState?
3)ajax异步与同步的区别?
4)ajax传递中文用什么方法?
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
示例函数:
~ function(proto){
function bind(context,...args){
let that = this;
return function(){
return that.call(context,...args)
}
}
proto.bind = bind;
}(Function.prototype)
8 AOP切面编程
POP:面向过程
OOP:面相对象
AOP:面相切面
ajax
1)ajax请求的原理/ 手写一个ajax请求?
2)readyState?
3)ajax异步与同步的区别?
4)ajax传递中文用什么方法?
[外链图片转存中…(img-gfrC1Koc-1715646513636)]
[外链图片转存中…(img-MSDPPcHh-1715646513636)]