day05面试题

1.同步和异步的区别

同步(Synchronous)和异步(Asynchronous)是用来描述程序执行模式的概念。

同步指的是代码按照顺序依次执行,每一行代码执行完成后再执行下一行代码。在同步模式下,当执行一个耗时的操作时,程序会阻塞,直到该操作完成才能进行下一步操作。这意味着在执行耗时操作期间,程序无法进行其他的任务处理。

异步指的是代码的执行不依赖于顺序,可以在等待某个操作完成的同时继续执行其他的任务。在异步模式下,耗时的操作会被委托给其他线程或进程来处理,而不会阻塞主程序的执行。一旦耗时操作完成,系统会通知程序并执行相应的回调函数或事件处理器。

异步编程可以有效地提高程序的性能和响应能力,尤其在需要进行大量I/O操作或网络请求时非常有用,可以避免程序的阻塞并允许同时处理多个任务。

异步(Asynchronous)编程可以进一步分为以下几种形式:

  1. 回调函数(Callback):在异步操作完成后,通过回调函数来处理结果或执行下一步的操作。回调函数作为参数传递给异步函数,在异步操作完成时被调用。这是 JavaScript 中最常见的异步编程方式。

  2. Promise:Promise 是 ECMAScript 6 引入的一种用于处理异步操作的机制。它表示一个异步操作的最终完成或失败,并可以链式拼接多个操作,以便更好地处理和组织异步代码。

  3. async/await:async/await 是 ECMAScript 2017 引入的异步编程语法糖。使用 async 关键字声明的函数内部可以使用 await 操作符来等待一个异步操作的完成,使得异步代码看起来更像同步代码,提高了可读性。

  4. 事件(Event):通过事件驱动的方式处理异步操作。当异步操作完成时,触发相应的事件,程序通过注册事件处理器来响应操作完成的事件。

  5. 观察者模式(Observer Pattern):通过定义观察者和被观察者对象,使观察者能够监听到被观察者的状态变化,并做出相应的处理。被观察者完成异步操作后通知观察者进行处理。

这些方法都是用于处理异步操作的常见模式和技术,每种方式都有其适用的场景和特点。根据具体的需求和编程环境选择合适的异步编程方式可以提高代码的可维护性和性能。

异步代码执行涉及到宏任务(Macro Task)和微任务(Micro Task)的概念。

宏任务指的是较大粒度的任务单元,通常包括整体的一段代码、事件回调、定时器等。在 JavaScript 中,常见的宏任务包括 setTimeout、setInterval、I/O 操作、DOM 事件等。

微任务则是相对细小的任务单元,在宏任务执行过程中产生,在宏任务结束前执行。常见的微任务有 Promise 的回调、MutationObserver 等。

在事件循环(Event Loop)的执行过程中,当一个宏任务执行完毕后,会检查微任务队列(即所有已注册的微任务),并依次执行队列中的微任务。只有当微任务队列为空时,才会执行下一个宏任务。

简而言之,异步代码执行过程可以概括为以下几个步骤:

  1. 执行当前的宏任务。
  2. 检查并执行微任务队列中的所有微任务。
  3. 更新渲染(若有需要)。
  4. 执行下一个宏任务,重复上述步骤。

使用宏任务和微任务的概念可以更好地理解和管理异步代码的执行顺序,并避免出现阻塞或不符合预期的行为。

总结区别:

  • 同步是按照顺序依次执行代码,阻塞程序执行;异步是不按照顺序执行代码,不阻塞程序执行。
  • 同步操作完成后再执行下一步操作,而异步操作可能在后台进行,并通过回调函数或事件处理器通知程序操作已完成。
  • 同步模式适用于简单的操作,而异步模式适用于需要处理大量I/O或网络请求等耗时操作的情况。

总的来说,同步和异步是两种不同的代码执行模式,根据实际需求选择合适的模式可以提高程序的效率和响应性。

2.谈一谈箭头函数与普通函数的区别

