前端面试题详解整理3手写ajax请求axios底层原理,文件上底层,登录场常用的数组方法及其使用场景,数组插值实现,原型链,bind,call,apply的用法

53 阅读10分钟

记录:一次前端实习面试_牛客网 (nowcoder.com)

CSS 垂直居中的七种方法 - 个人文章 - SegmentFault 思否 50%的外框高度,-50%的div高度

image.png

CSS盒模型 实现垂直居中position的几个值BFC ES6新特性手写ajax请求 ES6(ECMAScript 2015)引入了许多新特性,其中包括箭头函数、模板字符串、解构赋值、let和const声明、类和模块等。下面是使用ES6语法手写一个简单的Ajax请求的示例:

// 使用fetch API进行Ajax请求
function fetchAjax(url, options) {
    return fetch(url, options)
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            // 处理返回的数据
            console.log(data);
        })
        .catch(error => {
            // 处理请求错误
            console.error('There was a problem with the fetch operation:', error);
        });
}

// 调用fetchAjax函数发送Ajax请求
fetchAjax('https://jsonplaceholder.typicode.com/posts/1', {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json'
    }
});

在这个示例中,我们使用了ES6的箭头函数、Promise以及fetch API来实现Ajax请求。fetch函数是浏览器提供的原生API,用于发送网络请求。fetch函数返回一个Promise对象,我们可以通过.then()方法来处理请求成功的回调和.catch()方法来处理请求失败的回调。

axios底层原理

Axios 是一个基于 Promise 的 HTTP 客户端,可以用于浏览器和 Node.js。 它的底层原理主要基于浏览器提供的 XMLHttpRequest 对象或者是 Node.js 提供的 http 模块。

以下是 Axios 底层原理的一般流程:

  1. 创建请求对象:Axios 在发送请求之前会创建一个 XMLHttpRequest 对象(在浏览器中)或者使用 Node.js 提供的 http 模块(在 Node.js 中)来进行网络通信。

  2. 配置请求:用户使用 Axios 发送请求时,可以通过一系列配置选项来配置请求,例如 URL、请求方法、请求头、请求参数等。

  3. 发送请求:一旦请求对象被配置完成,Axios 就会使用该对象来发送网络请求。 在浏览器中,Axios 使用 XMLHttpRequest 对象的 send() 方法来发送请求; 在 Node.js 中,Axios 使用 http 模块的 request() 方法来发送请求。

  4. 接收响应:一旦服务器返回响应,Axios 会检测响应状态码,并根据状态码来判断请求的成功或失败。如果请求成功,Axios 将会解析服务器返回的数据,并将其封装成 Promise 对象的 resolve 状态;如果请求失败,Axios 将会将错误信息封装成 Promise 对象的 reject 状态。

  5. 处理响应:在接收到响应后,Axios 会根据用户配置的响应拦截器来对响应进行处理。这些处理可能包括对响应数据的转换、错误处理等。

  6. 返回结果:最终,Axios 会将处理后的响应数据返回给调用方,并通过 Promise 对象的 resolve 或 reject 方法来通知调用方请求的结果。

总的来说,Axios 的底层原理主要是通过 XMLHttpRequest 对象(浏览器中)或 http 模块(Node.js 中)来实现网络请求,并通过 Promise 对象来处理请求和响应。同时,Axios 还提供了一系列的配置选项和拦截器,使得请求和响应的处理更加灵活和可控。

说几个数组方法以及使用场景 以下是几个常用的数组方法及其使用场景:

  1. map() 方法

    • 使用场景:用于对数组中的每个元素执行指定操作,返回一个新的数组,常用于数据转换和提取。
    • 示例:
      const numbers = [1, 2, 3, 4, 5];
      const squaredNumbers = numbers.map(num => num * num);
      console.log(squaredNumbers); // [1, 4, 9, 16, 25]
      
  2. filter() 方法

    • 使用场景:用于过滤数组中的元素,返回满足条件的元素组成的新数组。
    • 示例:
      const numbers = [1, 2, 3, 4, 5];
      const evenNumbers = numbers.filter(num => num % 2 === 0);
      console.log(evenNumbers); // [2, 4]
      
  3. reduce() 方法

    • 使用场景:用于对数组中的所有元素执行指定的 reducer 函数,将它们归约为单个值。
    • 示例:
      const numbers = [1, 2, 3, 4, 5];
      const sum = numbers.reduce((acc, curr) => acc + curr, 0);
      console.log(sum); // 15
      
  4. forEach() 方法

    • 使用场景:用于对数组中的每个元素执行指定操作,但不会返回新的数组。
    • 示例:
      const numbers = [1, 2, 3, 4, 5];
      numbers.forEach(num => console.log(num * 2));
      // 输出:2, 4, 6, 8, 10
      
  5. find() 方法

    • 使用场景:用于查找数组中满足指定条件的第一个元素。
    • 示例:
      const numbers = [1, 2, 3, 4, 5];
      const found = numbers.find(num => num > 2);
      console.log(found); // 3
      

这些方法是 JavaScript 中常用的数组方法,可以帮助你对数组进行各种操作,从而简化代码并提高效率。

数组插值实现 数组插值是指在数组中特定位置插入新的元素。你可以使用不同的方法来实现数组插值,以下是一种常见的方法:

// 定义原始数组
let arr = [1, 2, 3, 4, 5];

// 要插入的值和位置
let valueToInsert = 10;
let indexToInsert = 2; // 在索引为2的位置插入新值

// 方法一:使用splice()方法插入
arr.splice(indexToInsert, 0, valueToInsert);
console.log(arr); // [1, 2, 10, 3, 4, 5]

