美团前端日常二面

138 阅读25分钟

美团前端日常二面

6.6日一面1小时后约了6.11二面,二面如下:

1.自我介绍
2.怎么自学前端的
3.css水平居中的方式
在CSS中,有多种方式可以实现水平居中。以下是几种常见的方法:

1. 使用 text-align 属性

适用于行内元素或行内块级元素。

<div style="text-align: center;">
  <span>这是一个行内元素</span>
</div>

2. 使用 margin: auto

适用于块级元素,需要设置宽度。

<div style="width: 50%; margin: 0 auto;">
  这是一个块级元素
</div>

3. 使用 flexbox

适用于任何类型的元素。

<div style="display: flex; justify-content: center;">
  <div>这是一个块级元素</div>
</div>

4. 使用 grid

适用于任何类型的元素。

<div style="display: grid; place-items: center;">
  <div>这是一个块级元素</div>
</div>

5. 使用绝对定位和 transform

适用于任何类型的元素。

<div style="position: relative;">
  <div style="position: absolute; left: 50%; transform: translateX(-50%);">
    这是一个块级元素
  </div>
</div>

6. 使用 inline-blocktext-align

适用于块级元素。

<div style="text-align: center;">
  <div style="display: inline-block;">
    这是一个块级元素
  </div>
</div>

7. 使用 table 布局

适用于块级元素。

<div style="display: table; margin: 0 auto;">
  这是一个块级元素
</div>

每种方法都有其适用的场景和优缺点,选择合适的方法可以根据具体需求和布局情况来决定。 4.css关于颜色的代码有哪些,尽可能多的说
CSS中关于颜色的代码有很多种,以下是一些常见的颜色表示方法和相关属性:

颜色表示方法

  1. 十六进制颜色值

    color: #ff0000; /* 红色 */
    color: #00ff00; /* 绿色 */
    color: #0000ff; /* 蓝色 */
    
  2. RGB颜色值

    color: rgb(255, 0, 0); /* 红色 */
    color: rgb(0, 255, 0); /* 绿色 */
    color: rgb(0, 0, 255); /* 蓝色 */
    
  3. RGBA颜色值(带透明度)

    color: rgba(255, 0, 0, 0.5); /* 半透明红色 */
    color: rgba(0, 255, 0, 0.5); /* 半透明绿色 */
    color: rgba(0, 0, 255, 0.5); /* 半透明蓝色 */
    
  4. HSL颜色值

    color: hsl(0, 100%, 50%); /* 红色 */
    color: hsl(120, 100%, 50%); /* 绿色 */
    color: hsl(240, 100%, 50%); /* 蓝色 */
    
  5. HSLA颜色值(带透明度)

    color: hsla(0, 100%, 50%, 0.5); /* 半透明红色 */
    color: hsla(120, 100%, 50%, 0.5); /* 半透明绿色 */
    color: hsla(240, 100%, 50%, 0.5); /* 半透明蓝色 */
    
  6. 预定义颜色名

    color: red; /* 红色 */
    color: green; /* 绿色 */
    color: blue; /* 蓝色 */
    

相关属性

  1. 文本颜色

    color: #ff0000; /* 设置文本颜色为红色 */
    
  2. 背景颜色

    background-color: #00ff00; /* 设置背景颜色为绿色 */
    
  3. 边框颜色

    border-color: #0000ff; /* 设置边框颜色为蓝色 */
    
  4. 渐变背景

    background: linear-gradient(to right, red, yellow); /* 从红色到黄色的线性渐变 */
    background: radial-gradient(circle, red, yellow); /* 从红色到黄色的径向渐变 */
    
  5. 阴影颜色

    box-shadow: 10px 10px 5px #888888; /* 设置盒子阴影颜色 */
    text-shadow: 2px 2px 4px #000000; /* 设置文本阴影颜色 */
    
  6. 透明度

    opacity: 0.5; /* 设置元素透明度为50% */
    
  7. 边框颜色(单独设置每个边)

    border-top-color: red; /* 设置上边框颜色 */
    border-right-color: green; /* 设置右边框颜色 */
    border-bottom-color: blue; /* 设置下边框颜色 */
    border-left-color: yellow; /* 设置左边框颜色 */
    
  8. 背景图像的叠加颜色

    background: url('image.jpg') no-repeat center center;
    background-color: rgba(0, 0, 0, 0.5); /* 叠加半透明黑色 */
    