箭头函数(Arrow Function)与普通函数(Regular Function)在语法上和功能上存在一些区别。

  1. 语法简洁:箭头函数的语法更加简洁,可以通过 "=> " 箭头符号来定义函数,省略了 function 关键字和大括号。例如:

    • 普通函数:function add(a, b) { return a + b; }
    • 箭头函数:(a, b) => a + b
  2. 适用场景:箭头函数通常更适用于只包含单个表达式并返回其结果的简单函数,而普通函数适用于复杂的函数逻辑或需要特定的上下文(this 绑定)的场景。

  3. this 的绑定:在普通函数中,this 的值是动态的,并且根据如何调用函数而有所不同。而箭头函数没有自己的 this 绑定,它会捕获当前上下文的 this 值。箭头函数中的 this 始终指向定义时所在的作用域。这对于避免 this 绑定问题和编写更简洁的回调函数很有用。

  4. arguments 对象:普通函数中可通过 arguments 对象访问所有传递给函数的参数。而箭头函数没有自己的 arguments 对象,可以使用剩余参数(rest parameters)来接收函数参数。

  5. 构造函数:普通函数可以用作构造函数创建新的对象实例,并且具有原型对象。而箭头函数不能被用作构造函数,因为它没有自己的 this 绑定。

总之,箭头函数更加简洁、适用于简单的函数和回调函数,并且避免了 this 绑定问题。而普通函数则适用于复杂的函数逻辑和需要特定上下文的情况。在选择使用哪种函数类型时,需要根据具体的需求和代码逻辑来进行判断。

3.JS 数组和对象的遍历方式,以及几种方式的比较

JavaScript 中遍历数组和对象的常见方式包括使用 for 循环、for…in 循环、for…of 循环和 Object.keys() 方法。

  1. 使用 for 循环遍历数组:
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
    
    
  console.log(arr[i]);
}
  1. 使用 for…in 循环遍历对象的属性:
const obj = {
    
     a: 1, b: 2, c: 3 };
for (let key in obj) {
    
    
  if (obj.hasOwnProperty(key)) {
    
    
    console.log(key, obj[key]);
  }
}

注意:需要使用 hasOwnProperty() 方法来判断属性是否为对象自身的属性,避免遍历到原型链上的属性。

  1. 使用 for…of 循环遍历数组(ES6+):
const arr = [1, 2, 3];
for (let value of arr) {
    
    
  console.log(value);
}

for…of 循环直接遍历数组的元素值,更加简洁。

  1. 使用 Object.keys() 方法遍历对象的键(ES5+):
const obj = {
    
     a: 1, b: 2, c: 3 };
Object.keys(obj).forEach(key => {
    
    
  console.log(key, obj[key]);
});

Object.keys() 方法返回一个由对象的键组成的数组,然后可以使用 forEach() 方法遍历数组。

比较:

  • for 循环是最传统、通用的遍历方式,适用于遍历数组和具有数值键的对象。
  • for…in 循环适用于遍历对象的属性,但需要注意原型链的影响。
  • for…of 循环是简洁且直观的数组遍历方式,不支持对象遍历。
  • Object.keys() 方法可以方便地获取对象的键,并通过遍历键进行访问。

根据实际需求选择合适的遍历方式,通常情况下,for 循环和 for…of 循环是最常用的遍历数组的方式,而 for…in 循环和 Object.keys() 方法则适用于遍历对象。

4.如何解决跨域问题

要解决跨域问题,可以采取以下几种常见的方法:

  1. 跨域资源共享(CORS):在服务端设置响应头来允许特定的源(域名、协议、端口)进行跨域访问。需要在服务器端配置正确的响应头信息,例如添加 Access-Control-Allow-Origin 字段,并指定允许访问的源。

  2. 代理服务器:通过在同一域中设置一个服务器作为中间层,将客户端请求转发到目标服务器,实现跨域访问。客户端请求先发送到代理服务器,再由代理服务器向目标服务器发送请求并返回结果给客户端。

  3. JSONP(JSON with Padding):利用 <script> 标签没有跨域限制的特性,通过动态创建一个 <script> 元素,将跨域请求的 URL 放入 src 属性中,服务器返回的数据会被包裹在回调函数中返回。JSONP 只能用于 GET 请求,并且需要目标服务器支持返回指定回调函数的格式。

  4. WebSocket:使用 WebSocket 协议进行跨域通信。WebSocket 是 HTML5 提供的一种新协议,在浏览器和服务器之间建立持久的连接,双方可以通过异步通信方式进行数据传输。

  5. 反向代理:在服务器端配置反向代理,将客户端请求转发到目标服务器,并在响应返回给客户端之前修改响应头,消除跨域限制。常见的反向代理工具有 Nginx、Apache 等。

需要根据具体情况选择合适的解决方案,并确保正确配置相关信息来实现跨域访问。同时,还需注意考虑安全性和合法性,以防止滥用或泄露敏感数据。

5.XML和JSON的区别

