web前端面试题及答案2023

本文深入探讨了前端面试中常见的问题,包括组件之间的通信方式,如父子组件通信、Redux与Context的应用,以及深拷贝、Vue生命周期、IndexedDB的特点。此外,还详细阐述了进程和线程的区别、AJAX的工作原理,以及Event Loop的详细流程,包括Node.js中的Event Loop阶段。最后,讲解了数组原型方法的实现、Promise的相关操作和JavaScript代码输出的结果分析。

组件之间通信

  • 父子组件通信
  • 自定义事件
  • redux和context

context如何运用

  • 父组件向其下所有子孙组件传递信息
  • 如一些简单的信息:主题、语言
  • 复杂的公共信息用redux

在跨层级通信中,主要分为一层或多层的情况

  • 如果只有一层,那么按照 React 的树形结构进行分类的话,主要有以下三种情况:父组件向子组件通信子组件向父组件通信以及平级的兄弟组件间互相通信
  • 在父与子的情况下 ,因为 React 的设计实际上就是传递 Props 即可。那么场景体现在容器组件与展示组件之间,通过 Props 传递 state,让展示组件受控。
  • 在子与父的情况下 ,有两种方式,分别是回调函数与实例函数。回调函数,比如输入框向父级组件返回输入内容,按钮向父级组件传递点击事件等。实例函数的情况有些特别,主要是在父组件中通过 React 的 ref API 获取子组件的实例,然后是通过实例调用子组件的实例函数。这种方式在过去常见于 Modal 框的显示与隐藏
  • 多层级间的数据通信,有两种情况 。第一种是一个容器中包含了多层子组件,需要最底部的子组件与顶部组件进行通信。在这种情况下,如果不断透传 Props 或回调函数,不仅代码层级太深,后续也很不好维护。第二种是两个组件不相关,在整个 React 的组件树的两侧,完全不相交。那么基于多层级间的通信一般有三个方案。
    • 第一个是使用 React 的 Context API,最常见的用途是做语言包国际化
    • 第二个是使用全局变量与事件。
    • 第三个是使用状态管理框架,比如 Flux、Redux 及 Mobx。优点是由于引入了状态管理,使得项目的开发模式与代码结构得以约束,缺点是学习成本相对较高

代码输出结果

function runAsync (x) {
   
   
    const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}

Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))

输出结果如下:

1
2
3
[1, 2, 3]

首先,定义了一个Promise,来异步执行函数runAsync,该函数传入一个值x,然后间隔一秒后打印出这个x。

之后再使用Promise.all来执行这个函数,执行的时候,看到一秒之后输出了1,2,3,同时输出了数组[1, 2, 3],三个函数是同步执行的,并且在一个回调函数中返回了所有的结果。并且结果和函数的执行顺序是一致的。

进程和线程的区别

  • 进程可以看做独立应用,线程不能
  • 资源:进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位);线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)。
  • 通信方面:线程间可以通过直接共享同一进程中的资源,而进程通信需要借助 进程间通信。
  • 调度:进程切换比线程切换的开销要大。线程是CPU调度的基本单位,线程的切换不会引起进程切换,但某个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
  • 系统开销:由于创建或撤销进程时,系统都要为之分配或回收资源,如内存、I/O 等,其开销远大于创建或撤销线程时的开销。同理,在进行进程切换时,涉及当前执行进程 CPU 环境还有各种各样状态的保存及新调度进程状态的设置,而线程切换时只需保存和设置少量寄存器内容,开销较小。

深拷贝

实现一:不考虑 Symbol

function deepClone(obj) {
   
   
    if(!isObject(obj)) return obj;
    let newObj = Array.isArray(obj) ? [] : {
   
   };
    // for...in 只会遍历对象自身的和继承的可枚举的属性(不含 Symbol 属性)
    for(let key in obj) {
   
   
        // obj.hasOwnProperty() 方法只考虑对象自身的属性
        if(obj.hasOwnProperty(key)) {
   
   
            newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key];
        }
    }
    return newObj;
}

实现二:考虑 Symbol

// hash 作为一个检查器,避免对象深拷贝中出现环引用,导致爆栈
function deepClone(obj, hash = new WeakMap()) {
   
   
    if(!isObject(obj)) return obj;
    // 检查是有存在相同的对象在之前拷贝过,有则返回之前拷贝后存于hash中的对象
    if(hash.has(obj)) return hash.get(obj);
    let newObj = Array.isArray(obj) ? [] : {
   
   };
    // 备份存在hash中,newObj目前是空对象、数组。后面会对属性进行追加,这里存的值是对象的栈
    hash.set(obj, newObj);
    // Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
    Reflect.ownKeys(obj).forEach(key => {
   
   
        // 属性值如果是对象,则进行递归深拷贝,否则直接拷贝
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key], hash) : obj[key];
    });
    return newObj;
}