这些是CSS中常见的颜色表示方法和相关属性。通过这些方法,你可以灵活地控制网页元素的颜色和透明度。

5.js中let,const,var的区别
在JavaScript中,letconstvar是用于声明变量的关键字,它们之间有一些重要的区别。以下是它们的主要区别:

1. 作用域(Scope)

  • var:

    • 函数作用域:var声明的变量在函数内部是局部变量,在函数外部是全局变量。
    • 变量提升:var声明的变量会被提升到函数或全局作用域的顶部,但不会初始化。
  • let:

    • 块级作用域:let声明的变量在块级作用域(例如,{}内部)内是局部变量。
    • 变量提升:let声明的变量也会被提升,但不会初始化,使用前必须声明。
  • const:

    • 块级作用域:const声明的变量在块级作用域内是局部变量。
    • 变量提升:const声明的变量也会被提升,但不会初始化,使用前必须声明。

2. 重新赋值(Reassignment)

  • var:

    • 可以重新赋值和重新声明。
  • let:

    • 可以重新赋值,但不能重新声明。
  • const:

    • 不能重新赋值,也不能重新声明。const声明的变量必须在声明时初始化。

3. 常量(Constants)

  • const:
    • const声明的变量是常量,意味着它的值在初始化后不能改变。
    • 对于对象和数组,const声明的变量指向的引用不能改变,但对象的属性或数组的元素是可以改变的。

示例代码

// var 示例
function varExample() {
  if (true) {
    var x = 10;
  }
  console.log(x); // 10
}
varExample();

// let 示例
function letExample() {
  if (true) {
    let y = 20;
    console.log(y); // 20
  }
  // console.log(y); // ReferenceError: y is not defined
}
letExample();

// const 示例
function constExample() {
  const z = 30;
  // z = 40; // TypeError: Assignment to constant variable.
  console.log(z); // 30

  const obj = { a: 1 };
  obj.a = 2; // 允许修改对象的属性
  console.log(obj.a); // 2
}
constExample();

总结

  • 使用var时要小心变量提升和全局污染的问题。
  • letconst提供了块级作用域,避免了var的一些问题。
  • const用于声明常量,确保变量不会被重新赋值。

在现代JavaScript开发中,推荐优先使用letconst,而不是var

6.js中数组方法有哪些了,怎么使用
在JavaScript中,数组方法有很多,以下是一些常用的数组方法及其使用示例:

1. push()

向数组的末尾添加一个或多个元素,并返回新的长度。

let arr = [1, 2, 3];
arr.push(4); // [1, 2, 3, 4]

2. pop()

删除数组的最后一个元素,并返回该元素。

let arr = [1, 2, 3];
let lastElement = arr.pop(); // 3, arr变为[1, 2]

3. shift()

删除数组的第一个元素,并返回该元素。

let arr = [1, 2, 3];
let firstElement = arr.shift(); // 1, arr变为[2, 3]

4. unshift()

向数组的开头添加一个或多个元素,并返回新的长度。

let arr = [1, 2, 3];
arr.unshift(0); // [0, 1, 2, 3]

5. concat()

合并两个或多个数组,不改变现有数组,返回一个新数组。

let arr1 = [1, 2];
let arr2 = [3, 4];
let newArr = arr1.concat(arr2); // [1, 2, 3, 4]

6. slice()

返回一个从开始到结束(不包括结束)选择的数组的一部分。

let arr = [1, 2, 3, 4, 5];
let slicedArr = arr.slice(1, 3); // [2, 3]

7. splice()

通过删除或替换现有元素或者添加新元素来修改数组。

let arr = [1, 2, 3, 4, 5];
arr.splice(2, 1, 'a', 'b'); // [1, 2, 'a', 'b', 4, 5]

8. forEach()

对数组的每个元素执行一次提供的函数。

let arr = [1, 2, 3];
arr.forEach(element => console.log(element)); // 1 2 3

9. map()