XML(可扩展标记语言)和 JSON(JavaScript 对象表示法)都是常用的数据交换格式,但在语法和使用方面存在一些区别。

  1. 语法形式:

    • XML 使用自定义的标签来描述数据结构,标签包括开始标签 <tag>、结束标签 </tag> 和自闭合标签 <tag/>
    • JSON 使用键值对的形式描述数据,键使用双引号括起来,值可以是字符串、数字、布尔值、数组、对象等。
  2. 可读性:

    • XML 设计初衷是作为一个具有良好可读性的文本格式,适合人阅读和编写。
    • JSON 相对于 XML 更加紧凑和简洁,也比较易于阅读,但相对于 XML 稍弱一些。
  3. 数据类型支持:

    • XML 没有内置的数据类型,所有数据都是以文本的形式呈现。需要对数据进行解析和转换才能在程序中使用。
    • JSON 支持基本数据类型(字符串、数字、布尔值、null),并且可以表示数组和对象,更直接地映射到大多数编程语言中的数据结构。
  4. 扩展性:

    • XML 具有自定义标签的能力,可以根据需求扩展新的标签和规则。
    • JSON 是一种简单的键值对结构,没有官方的扩展机制。如果需要扩展,通常会通过约定和协商来实现。
  5. 应用场景:

    • XML 在许多传统的应用领域如电子数据交换(EDI)、配置文件、文档解析等方面有广泛应用。
    • JSON 由于其轻量且易于解析的特点,在Web开发中被广泛使用,尤其是与 JavaScript 语言结合使用,以实现数据传输和前后端之间的交互。

总之,XML 和 JSON 都有各自适用的场景,选择使用哪种格式取决于具体需求、编程环境和团队协作方式。

6.谈谈你对webpack的看法

Webpack 是一个强大的模块打包工具,主要用于 JavaScript 应用程序的模块化管理和构建。以下是我对 Webpack 的看法:

  1. 模块化管理:Webpack 支持将代码拆分成多个模块,并通过依赖关系进行管理。它能够识别模块之间的依赖关系,自动构建依赖图,并将所有模块打包成最终的输出文件。这使得我们可以在项目中使用模块化的方式组织代码,提高代码的可维护性和复用性。

  2. 强大的打包能力:Webpack 不仅支持 JavaScript 文件的打包,还可以处理其他类型的文件,如 CSS、图片、字体等。通过加载器(Loader)可以实现各种文件类型的转换和处理,例如使用 Babel 对 ES6+ 语法进行转换,使用样式加载器处理并打包样式文件等。这为开发者提供了很大的灵活性和便利性。

  3. 插件系统:Webpack 的插件系统使得开发者可以根据需求进行扩展和定制。许多社区提供的插件可用于优化打包结果、自动生成 HTML 文件、提取公共代码、压缩代码等。通过使用插件,我们可以更好地应对不同的开发场景和优化需求。

  4. 开发环境和生产环境的支持:Webpack 提供了开发环境和生产环境的配置选项。在开发环境中,可以使用热模块替换(HMR)功能实时更新修改的模块,提高开发效率。在生产环境中,可以对代码进行优化和压缩,生成更小、更高效的构建文件。

  5. 庞大的社区支持:Webpack 是目前最受欢迎的前端构建工具之一,拥有庞大而活跃的开发者社区。社区提供了大量的文档、教程、插件和工具,为开发者提供了很多支持和解决问题的

7.new关键字定义一个变量的流程

当使用new关键字创建一个对象时,会经过以下几个步骤:

  1. 分配内存空间:根据对象的类型,在内存中分配足够的空间来存储对象的成员变量。
  2. 初始化成员变量:根据对象的类型,对成员变量进行初始化。如果有默认构造函数,则使用默认值初始化;如果有自定义构造函数,则使用构造函数进行初始化。
  3. 执行构造函数:如果有自定义构造函数,会执行该构造函数。构造函数可以完成一些对象的初始化工作,如设置初始值、分配资源等。
  4. 返回对象的引用:new表达式返回对象的引用,可以将其赋值给一个变量,以便后续使用。
    以下是一个示例代码:

public class MyClass {
    
    
    private int myVariable;
    
    public MyClass(int value) {
    
    
        myVariable = value;
    }
    
    public int getMyVariable() {
    
    
        return myVariable;
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        MyClass obj = new MyClass(10);
        System.out.println(obj.getMyVariable()); // 输出:10
    }
}

在上面的代码中,new MyClass(10)创建了一个MyClass对象,并调用了带有一个整数参数的构造函数来初始化myVariable成员变量。最后,将对象的引用赋值给obj变量,并通过调用getMyVariable()方法获取myVariable的值。

猜你喜欢

转载自blog.csdn.net/qq_53509791/article/details/131673305
今日推荐