1原型链
每一个实例对象都有一个proto属性,指向他构造函数的原型对象prototype或者null,访问一个对象的属性或者方法时,如果对象不存在这个属性或者方法,js会沿着原型链逐层向上查找,直到找到属性方法,或者查到原型链顶端,object.prototype
优点:原型链带来的好处,可以实现继承,方法和属性的复用,避免了在每个对象上都复制一份相同的属性方法,解节约了内存,实现了js的继承
缺点:访问不存在的属性或者方法,会遍历整个原型链,对性能有副作用
2.instanceof的原理
用来检测构造函数的prototype属性是否出现在某个实例对象的原型链上,如果存在返回true,不存在返回false
追问详答:www.yuque.com/u1598738/vq…
缺点:他是基于原型链的检查,如果某个对象原型链比较深,那么检查效率会比较低
3. New的原理
概念:new运算符创建一个用户自定义的对象类型的实例,或者具有构造函数的内置对象的实例
原理:
创建一个新的对象,并且把该对象绑定到构造函数(constructor)的this上
1. 创建一个空对象,该对象的原型为构造函数的原型对象
2. 把构造函数的this绑定到空对象上 this = object.create(constructor.prototype)
3. 执行构造函数的代码,并将属性和方法添加到该空对象中。
4. 如果构造函数没有显式返回一个对象,泽返回该空对象;否则付汇构造函数显式返回的对象。
实现
function myNew(Constructor,...args) {
// 创建一个空对象,原型为构造函数的原型对象
var obj = Object.create(Constructor.prototype);
//将构造函数的this绑定到空对象上,执行构造函数代码
var result = Constructor.apply(obj,args);
// 如果构造函数返回一个对象,泽返回该对象,否则返回空对象
return (typeof result === ‘object’ && result !== null) ? result : obj;
}
- js作用域
概念:指程序中定义变量的可见范围
详细: 作用域分为全局作用域和函数作用域,块级作用域(花括号内的,比如if while)
全局指在哪里都能访问,函数作用域指能在定义的函数中访问,在函数外部无法访问这些变量
es6使用let和const 可以创建块级作用域,使用let和const 可以创建块级作用域
- js事件流
概念:事件流(Event flow)的是浏览器处理事件的方式
详细说: 事件流分为三个阶段: 捕获阶段,目标阶段,和冒泡阶段
1. 捕获阶段:事件从最外层节点开始,向下传播,达到事件的目标节点
2. 目标阶段:到达目标节点,触发节点上的事件处理函数
3. 冒泡阶段:事件从目标节点开始,逐级向上传播,直到达到最外层的节点
阻止传播的API: event.stopPropagation(); event.preventDefault();
阻止传播,并且会阻止同一元素上其他事件处理程序被触发的API: event.stopPropagation();
- js事件轮询机制
概念:js事件轮询(Event Loop)用于处理js中的事件和回调函数
Js事件轮询机制可以使得单线程的js能够处理躲个任务,从而实现异步编程
js中的任务可以分为两类 宏任务(macro queue) 和微任务(micro task)。
宏任务包括一些花费时间较长的操作 比如定时器,事件回调等。当宏任务执行完毕后,js会检查是否存在未执行的微任务,如果存在,泽立即执行这些微任务,所有微任务执行完毕后,开始执行宏任务。
微任务包含一些需要尽快执行的操作,比如promise的回调函数,mutationObserver的回调函数等。微任务可以使用promise对象的then()或者mutationobserver的observe()方法注册
Js中时间轮训的执行机制如下:
1. 执行当前宏任务的同步代码,直到遇到第一个宏任务或者微任务。
2. 遇到微任务,则把他添加都微任务队列,继续执行下一个同步代码
3. 遇到宏任务,添加到宏任务队列,继续执行下一个同步代码
4. 当前任务执行完毕后,js会检查微任务或宏任务队列是否为空,如果不为空,则执行队列中的第一个任务,重复执行该步骤直到任务队列为空
5. 当前事件轮询结束,等待下一次事件的触发
JS的事件轮询机制是单线程的,宏任务时间过长会阻塞其他惹怒执行,从而导致应用的性能问题,编写JS代码时,避免长时间的同步操作,使用异步操作,保证程序的性能和响应速度
- 闭包的作用和原理以及使用场景
概念:闭包指在一个函数内部定义一个函数,该函数可以访问外部函数的变量与参数,可以将函数作为返回值,形成有权访问函数作用域中的变量的函数,有点是私有化数据,缺点是使用不当会导致内存泄漏,在不需要时及时把变量重置为null
详细:闭包可以将变量和函数私有化,从而避免命名冲突和变量污染。当函数执行完毕后,函数内的变量和函数仍然在内存中,不会被自动回收,因此可以被其他函数继续访问和使用,这个机制称为闭包。闭包的原理是在内存中创建一个包含函数和变量的环境,函数返回后,该环境还存在于内存中,可以被其他函数访问和使用。
应用场景:
-保存变量状态和私有化变量和函数,私有化方法
-用于事件处理和回调函数
-用于封装类和模块
-用于实现柯里化和函数式编程(多参数的函数转换为一系列单参数的函数,避免重复代码)
-用于解决循环中的异步问题
-用于实现缓存和记忆化等功能
如何防止闭包的内存泄漏
-避免创建不必要闭包
-使用后及时释放闭包,将引用闭包的变量赋值为null或者手动解除对闭包的引用
-使用模块模式:在模块模式中,可以使用立即执行函数来创建一个私有作用域,从而避免闭包中的变量被外部访问,避免内存泄漏
-避免循环引用:如果表中引用了dom元素或者其他对象,需要确保在不需要时释放,避免循环引用造成内存泄漏
- symbol这个新增的基础数据类型有什么用
概念:symbol是es6中新增的基础数据类型,主要作用是创建一个位唯一的标识符,用于对象属性名的命名,常量的定义等场景,每个sysbol都是唯一的,可以用作对象的属性名,避免命名冲突,还可以实现一血常量和枚举值,这些值不可修改和重复,另外,symbol不会出现在for...in for...of Objects.keys() Object.getOwnPropertyNames()等遍历方法中,可以定义些不希望被遍历到的属性,比如一些内部实现细节或隐藏属性
优点:有很高的性能和可靠性
9. 箭头函数和普通函数有什么区别,箭头函数能当构造函数吗,es6新特性
1.es6新特性
-块级作用域:let和const只在块级作用域有效
-箭头函数:具有简化语法和自动绑定this上下文特点
-模版字符串:使用反引号’’和${}操作符,可以方便的拼接字符串和变量
-解构赋值:可以将数组和对象的值解构赋给变量
-类和继承:引入了class和extends关键字,使得JS支持面向对象编程
-Promise和async/await: 用于处理异步编程特性
箭头函数和普通函数的区别
- 箭头函数没有自己的this上下文,它的this上下文继承自外部作用域,因此不能用call(),apply()或bind()方法改变this上下文。
- 箭头函数没有自己的arguments对象,如果需要获取函数,可以使用rest参数或者展开运算符
- 箭头函数不能作为构造函数使用,不能使用new关键字创建对象
注意
关于箭头函数能否作为构造函数的问题,根据规范来说,箭头函数没有construct方法,因此不能使用new关键字创建对象。如果强制使用new调用箭头函数,会抛出一个类型错误。因此。一般来说箭头函数不应该用于创建对象,而应该用于函数式编程和简化回调函数等场景
- promise的常用API
Promise all: 所有的Promise对象都resolve后,会resolve并返回一个Promise返回值组成的数组,一个Promise被reject,则会立即返回reject并且返回对应错误信息。
Promise.allSettled() : 所有对象都返回后,resolve一个所有Promise状态对象组成的数组
- JS脚本异步加载如何实现,有什么区别
- 创建动态script标签,并且设置其src属性为需要加载的脚本URL ,这种方式可以通过设置onload或onReadyStateChange事件来检测脚本是否加载完成。
- 使用XMLHttpRequest对象或者Fetch API 发送异步请求,并且在成功后将响应解析为JS代码,然后使用eval()函数或者Function()构造函数来执行脚本。
异步加载与同步加载的区别
-异步加载可以提高页面的加载速度和响应性能,避免JS阻塞而造成页面卡顿的情况。
-异步加载可以避免因为加载甲苯而造成的阻塞情况,使页面的其他资源可以更快的加载和呈现
-异步加载可以更灵活的控制脚本的加载顺序和执行时间,可以根据页面需要动态加载和卸载脚本,提高页面的可维护性和可扩展性,
- for in 和 for of 区别
For in 循环用于遍历对象可枚举属性,包括自有属性和继承属性,将对象的每个属性名(键名)作为迭代对象遍历(想到了Object.key),不适合用来遍历数组和类数组对象
For of 用来遍历可迭代对象的元素,他会将对象的每个元素作为迭代变量来遍历。他遍历的对象是元素值,不是属性名,适合遍历数组,字符串等可以迭代对象的元素值。
13. ==和===有什么区别
都是比较两个值是否相等 ,== 会进行类型转换 ,=== 会比较类型和值是否都相等
==的类型转换机制:
-如果两个值类型相同,直街比较值
-一个是null一个是undefined,则他们相等
-如果一个是数字,一个是字符串,则将字符串转换为数字后比较
-一个是boolean 一个不是boolean,将布尔转换为数字比较
-如果值是一个对象,另一个是数字,字符串或布尔,则将对象转为原始值后再比较
14. requestAnimationFrame / requestIdleCallback , 分别有什么用?
requestAnimationFrame / requestIdleCallback 都是用于再浏览器中执行动画或其他高性能任务API。
requestAnimationFrame 浏览器提供的动画请求机制,他会再浏览器下一次绘制之前执行指定的回调函数。这样做的
requestIdleCallback 在浏览器空闲时执行指定的回调函数
15. 垃圾回收机制
浏览器垃圾回收机制根据数据的存储方式分为栈垃圾回收和堆垃圾回收。
栈垃圾回收的方式非常简便,当一个函数执行结束之后,JavaScript 引擎会通过向下移动 ESP 来销毁该函数保存在栈中的执行上下文,遵循先进后出的原则。
堆垃圾回收,当函数直接结束,栈空间处理完成了,但是堆空间的数据虽然没有被引用,但是还是存储在堆空间中,需要垃圾回收器将堆空间中的垃圾数据回收。
Scavenge算法: 1. 标记:对对象区域中的垃圾进行标记 2. 清除垃圾数据和整理碎片化内存:副垃圾回收器会把这些存活的对象复制到空闲区域中,并且有序的排列起来,复制后空闲区域就没有内存碎片了 3. 角色翻转:完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域,这样就完成了垃圾对象的回收操作,同时这种角色翻转的操作还能让新生代中的这两块区域无限重复使用下去
标记-清除算法: 1. 标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。 2. 清除:将垃圾数据进行清除。 3. 产生内存碎片:对一块内存多次执行标记 - 清除算法后,会产生大量不连续的内存碎片。而碎片过多会导致大对象无法分配到足够的连续内存。
标记-整理算法 1. 标记:和标记 - 清除的标记过程一样,从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。 2. 整理:让所有存活的对象都向内存的一端移动 3. 清除:清理掉端边界以外的内存 V8 是使用副垃圾回收器和主垃圾回收器处理垃圾回收的,不过由于 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿。 为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为增量标记(Incremental Marking)算法
16. 强缓存和协商缓存
强制缓存
当web应用获取资源时,先从 本地 获取,如果有就直接用,返回200,否则,重新发起请求。
Header字段:Expires,Cache-Control
协商缓存
服务端可以根据唯一的字符串是否发生变化判断该资源是否为最新。
具体过程:浏览器第一次请求资源时,服务端会返回资源和资源标识 ETag 字符串;
当浏览器下一次请求该资源时,会带上这个字符串,请求头的键名为:If-None-Match,键值为第一次访问时服务端返回的 ETag 字符串;
该请求发送到服务端之后,服务端会检查该值跟所请求资源的标识字符串是否一致:
(1)如果一致说明文件内容没有发生变化,直接返回304;
(2)如果不一致返回200 + 最新资源 + 最新的 ETag字符串。
17.跨域的解决办法
不满足同源协议才会有跨域问题出现
1. 协议不同,如 http 和 https;
2. 域名不同;
3. 端口不同。
解决办法
1开发环境:在webpack里配置devserver
2线上环境:
1.配置nginx
2.jsonp 只是支持get请求
3.cors
18.浏览器渲染过程,输入网址到网页打开
输入到打开
· 用户输入网址:用户在浏览器的地址栏中输入目标网址,例如 www.example.com。
· 浏览器解析网址:浏览器解析用户输入的网址,它包括协议(例如 HTTP 或 HTTPS)、主机名(例如 www.example.com)、端口号(如果未指定,默认为 80 或 443)、路径(例如 /index.html)等信息。
· DNS解析:浏览器将主机名(例如 www.example.com)发送给本地域名系统(DNS)服务器,以获取目标网站的 IP 地址。DNS服务器将返回对应的 IP 地址,例如 93.184.216.34。
· 建立TCP连接:浏览器使用目标网站的 IP 地址和端口号(通常是 80 或 443)尝试与目标服务器建立 TCP 连接。如果目标网址使用 HTTPS,端口号通常是 443。
· 发送HTTP请求:一旦TCP连接建立,浏览器发送一个HTTP请求,其中包含要获取的网页的详细信息。这个HTTP请求包括请求方法(通常是 GET)、路径(例如 /index.html)、HTTP版本和其他头部信息。
· 服务器处理请求:目标服务器接收到HTTP请求后,开始处理请求。这通常涉及到在服务器上查找请求的资源,执行服务器端代码(如PHP或Java),或者从缓存中获取数据。
· 服务器响应:服务器处理完请求后,返回一个HTTP响应给浏览器。HTTP响应包含响应状态码(例如 200 OK 表示成功),响应头部信息(例如内容类型、日期、服务器等)和实际的页面内容。
· 浏览器渲染页面:浏览器接收到HTTP响应后,将页面内容渲染为用户可见的网页。这涉及到解析HTML、CSS和JavaScript,构建页面的DOM树和渲染树,并将内容显示在浏览器窗口中。
· 显示页面:最终,浏览器将页面显示给用户。用户可以与页面交互,点击链接,填写表单等。
· 关闭连接:一旦网页加载完成,浏览器将维护的TCP连接关闭,释放资源。
浏览器渲染过程
把 html、css、JavaScript 等资源进行计算,然后转换为显示器的像素点,最终形成合成帧,返回给浏览器进
1先是对html进行解析,构建一个以 Document 为根节点的 DOM Tree,解析的过程中如果遇到 script 标签,则会转而去加载解析 JavaScript 代码,如果这里的 JavaScript 使用异步加载则不会影响 html 的解析,否则要等 JavaScript 解析完成后才能继续构建 DOM Tree
2DOM Tree 构建完成后会开始解析 CSS,计算样式属性,结合 DOM Tree 构建 CSSOM 树
3根据渲染树来布局,以计算每个节点的信息
4将各个节点绘制到屏幕上
19. http, 三次握手四次挥手
http 1 , 1.1 , 2, 3
http与https区别
三次握手
· 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c),此时客户端处于 SYN_SENT 状态
· 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,为了确认客户端的 SYN,将客户端的 ISN+1作为ACK的值,此时服务器处于 SYN_RCVD 的状态
· 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,值为服务器的ISN+1。此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接
四次挥手
· 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态,停止发送数据,等待服务端的确认
· 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态
· 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态
· 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态
四次挥手原因
服务端在收到客户端断开连接Fin报文后,并不会立即关闭连接,而是先发送一个ACK包先告诉客户端收到关闭连接的请求,只有当服务器的所有报文发送完毕之后,才发送FIN报文断开连接
- 编译时和运行时
前端的运行时 = JavaScript引擎(基础运行时) + 宿主环境API(浏览器/Node等) + 事件循环机制
主要由浏览器的 JavaScript 引擎(如 V8、SpiderMonkey)提供和管理。它负责:
- 解析与执行:将你的JS代码解析成可执行的指令并运行。
- 内存管理(堆/栈) :分配调用栈(执行上下文)和堆内存(存储对象)。
- 提供内置对象和API:如
Object,Array,setTimeout,console等。 - 事件循环与任务队列:这是前端运行时最关键的机制,它决定了代码何时以及以何种顺序执行。
编译时
在代码构建阶段,在代码实际运行之前,编译器、打包器、转译器将开发者友好的代码转换为浏览器可执行的代码 具体表现:
- 语法转换:TypeScript → JavaScript,JSX →
React.createElement() - 代码优化:Tree-shaking、代码压缩、作用域提升
- 静态分析:类型检查、语法校验、依赖分析
- 预计算:Svelte将模板编译为高效更新代码,Vue 3的静态提升