创建一个新数组,其结果是该数组中的每个元素调用一个提供的函数后返回的结果。

let arr = [1, 2, 3];
let newArr = arr.map(x => x * 2); // [2, 4, 6]

10. filter()

创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

let arr = [1, 2, 3, 4, 5];
let filteredArr = arr.filter(x => x > 2); // [3, 4, 5]

11. reduce()

对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。

let arr = [1, 2, 3, 4];
let sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // 10

12. find()

返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

let arr = [1, 2, 3, 4];
let found = arr.find(element => element > 2); // 3

13. findIndex()

返回数组中满足提供的测试函数的第一个元素的索引。否则返回 -1

let arr = [1, 2, 3, 4];
let index = arr.findIndex(element => element > 2); // 2

14. includes()

判断一个数组是否包含一个指定的值,根据情况返回 truefalse

let arr = [1, 2, 3];
let hasTwo = arr.includes(2); // true

15. sort()

对数组的元素进行排序并返回数组。

let arr = [3, 1, 4, 1, 5, 9];
arr.sort((a, b) => a - b); // [1, 1, 3, 4, 5, 9]

16. reverse()

颠倒数组中元素的顺序。

let arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]

17. join()

将数组的所有元素连接成一个字符串。

let arr = [1, 2, 3];
let str = arr.join('-'); // "1-2-3"

18. every()

测试数组的所有元素是否都通过了指定函数的测试。

let arr = [1, 2, 3, 4];
let allLessThanFive = arr.every(x => x < 5); // true

19. some()

测试数组中的至少一个元素是否通过了指定函数的测试。

let arr = [1, 2, 3, 4];
let someGreaterThanThree = arr.some(x => x > 3); // true

这些方法可以帮助你在JavaScript中高效地操作数组。希望这些示例对你有所帮助!

7.介绍一下项目中登录的详细过程,怎么使用验证码登录的?怎么区分的权限
在一个项目中,登录过程通常涉及用户身份验证和权限管理。以下是详细的登录过程,包括使用验证码登录和权限区分的步骤:

登录过程

1. 用户输入凭证

用户在登录页面输入用户名和密码,或者选择使用验证码登录。

2. 验证用户凭证

服务器接收到用户输入的凭证后,进行以下验证步骤:

2.1 用户名和密码验证

  • 查找用户:在数据库中查找与输入的用户名匹配的用户记录。
  • 验证密码:使用哈希算法验证输入的密码是否与存储在数据库中的哈希密码匹配。

2.2 验证码登录

  • 生成验证码:服务器生成一个随机验证码,并将其发送到用户的手机或邮箱。
  • 用户输入验证码:用户在登录页面输入收到的验证码。
  • 验证验证码:服务器验证用户输入的验证码是否与发送的一致,并且在有效期内。

3. 生成会话或令牌

验证成功后,服务器生成一个会话(Session)或令牌(Token),并将其返回给客户端。客户端将会话ID或令牌存储在Cookie或本地存储中,以便在后续请求中使用。

4. 权限管理

在用户成功登录后,系统需要根据用户的角色或权限进行相应的权限管理。

4.1 获取用户角色

  • 查找角色:在数据库中查找与用户关联的角色信息。
  • 加载权限:根据用户的角色加载相应的权限列表。

4.2 权限验证

  • 请求拦截:在每次用户请求时,服务器拦截请求并验证用户的权限。
  • 权限检查:根据用户的角色和权限列表,检查用户是否有权访问请求的资源或执行特定操作。

5. 响应客户端

服务器根据验证结果和权限检查结果,返回相应的响应给客户端。

示例代码

以下是一个简单的示例代码,展示了如何实现上述登录过程:

后端(Node.js + Express)

const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const bcrypt = require('bcrypt');
const app = express();

app.use(bodyParser.json());
app.use(session({
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: true
}));

// 模拟数据库
const users = [
  { id: 1, username: 'user1', password: '$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36Zf4a2Z8F4Z4Z4Z4Z4Z4Z4', role: 'admin' }, // 密码为 'password'
  { id: 2, username: 'user2', password: '$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36Zf4a2Z8F4Z4Z4Z4Z4Z4Z4', role: 'user' }
];