// 方法二:使用slice()方法和concat()方法生成新数组
arr = arr.slice(0, indexToInsert).concat(valueToInsert, arr.slice(indexToInsert));
console.log(arr); // [1, 2, 10, 3, 4, 5]

// 方法三:使用slice()方法和spread运算符(...)生成新数组
arr = [...arr.slice(0, indexToInsert), valueToInsert, ...arr.slice(indexToInsert)];
console.log(arr); // [1, 2, 10, 3, 4, 5]

以上三种方法都可以实现在指定位置插入新值到数组中。你可以根据具体的情况选择最适合的方法。

原型链 原型链是JavaScript中用于实现对象之间继承关系的一种机制。在JavaScript中,每个对象都有一个指向其原型(prototype)的内部链接。当试图访问对象的属性或方法时,如果对象本身没有定义这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的顶端(Object.prototype)。

下面是原型链的基本原理和实现方式:

  1. 原型:每个JavaScript对象都有一个原型对象,可以通过__proto__属性来访问。原型对象也是一个普通的对象,它包含了一些属性和方法。当访问对象的属性或方法时,如果对象本身没有定义,JavaScript引擎会沿着原型链向上查找。

  2. 原型链:原型链是由对象的原型构成的链式结构。每个对象都有一个指向其原型的链接,形成了一个从当前对象到顶层Object.prototype对象的链条。当访问对象的属性或方法时,JavaScript引擎会沿着这条链向上查找,直到找到对应的属性或方法或者到达链的顶端。

  3. 构造函数和原型对象:在JavaScript中,通过构造函数和原型对象来创建对象和实现继承。构造函数用于创建对象实例,而原型对象用于存储共享的属性和方法。每个通过构造函数创建的对象都会有一个指向构造函数的原型对象的链接。

  4. 继承关系:通过原型链,子对象可以继承父对象的属性和方法。当试图访问子对象的属性或方法时,如果子对象本身没有定义,JavaScript引擎会沿着原型链向上查找,直到找到对应的属性或方法或者到达链的顶端。

原型链是JavaScript中非常重要的一部分,它使得对象之间可以共享属性和方法,实现了对象的继承和复用。

bind,call,apply的用法文件上传底层实现登录场景实现

  1. bind、call、apply的用法

    • bind:用于创建一个新的函数,并指定该函数中的this值。它不会立即执行函数,而是返回一个新的函数。例如:const boundFunction = originalFunction.bind(thisArg, arg1, arg2);
    • call:用于在指定的this值和参数下调用函数。它会立即执行函数。例如:originalFunction.call(thisArg, arg1, arg2);
    • apply:与call类似,不同之处在于参数的传递方式。apply接收一个数组作为参数列表。例如:originalFunction.apply(thisArg, [arg1, arg2]);
  2. 文件上传底层实现: 文件上传的底层实现通常涉及以下几个步骤:

    • 创建一个包含文件上传表单的HTML元素,用户通过该表单选择要上传的文件。
    • 用户选择文件后,JavaScript代码通过监听文件选择事件(例如change事件),获取用户选择的文件对象。
    • 使用XMLHttpRequest(XHR)或Fetch API等技术,将文件发送到服务器。在发送文件之前,通常需要创建一个FormData对象,将文件对象添加到FormData中。
    • 服务器接收到文件后,根据具体业务逻辑进行处理,例如保存文件到服务器的文件系统中,或者将文件内容存储到数据库中。
    • 服务器处理完成后,通常会返回一个响应给客户端,以告知客户端文件上传是否成功。
  3. 登录场景实现: 前端登录场景的实现通常涉及以下几个步骤:

    • 创建一个包含用户名、密码输入框和登录按钮的HTML表单。
    • 用户输入用户名和密码后,通过JavaScript代码监听登录按钮的点击事件。
    • 当用户点击登录按钮时,JavaScript代码获取用户名和密码输入框中的值,并通过XMLHttpRequest(XHR)或Fetch API等技术,将用户名和密码发送给服务器。
    • 服务器接收到用户名和密码后,进行身份验证,验证成功则生成一个Token并返回给客户端,验证失败则返回错误信息。
    • 客户端收到服务器返回的Token后,通常会将Token存储在本地,例如LocalStorage或Cookie中。
    • 后续的请求中,客户端会在请求头中携带Token,以便服务器进行身份验证和授权。

以上是简要的回答,如需更详细的解释或示例代码,请告知。

token的作用和使用 Token(令牌)在网络安全中起着重要的作用,主要用于身份验证和授权。

  1. 身份验证(Authentication):Token用于验证用户的身份。当用户成功登录后,服务器会颁发一个Token给客户端,客户端在后续的请求中会携带这个Token,以证明自己的身份。常见的Token包括JWT(JSON Web Token)、OAuth Token等。

  2. 授权(Authorization):Token也用于授权,即确定用户对资源的访问权限。通过Token可以确定用户是否有权限访问某个资源,以及具体的操作权限。

Token的使用流程通常如下:

  1. 用户登录:用户提供用户名和密码进行登录,服务器验证用户身份后,颁发一个Token给客户端。

  2. Token存储:客户端通常会将Token存储在本地,比如LocalStorage、SessionStorage、Cookie等。

  3. 后续请求:客户端在后续的请求中携带这个Token,通常放在请求头的Authorization字段中,格式如:Bearer {token}。

  4. 服务端验证:服务端接收到请求后,会验证Token的合法性和有效期,以确定用户的身份和权限。如果Token合法且未过期,则允许用户访问资源。

  5. Token更新:Token通常有一个有效期限,一旦过期需要重新登录获取新的Token。有些系统会实现Token的自动刷新机制,以提高用户体验。

通过Token,可以实现无状态(Stateless)的认证和授权,即服务器不需要在内存中保存用户的登录状态,减轻了服务器的负担,同时也增强了系统的安全性。