Vue的生命周期是什么 每个钩子里面具体做了什么事情

Vue 实例有⼀个完整的⽣命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。
1、beforeCreate(创建前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据。
2、created(创建后) :实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 `$el` 属性。
3、beforeMount(挂载前) :在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。此时还没有挂载html到页面上。
4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。此过程中进行ajax交互。
5、beforeUpdate(更新前) :响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染。
6、updated(更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例仍然完全可用,`this` 仍能获取到实例。
8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。
另外还有 `keep-alive` 独有的生命周期,分别为 `activated``deactivated` 。用 `keep-alive` 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 `deactivated` 钩子函数,命中缓存渲染后会执行 `activated` 钩子函数。

IndexedDB有哪些特点?

IndexedDB 具有以下特点:

  • 键值对储存:IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  • 异步:IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
  • 支持事务:IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
  • 同源限制: IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
  • 储存空间大:IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
  • 支持二进制储存:IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

AJAX

const getJSON = function(url) {
   
   
    return new Promise((resolve, reject) => {
   
   
        const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
        xhr.open('GET', url, false);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.onreadystatechange = function() {
   
   
            if (xhr.readyState !== 4) return;
            if (xhr.status === 200 || xhr.status === 304) {
   
   
                resolve(xhr.responseText)
### Web前端相关面试题及其答案 #### 1. 对 JavaScript 的理解? JavaScript 是一种动态编程语言,具有弱类型特征,主要用于浏览器端开发。它支持事件驱动、函数优先以及异步处理机制。JavaScript 可分为三个部分:ECMAScript(核心语法)、DOM(文档对象模型)和 BOM(浏览器对象模型)。其主要特点包括跨平台兼容性、丰富的库生态以及强大的社区支持[^1]。 #### 2. 下列代码输出的值是什么? 假设代码如下: ```javascript let a = 1; function test() { console.log(a); } test(); ``` 由于 `a` 被声明在全局作用域下,在函数内部可以访问到全局变量 `a`,因此输出的结果为 `1`[^3]。 #### 3. 将下列代码改写为依次输出 0 到 9? 原代码可能是一个简单的循环结构,以下是实现逐秒打印的方法: ```javascript for (let i = 0; i < 10; i++) { setTimeout(() => { console.log(i); }, i * 1000); // 延迟时间设置为每秒增加 } ``` #### 4. 如何区分数组对象、普通对象和函数对象? 可以通过内置方法 `Array.isArray()` 来检测是否为数组;对于普通对象,可以直接使用 `typeof obj === 'object' && !Array.isArray(obj)` 进行判断;而函数对象则可通过 `typeof func === 'function'` 实现识别。 #### 5. 面向对象与面向过程的区别? - **面向对象**强调的是类的概念,通过封装数据成员和行为成员形成独立的对象实体,便于维护扩展。 - **面向过程**更关注解决问题的具体步骤流程描述,通常按照顺序逐步完成任务目标。 #### 6. 面向对象的三大基本特性? 继承(Inheritance)、封装(Encapsulation) 和 多态(Polymorphism)。 #### 7. XML 和 JSON 的区别? | 特性 | XML | JSON | |--------------|------------------------------|-----------------------------| | 数据格式 | 文本 | 文本 | | 易读性 | 较差 | 更好 | | 解析效率 | 较低 | 较高 | | 支持的数据类型 | 字符串为主 | 数字、布尔值等多种类型 | #### 8. Web Worker 和 WebSocket 的差异? - **Web Worker**: 主要用于后台运行脚本而不影响页面性能表现的技术手段之一; - **WebSocket**: 提供了一个持久连接通道使得服务器能够实时推送消息给客户端。 #### 9. JavaScript 中垃圾回收的主要方法? 主要包括引用计数法(reference counting garbage collection) 和标记清除算法(mark-and-sweep algorithm),其中后者更为常用并有效解决了者存在的循环引用问题[^1]。 #### 10. 使用 new 操作符创建实例的过程? 当调用new运算符时会发生四件事情: 1. 创建一个新的空对象。 2. 设置这个新对象的原型(__proto__)指向构造器函数prototype属性所指代的那个对象。 3. 执行构造器函数中的代码, 并将this绑定至新建出来的那个对象上. 4. 返回该新生成的对象如果构造器显式返回另一个非null对象,则最终结果将是那个被返回的对象而不是默认的新建对象[^1]。 #### 11. JS 延迟加载的方式有哪些? 常见的几种方式包括但不限于defer/async 属性应用于script标签内联或者外部文件链接处;动态插入 script 元素节点等等[^1]。 #### 12. WEB 应用从服务端主动推送到客户端的方式都有哪些? 主要有轮询(polling), 长轮询(long polling), Server-Sent Events(SSEs) ,以及最高效的 WebSocket 技术等几种主流方案可供选择[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值