// 登录接口
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username);
  if (user && await bcrypt.compare(password, user.password)) {
    req.session.userId = user.id;
    req.session.role = user.role;
    res.json({ message: '登录成功' });
  } else {
    res.status(401).json({ message: '用户名或密码错误' });
  }
});

// 权限检查中间件
function checkRole(role) {
  return (req, res, next) => {
    if (req.session.role === role) {
      next();
    } else {
      res.status(403).json({ message: '权限不足' });
    }
  };
}

// 受保护的路由
app.get('/admin', checkRole('admin'), (req, res) => {
  res.json({ message: '欢迎管理员' });
});

app.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});

前端(HTML + JavaScript)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>登录页面</title>
</head>
<body>
  <form id="loginForm">
    <input type="text" id="username" placeholder="用户名" required>
    <input type="password" id="password" placeholder="密码" required>
    <button type="submit">登录</button>
  </form>

  <script>
    document.getElementById('loginForm').addEventListener('submit', async (e) => {
      e.preventDefault();
      const username = document.getElementById('username').value;
      const password = document.getElementById('password').value;

      const response = await fetch('/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
      });

      const result = await response.json();
      if (response.ok) {
        alert(result.message);
      } else {
        alert(result.message);
      }
    });
  </script>
</body>
</html>

以上示例展示了一个基本的登录过程,包括用户名和密码验证、会话管理以及权限检查。实际项目中可能需要根据具体需求进行更多的安全措施和功能扩展。

8.vue的生命周期有哪些?
Vue.js 的生命周期钩子函数是指在 Vue 实例的不同阶段会自动调用的一些函数。了解这些生命周期钩子函数有助于在适当的时机执行特定的操作。以下是 Vue.js 的生命周期钩子函数:

  1. beforeCreate:实例初始化之后,数据观测 (data observer) 和事件配置之前被调用。
  2. created:实例已经创建完成,数据观测和事件配置完成,但 DOM 还未生成,$el 属性还不可用。
  3. beforeMount:在挂载开始之前被调用,相关的 render 函数首次被调用。
  4. mounted:实例被挂载到 DOM 上后调用,$el 属性可用。
  5. beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
  6. updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。
  7. beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
  8. destroyed:实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁。

以下是一个简单的示例,展示了如何在 Vue 组件中使用这些生命周期钩子函数:

<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue!'
    };
  },
  beforeCreate() {
    console.log('beforeCreate');
  },
  created() {
    console.log('created');
  },
  beforeMount() {
    console.log('beforeMount');
  },
  mounted() {
    console.log('mounted');
  },
  beforeUpdate() {
    console.log('beforeUpdate');
  },
  updated() {
    console.log('updated');
  },
  beforeDestroy() {
    console.log('beforeDestroy');
  },
  destroyed() {
    console.log('destroyed');
  }
};
</script>

在这个示例中,每个生命周期钩子函数都会在控制台输出一条消息,帮助你了解每个钩子函数在组件生命周期中的调用顺序。

9.keepalive有哪些属性
keep-alive 是一个在计算机网络中使用的技术,用于保持一个网络连接在一段时间内处于活动状态,而不是在每次请求后立即关闭。这样可以减少连接建立和关闭的开销,提高网络通信的效率。以下是一些常见的 keep-alive 使用场景:

HTTP Keep-Alive

在 HTTP 协议中,keep-alive 头部用于保持客户端和服务器之间的连接。默认情况下,HTTP/1.1 是启用 keep-alive 的。以下是一个示例:

GET /index.html HTTP/1.1
Host: www.example.com
Connection: keep-alive

TCP Keep-Alive

TCP 协议也支持 keep-alive,用于检测空闲连接是否仍然有效。TCP keep-alive 选项通常由操作系统配置。例如,在 Linux 系统中,可以通过以下命令设置:

sysctl -w net.ipv4.tcp_keepalive_time=600
sysctl -w net.ipv4.tcp_keepalive_intvl=60
sysctl -w net.ipv4.tcp_keepalive_probes=5

WebSocket Keep-Alive

在 WebSocket 通信中,keep-alive 机制可以通过发送心跳消息来实现,以确保连接的持续性。以下是一个简单的 JavaScript 示例:

const socket = new WebSocket('ws://example.com/socket');

socket.onopen = function() {
    setInterval(function() {
        socket.send(JSON.stringify({ type: 'ping' }));
    }, 30000); // 每30秒发送一次心跳消息
};

socket.onmessage = function(event) {
    const message = JSON.parse(event.data);
    if (message.type === 'pong') {
        console.log('Received pong');
    }
};

数据库连接池

在数据库连接池中,keep-alive 机制用于保持数据库连接的活跃状态,减少频繁的连接建立和关闭操作。例如,在 MySQL 中,可以通过配置连接池参数来实现:

const mysql = require('mysql');
const pool = mysql.createPool({
    connectionLimit: 10,
    host: 'example.com',
    user: 'user',
    password: 'password',
    database: 'database',
    // Keep-alive settings
    acquireTimeout: 30000,
    connectTimeout: 10000,
    waitForConnections: true,
    queueLimit: 0
});

总结

keep-alive 技术在各种网络协议和应用中都有广泛的应用,主要目的是提高连接的效率和稳定性。通过合理配置 keep-alive 参数,可以显著提升系统的性能和用户体验。

10.keepalive的原理是什么

keepalive 是一种网络通信技术,用于保持一个已经建立的连接处于活动状态,即使在没有数据传输的情况下。其主要目的是防止连接因长时间没有数据传输而被网络设备(如路由器、防火墙等)自动关闭。以下是 keepalive 的基本原理:

1. 定期发送探测包

keepalive 机制会在连接空闲时,定期发送小的探测包(通常是空数据包或特定的 keepalive 包)到对端。这个探测包的作用是告诉对端和中间的网络设备,这个连接仍然是活跃的,不要关闭它。

2. 响应探测包

对端收到 keepalive 探测包后,会发送一个响应包,确认连接仍然有效。如果对端没有响应,发送方会重试几次(具体次数和间隔时间可以配置),如果仍然没有响应,则认为连接已经断开。

3. 配置参数

keepalive 机制通常可以配置以下参数:

  • 探测间隔(Keepalive Interval):发送探测包的时间间隔。
  • 探测次数(Keepalive Probes):在放弃连接前,重试发送探测包的次数。
  • 空闲时间(Keepalive Time):连接空闲多长时间后开始发送探测包。

4. 应用场景

keepalive 机制广泛应用于各种网络协议和应用中,例如:

  • TCP Keepalive:在 TCP 协议中,keepalive 选项用于保持 TCP 连接。
  • HTTP Keepalive:在 HTTP 协议中,keepalive 头部用于保持 HTTP 连接,减少连接建立和关闭的开销。
  • WebSocket Keepalive:在 WebSocket 协议中,keepalive 机制用于保持长连接。

5. 实现示例

以 TCP Keepalive 为例,以下是一些常见的配置命令(以 Linux 系统为例):

# 设置全局 TCP Keepalive 空闲时间(单位:秒)
sysctl -w net.ipv4.tcp_keepalive_time=7200

# 设置全局 TCP Keepalive 探测间隔(单位:秒)
sysctl -w net.ipv4.tcp_keepalive_intvl=75

# 设置全局 TCP Keepalive 探测次数
sysctl -w net.ipv4.tcp_keepalive_probes=9

总结

keepalive 机制通过定期发送探测包来保持连接的活跃状态,防止因长时间没有数据传输而导致连接被关闭。它在各种网络协议和应用中都有广泛的应用,确保了长时间的稳定连接。

<keep-alive> 是 Vue.js 中的一个内置组件,用于在组件切换过程中保留组件的状态或避免重新渲染。它通常与动态组件或路由组件一起使用,以提高性能和用户体验。

基本用法

<template>
  <div id="app">
    <button @click="currentView = 'componentA'">组件 A</button>
    <button @click="currentView = 'componentB'">组件 B</button>
    <keep-alive>
      <component :is="currentView"></component>
    </keep-alive>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentView: 'componentA'
    };
  },
  components: {
    componentA: ComponentA,
    componentB: ComponentB
  }
};
</script>

