面试-JS

23 阅读13分钟

闭包

概念

  • 函数及外部的作用域就形成了一个闭包。

特点

  • 如果没有闭包的特性,那么函数想访问外部的变量,需要通过参数传递

  • 闭包能让函数访问外部作用域,即便是外部函数执行完毕,仍能保留变量的状态

  • // 保持局部变量的状态,相当于创建私有变量
    function outer() {
      let a = 0
      function inner() {
        return a++
      }
      return inner
    }
    
  • 闭包使用不当可能导致内存泄漏:引用的变量无法被释放,可能导致内存被占用

    • 引用了大数组,复杂的对象
    • 引用了DOM元素

原型-原型链

概念

原型:每个JS对象都有一个内置属性,这个属性指向另一个对象,这个对象被称为“原型对象”

当我们访问一个对象的属性时,会先检查该对象本身是否有,如果该对象没有这个属性,那么JS就会去它的原型对象上找,如果原型对象上没有,则继续往上找,直到找到该属性或者到达原型链的终点(null)

实践

  • 实现继承(抽离代码、逻辑)
  • 共享属性、方法
  • 构造函数通过new创建实例
  • class是原型的语法糖

举例

  • 要创建一个鲨鱼实例,大象实例
  • 如果给他们分别写一个类,那么就会有很多重复代码(身高、体重,吃喝拉撒)
  • 如果给他们定义一个动物基类,将这些重复代码(属性、方法)抽离到基类里
  • 再通过继承基类对公共属性、方法进行复用,然后在他们自己的类,实现独有的功能
  • 这样就可以实现代码复用,提高可维护性和开发效率

this

概念

this是JavaScript中的一个比较灵活关键字,指向函数执行时的上下文,提供一种在对象方法中引用当前对象的方式。

这使得我们可以在对象的方法内部访问对象的其他属性或方法,而不需要必须明确对象名称本身,让我们代码编写起来更加灵活、方便。

几个关键的点

  • this是在运行时绑定的
  • this的绑定和定义的位置(编写的位置)无关
  • this的绑定和调用方式以及调用的位置有关

四种主要规则

  • 默认绑定:非严格模式下独立调用时,this默认绑定到全局对象
  • 隐式绑定:当函数作为对象的方法被调用时,this绑定到该对象
  • 显式绑定:call、apply、bind,将this绑定到指定的对象
  • new绑定:通过new调用构造函数时,this绑定到新创建的对象实例
  • 箭头函数不会创建自己的this,它会使用外部作用域的this

bind、call、apply

  • 显式设置函数内部的this值
function sayHello(message) {
  console.log(message + ",我是" + this.name);
}
​
const person = { name: "李四" };
​
sayHello.call(person, "早上好"); // 输出 "早上好,我是李四",this 指向 person
sayHello.apply(person, ["中午好"]); // 输出 "中午好,我是李四",this 指向 person
const greetPerson = sayHello.bind(person, "下午好");
greetPerson(); // 输出 "下午好,我是李四",this 指向 person

浏览器兼容

原因:浏览器内核不同

现代工程化的开发架构,通过配置选项

  • browserslist(市场份额、版本)
  • polyfill、babel
  • autoprefixer、postcss
  • caniuse

浏览器渲染

  • DNS解析

    • 用户输入的URL通常是一个域名地址,直接通过域名是无法找到服务器的,因为服务器本质上是一套拥有IP地址的主机
    • 需要通过DNS服务器来解析域名,获取IP地址
    • DNS会优先查找缓存,包括浏览器缓存、操作系统缓存、路由器缓存、ISP缓存
    • 如果缓存查找失败,就需要通过DNS递归解析,包括根域名服务器、顶级域名服务器、权威域名服务器
    • 最终找到IP地址,就可以通过该IP地址去连接服务器,并且IP地址会被发送回用户电脑,缓存起来
  • TCP连接

    • TCP的连接会进行三次握手,客户端发送SYN包,服务器接收到后返回一个SYN-ACK包,客户端再次发送一个ACK包,完成握手过程
    • 此时TCP连接建立完成,就可以开始传输数据了
  • HTTP请求

    • TCP建立连接成功,客户端就可以通过这个连接发送HTTP请求,包括请求方法、URL、协议版本、请求头、请求体
    • 服务器接收到HTTP请求后,会处理这个请求,并且返回一个HTTP响应
    • HTTP响应包括状态码、响应头、响应内容,这里通常是index.html文件
  • HTML解析和CSS解析

    • 浏览器获取到index.html后,可以开始对文档进行解析
    • 包括HTML解析来构建DOM Tree,在这个过程中会遇到CSS文件和JS文件
    • 遇到CSS文件和JS文件会继续向服务器发送HTTP请求,并将指定的CSS、JS文件进行下载
    • 之后对CSS文件进行解析,解析出对应的CSSOM
  • 渲染render、布局layout、绘制paint

    • DOM Tree和CSSOM共同构建出Render Tree
    • 之后在Render Tree上进行布局,计算每个元素尺寸、位置、宽高
    • 再由浏览器进行绘制(颜色、边框、圆角、阴影...),显示在屏幕上

