2024年最全JavaScript高级_new array(9999999),教资面试心得体会

框架相关

原生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代表执行主体,该指向和在哪创建或执行无关,也不是执行上下文

情况分五种:

  1. 函数前面有点,this就是前面的对象;函数前无点,this就是window(严格模式下是undefined)
  2. 事件绑定时,回调函数中的this是,被绑定元素本身
  3. 构造函数中的this指new出来的实例
  4. 箭头函数中的this绑定的是上下文中的this
  5. 可以基于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网络层的前端性能优化

前端优化点:

  1. HTTP网络层优化
  2. 代码编译层优化(webpack)
  3. 代码运行层优化(html css js vue react)
  4. 安全优化(xss csrf)
  5. 数据埋点及性能监控

CRP:关键路径渲染

七层网络模型:

  1. 物理层:比特、电气特性
  2. 数据链路层:帧、MAC地址
  3. 网络层:报文
  4. 传输层:流量控制、错误检测
  5. 会话层
  6. 表示层
  7. 应用层

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传递中文用什么方法?

ajax.PNG

前12.PNG

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

示例函数:

~ 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)]

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值