在这个例子中,当你在 ComponentAComponentB 之间切换时,<keep-alive> 会缓存这两个组件的状态,而不是每次都重新创建它们。

属性

  • include:字符串或正则表达式,只有名称匹配的组件会被缓存。
  • exclude:字符串或正则表达式,名称匹配的组件不会被缓存。
  • max:数字,最多可以缓存多少组件实例。

示例

使用 includeexclude

<template>
  <div id="app">
    <button @click="currentView = 'componentA'">组件 A</button>
    <button @click="currentView = 'componentB'">组件 B</button>
    <button @click="currentView = 'componentC'">组件 C</button>
    <keep-alive include="componentA,componentB">
      <component :is="currentView"></component>
    </keep-alive>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
import ComponentC from './ComponentC.vue';

export default {
  data() {
    return {
      currentView: 'componentA'
    };
  },
  components: {
    componentA: ComponentA,
    componentB: ComponentB,
    componentC: ComponentC
  }
};
</script>

在这个例子中,只有 ComponentAComponentB 会被缓存,而 ComponentC 不会被缓存。

使用 max

<template>
  <div id="app">
    <button @click="currentView = 'componentA'">组件 A</button>
    <button @click="currentView = 'componentB'">组件 B</button>
    <button @click="currentView = 'componentC'">组件 C</button>
    <keep-alive :max="2">
      <component :is="currentView"></component>
    </keep-alive>
  </div>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
import ComponentC from './ComponentC.vue';

export default {
  data() {
    return {
      currentView: 'componentA'
    };
  },
  components: {
    componentA: ComponentA,
    componentB: ComponentB,
    componentC: ComponentC
  }
};
</script>

在这个例子中,最多会缓存两个组件实例。如果你在 ComponentAComponentBComponentC 之间切换,最早缓存的组件实例会被移除。

生命周期钩子

<keep-alive> 组件还提供了两个特定的生命周期钩子:

  • activated:当组件被激活时调用。
  • deactivated:当组件被停用时调用。
<template>
  <div>我是一个需要缓存的组件</div>
</template>

<script>
export default {
  activated() {
    console.log('组件被激活');
  },
  deactivated() {
    console.log('组件被停用');
  }
};
</script>

通过使用 <keep-alive>,你可以显著提高应用的性能,特别是在需要频繁切换视图的场景中。

11.promise是干嘛的,说一说常用的方法

Promise 是 JavaScript 中用于处理异步操作的一种对象。它代表一个在未来某个时间点可能会完成或失败的操作及其结果。Promise 提供了一种更清晰和更简洁的方式来处理异步代码,避免了“回调地狱”(callback hell)。

Promise 的状态

一个 Promise 对象有以下三种状态:

  1. Pending(待定): 初始状态,既不是成功也不是失败。
  2. Fulfilled(已完成): 操作成功完成。
  3. Rejected(已拒绝): 操作失败。

创建 Promise

你可以使用 new Promise 来创建一个新的 Promise 对象。它接受一个执行函数,该函数有两个参数:resolvereject

const myPromise = new Promise((resolve, reject) => {
  // 异步操作
  let success = true; // 假设这是异步操作的结果
  if (success) {
    resolve("操作成功");
  } else {
    reject("操作失败");
  }
});

常用方法

Promise 对象有几个常用的方法:

then()

then() 方法用于在 Promise 成功时执行某个函数。它接受两个参数:一个是成功时的回调函数,另一个是失败时的回调函数(可选)。

myPromise.then(
  (value) => {
    console.log(value); // 操作成功时的处理
  },
  (error) => {
    console.error(error); // 操作失败时的处理
  }
);

catch()

catch() 方法用于在 Promise 失败时执行某个函数。它相当于 then 方法的第二个参数,但更为简洁。

myPromise.catch((error) => {
  console.error(error); // 操作失败时的处理
});

finally()

finally() 方法用于在 Promise 完成(无论成功还是失败)时执行某个函数。

myPromise.finally(() => {
  console.log("操作完成,无论成功还是失败");
});

all()

Promise.all() 方法接受一个 Promise 对象的数组,当所有 Promise 对象都成功时,它返回一个新的 Promise,该 Promise 的结果是一个包含所有成功结果的数组。如果有任何一个 Promise 失败,则返回的 Promise 失败。

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // [3, 42, "foo"]
});