跨域

同源:协议、主机、端口必须完全相同

同源策略:不同源就被认为是跨域,跨域请求会被浏览器阻止

跨域限制主要目的是为了安全

  • 防止CSRF(跨站请求伪造):攻击者在A网站伪造请求访问B网站的接口,盗取用户数据
  • 防止恶意脚本窃取数据

解决方法

  • CORS(跨域资源共享):服务器通过响应头控制跨域请求是否被允许(Access-Control-Allow-x)

  • 配置代理(webpack、vite创建一个开发服务器,利用http-proxy获取代理配置项后,帮我们做正向代理)

  • Nginx反向代理

    • 代理静态资源和API服务器
    • 仅仅代理API服务器
  • WebSocket不受同源策略的限制

URI、URL、URN

URI(统一资源标识符)= URL(统一资源定位符) + URN(统一资源名称)

URL:标识资源,并且提供访问方式,包括协议+主机+端口+路径+查询参数+hash值,相当于某个人的具体地址,可以直接找到他

URN:标识资源,不提供访问方式,相当于某个人的电话号码,无法直接找到他

正向代理、反向代理

代理(Proxy)是一种网络服务,它充当客户端和服务器之间的中间人。客户端向代理服务器发送请求,代理服务器再将请求转发给目标服务器。服务器返回的响应也会经过代理服务器,然后转发给客户端。

正向代理:客户端的代理,代表客户端向服务器发起请求

  • 访问受限资源:国内存在防火墙,无法直接访问Google,使用VPN作为正向代理进行访问
  • 保护用户隐私:客户端知道代理服务器IP,目标服务器不知道客户端的真实IP
  • 过滤内容,当防火墙使用:学校、公司的电脑屏蔽某些网站

反向代理:服务器的代理,代表服务器接收客户端的请求

  • 负载均衡:将请求分发给多台服务器,优化资源使用,提高并发能力
  • 安全保护:隐藏服务器的真实IP,拦截恶意攻击,保护服务器安全
  • 缓存:将静态资源缓存到代理服务器,减少重复资源的请求,加快访问速度

事件循环

JavaScript是单线程语言,即同一时间只能执行一个任务。如果没有事件循环,那么那些耗时任务(网络请求、定时器)就很容易导致页面卡死。

事件循环是一种机制,主要任务是监视调用栈(call stack)和任务队列(task queue),如果是同步任务,就按顺序放入调用栈直接执行,如果是异步任务,就会放入任务队列中等待执行,当调用栈为空时,会从任务队列中取出任务执行。

事件循环的执行流程:

  • 执行同步代码
  • 执行所有微任务
  • 执行一个宏任务
  • 回到步骤2

事件循环保证了JavaScript的单线程执行模型,同时又能不阻塞主线程的情况下,处理大量的异步任务,使浏览器能流畅运行。

浏览器引擎

也称渲染引擎、排版引擎或浏览器内核。常见的浏览器引擎:Webkit(Safari),Blink(Chrome),Gecko(Firefox)

主要组成部分

  • HTML解析器
  • CSS解析器
  • JavaScript引擎
  • 渲染引擎

工作流程

  • 解析HTML文件,构建DOM树
  • 解析CSS文件,构建CSSOM树
  • 构建渲染树:将DOM树和CSSOM树合并,构建出渲染树
  • 布局:计算每个元素尺寸、位置、宽高
  • 绘制:字体、颜色、边框、圆角、阴影...
  • 合成:将多个图层合成为最终的页面,显示在屏幕上
  • 解析、执行JavaScript代码:V8将JS代码解析成AST,再将AST解析成字节码,或将字节码编译成CPU可直接执行的机器码

优化技术

  • 异步加载:async、defer异步加载JS文件,避免阻塞解析HTML
  • 延迟渲染:将非关键资源(图片、视频)延迟加载,优先渲染可见区域
  • GPU加速:利用GPU进行渲染、合成,还可以提升页面滚动和动画的性能
  • 缓存机制:利用缓存(HTTP缓存),减少重复资源的加载时间
  • 增量渲染:分块解析和渲染HTML,逐步显示页面内容,提升用户体验

垃圾回收

在 JavaScript 中,内存的分配和释放都是自动完成的,这个过程就称为垃圾回收。

基本思路:确定哪个变量不会再使用,然后释放它占用的内存。