race()

Promise.race() 方法同样接受一个 Promise 对象的数组,但只要其中的一个 Promise 对象完成(无论成功还是失败),它就返回一个新的 Promise,该 Promise 的结果是第一个完成的 Promise 的结果。

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // "two"
});

总结

Promise 提供了一种更优雅的方式来处理异步操作,避免了嵌套的回调函数,使代码更易读和维护。通过 thencatchfinallyallrace 等方法,你可以灵活地处理各种异步场景。

12.同步异步

同步和异步是计算机科学中常见的概念,尤其在编程和系统设计中。以下是它们的详细解释:

同步 (Synchronous)

同步操作是指在执行某个任务时,必须等待该任务完成后才能继续执行后续任务。换句话说,同步操作是按顺序进行的,每个任务必须等待前一个任务完成。

特点:

  1. 顺序执行:任务按顺序一个接一个地执行。
  2. 阻塞:当前任务未完成时,后续任务会被阻塞,无法执行。
  3. 简单易理解:由于任务按顺序执行,逻辑较为简单,容易理解和调试。

示例:

def task1():
    print("Task 1 started")
    # 模拟任务耗时
    time.sleep(2)
    print("Task 1 completed")

def task2():
    print("Task 2 started")
    # 模拟任务耗时
    time.sleep(2)
    print("Task 2 completed")

task1()
task2()

异步 (Asynchronous)

异步操作是指在执行某个任务时,不必等待该任务完成就可以继续执行后续任务。异步操作允许多个任务并发执行,从而提高效率。

特点:

  1. 并发执行:多个任务可以同时进行,不必等待其他任务完成。
  2. 非阻塞:任务可以在等待其他任务完成的同时继续执行其他操作。
  3. 复杂性较高:由于任务是并发执行的,逻辑较为复杂,调试和维护难度较大。

示例:

import asyncio

async def task1():
    print("Task 1 started")
    # 模拟任务耗时
    await asyncio.sleep(2)
    print("Task 1 completed")

async def task2():
    print("Task 2 started")
    # 模拟任务耗时
    await asyncio.sleep(2)
    print("Task 2 completed")

async def main():
    await asyncio.gather(task1(), task2())

asyncio.run(main())

适用场景

  • 同步:适用于任务之间有严格顺序要求的场景,如某些计算任务、文件读写等。
  • 异步:适用于I/O密集型任务,如网络请求、数据库查询等,可以显著提高系统的响应速度和资源利用率。

总结

同步和异步各有优缺点,选择哪种方式取决于具体的应用场景和需求。在实际开发中,常常需要根据任务的特性和系统的性能要求来选择合适的方式。

13.通信方式

通信方式是指信息在不同地点之间传递的手段和方法。根据传输介质和技术的不同,通信方式可以分为以下几类:

1. 有线通信

有线通信是通过物理介质(如铜线、光纤等)进行信息传递的方式。

  • 电话线通信:传统的电话网络使用铜线进行语音和数据传输。
  • 光纤通信:利用光纤传输数据,具有高带宽和低延迟的特点,广泛应用于互联网骨干网和高速数据传输。

2. 无线通信

无线通信是通过电磁波在空中传输信息的方式。

  • 无线电通信:利用无线电波进行信息传递,应用于广播、电视、对讲机等。
  • 蜂窝通信:移动通信技术,如2G、3G、4G、5G,主要用于手机和移动设备的通信。
  • 卫星通信:通过卫星进行长距离通信,适用于地面通信无法覆盖的区域。

3. 数据通信

数据通信是指通过计算机网络进行数据传输的方式。

  • 局域网(LAN):在有限的地理范围内(如办公室、校园)连接计算机和设备。
  • 广域网(WAN):覆盖更大地理范围的网络,如互联网。

4. 光通信

光通信是利用光波进行信息传输的方式,主要包括光纤通信和自由空间光通信。

  • 光纤通信:通过光纤传输数据,具有高带宽和低损耗的特点。
  • 自由空间光通信:利用激光在自由空间中传输数据,适用于短距离高速数据传输。