这个过程是周期性的,即垃圾回收程序每隔一定时间就会自动运行。

  • 引用计数

    • 每个对象都有一个引用计数,记录它被引用的次数

    • 如果有其他对象引用它,那么引用计数就+1

    • 如果其他对象停止引用它,那么引用计数就-1

    • 当一个对象的引用计数为0时,它所占用的内存就会被回收

    • 存在循环引用的问题

      • 函数中对象循环用,在引用计数策略下,它们的引用计数一直不为0,函数被多次调用,则会导致大量内存不会被释放
      • 解决方式:将对应的属性置为null,停止引用
  • 标记清除

    • 核心思路:可达性
    • 从根对象(window)开始递归遍历所有可访问的对象,并标记它们为“可达”
    • 未被标记的对象被认为是“不可达对象”,也就是垃圾
    • 垃圾回收程序会将这些“不可达对象”进行清理,释放它们所占用的内存
    • 标记清除很好地解决了循环引用的问题
  • 标记整理:将保留的对象搬运汇集到连续的内存空间,从而整合空闲空间,避免内存碎片化

  • 分代收集

    • 将对象分为“新的”,“旧的”
    • 许多对象出现,完成它们的工作很快“死去”,它们就可以被清理
    • 那些长期存活的对象,也就是经过几次垃圾清理都没能被清理的对象,会变得老旧,检查的频次就会减少
  • 增量回收:将标记过程分成多个小步骤,每次只标记一部分对象,而不是一次性标记所有对象,减少垃圾回收对程序带来的影响

  • 空闲时间回收:在CPU空闲时进行垃圾回收

JS引擎应用比较广泛的就是标记清除算法,当然类似于V8引擎为了进行更好的优化,他在算法的实现细节上会结合其他的算法

作用域

描述在代码中定义变量的区域,这个区域决定了变量的可访问性和生命周期。

  • 全局作用域:任何部分都可以访问这些全局变量

  • 函数作用域:在函数内部声明的变量和函数,只能在函数内部访问

  • 块级作用域:let/const,仅在{}块中可见。ES6新特性,对于管理局部变量非常有用,尤其是循环和条件语句中

  • 词法作用域

    • 作用域在代码编写时就已经确定,而不是在运行时
    • 内部函数可以访问外部函数的变量,但外部函数不能访问内部函数的变量

执行上下文

指代码执行时,JS引擎所创建的一种“环境”

类型

  • 全局执行上下文:代码首次运行时,浏览器环境是window
  • 函数执行上下文:调用函数时创建

组成

  • 变量对象(VO)

    • 存储变量、函数声明和函数参数
    • 在全局上下文中,变量对象是全局对象(window)
    • 在函数上下文中,变量对象是活动对象(AO)
  • 作用域链:内部函数可访问外部函数的特性,向上查找作用域形成的链条

  • this:在函数执行时绑定的

如何影响JS代码的执行

  • JS引擎在执行一段代码时,会创建一个执行上下文。在这个阶段,变量和函数会被提升(var、function)
  • 执行阶段,JS引擎会按照代码的顺序逐行执行。变量会被赋值,函数会被调用。
  • 执行上下文在此过程中保持对作用域链的追踪,以便需要时解析
  • JS引擎使用一个栈来管理多个执行上下文。

SEO

搜索引擎优化,通过优化网站结构、内容和链接等方式,提高网站在搜索引擎结果页的排行,从而增加流量,提升网站曝光度。

  • 付费,打广告:成本比较高

  • 站内优化

    • 关键字优化:文档标题,内容标题(h1-h6),正文

      • 高质量内容:原创、高价值、解决问题,而不是低质、重复、搬运的内容
    • HTML语义化:使用适当的标签,使搜索引擎更容易理解页面结构

    • URL优化:使用简短、清晰、包含关键字的URL

    • 页面加载速度

      • CDN
      • JS、CSS、图片进行压缩,减少一些不必要的HTTP请求
    • 用户体验优化

      • 骨架图
      • 面包屑导航
      • 动画、变换
  • 站外优化

    • 外链建设:放入知名博客、社区
    • 社交媒体:分享平台内容,提高曝光度
  • 技术SEO

    • SSR:方便搜索引擎直接抓取页面
    • HTTPS
    • 移动端优化
    • Robots.txt:告知搜索引擎,哪些文件需要抓取,哪些文件不需要抓取

总之,SEO是一个长期的过程,需要持续投入和调整(定期更新维护,提高网站活跃度),遵守搜索引擎的规则。

TypeScript

  • 静态类型检查
  • 类型推断
  • 接口,类型别名,枚举
  • 泛型

增强了JS的能力,提供类型检查、类型推断,使得开发者能够在编写代码阶段就能发现数据类型导致的错误,编写更加健壮、可维护的代码。