5. 声音通信

声音通信是通过声波进行信息传递的方式。

  • 电话通信:通过电话网络进行语音传输。
  • 对讲机:利用无线电波进行短距离语音通信。

6. 其他通信方式

  • 红外通信:利用红外线进行短距离数据传输,如遥控器、红外传感器等。
  • 蓝牙通信:短距离无线通信技术,广泛应用于无线耳机、键盘、鼠标等设备。

结论

不同的通信方式有其独特的应用场景和技术特点,选择合适的通信方式可以提高信息传递的效率和可靠性。随着技术的发展,通信方式也在不断演进和创新。

14.持久化存储的方式有哪些
持久化存储是指将数据保存到非易失性存储介质中,以便在系统重启或崩溃后仍能保留数据。以下是几种常见的持久化存储方式:

  1. 文件系统

    • 文本文件:如CSV、JSON、XML等格式的文件。
    • 二进制文件:如图片、视频、音频等。
  2. 数据库

    • 关系型数据库:如MySQL、PostgreSQL、Oracle等,使用SQL进行数据操作。
    • NoSQL数据库:如MongoDB、Cassandra、Redis等,适用于非结构化数据或需要高扩展性的场景。
  3. 对象存储

    • 云存储服务:如Amazon S3、Google Cloud Storage、Azure Blob Storage等,适用于存储大量非结构化数据。
  4. 日志系统

    • 日志文件:如应用程序日志、系统日志等,通常以文本文件形式存储。
    • 日志管理系统:如ELK Stack(Elasticsearch, Logstash, Kibana)、Splunk等,用于集中管理和分析日志数据。
  5. 持久化缓存

    • Redis:支持将内存中的数据持久化到磁盘。
    • Memcached:虽然主要用于缓存,但也可以通过扩展实现持久化。
  6. 消息队列

    • Kafka:支持将消息持久化到磁盘,适用于高吞吐量的消息处理场景。
    • RabbitMQ:支持消息持久化,确保消息在系统崩溃后不会丢失。
  7. 版本控制系统

    • Git:用于代码和文档的版本控制,数据持久化存储在本地或远程仓库中。
  8. 配置管理工具

    • Consul:用于服务发现和配置管理,支持持久化存储配置数据。
    • Etcd:分布式键值存储,用于配置管理和服务发现。
  9. 备份和恢复

    • 备份软件:如Veeam、Acronis等,用于定期备份数据并在需要时恢复。

这些持久化存储方式各有优缺点,选择合适的方式取决于具体的应用场景和需求。

15.手撕:在数组的原型上新加一个去重的方法

在 JavaScript 中,你可以通过向 Array.prototype 添加一个方法来实现数组去重。以下是一个示例代码,展示了如何在数组的原型上添加一个 unique 方法来去重:

// 向 Array.prototype 添加 unique 方法
Array.prototype.unique = function() {
  return [...new Set(this)];
};

// 示例使用
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.unique();
console.log(uniqueArr); // 输出: [1, 2, 3, 4, 5]

在这个示例中,我们使用了 Set 对象来去重。Set 是 ES6 引入的一种新的数据结构,它类似于数组,但是成员的值都是唯一的。通过将数组转换为 Set,然后再转换回数组,我们可以轻松地实现去重功能。

你也可以使用其他方法来实现去重,例如使用 filterindexOf

// 向 Array.prototype 添加 unique 方法
Array.prototype.unique = function() {
  return this.filter((item, index) => this.indexOf(item) === index);
};

// 示例使用
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.unique();
console.log(uniqueArr); // 输出: [1, 2, 3, 4, 5]

这个方法通过 filter 方法遍历数组,并使用 indexOf 检查每个元素第一次出现的位置是否与当前索引相同,从而实现去重。

无论使用哪种方法,最终的效果都是相同的:返回一个去重后的新数组。

16.什么时候能来呢,能实习多长时间

反问环节
1.针对目前我的技术,之后学习的建议
2.美团平台使用的技术栈是什么

八股整体答的还行,手撕也写出来了,期待结果了

作者:奥利li
链接:www.nowcoder.com/discuss/630…
来源:牛客网