前端笔试面试合集2

js类型比较

数组对象是object,用==比较会进行toString转换,所以不能和其他基本类型进行比较,而其他基本类型进行==比较的时候会先转换类型

阻塞和非阻塞网络IO有什么区别

在阻塞I/O模型中,当应用程序发起一个网络I/O请求时,程序会一直等待,直到I/O操作完成并返回结果才会继续执行下一条指令。在这个过程中,程序会被阻塞,无法执行其他任务,直到I/O操作完成。

相比之下,在非阻塞I/O模型中,当应用程序发起一个网络I/O请求时,程序不会一直等待,而是立即返回一个结果(通常是0或者错误码),然后继续执行下一条指令。在这个过程中,程序不会被阻塞,可以执行其他任务,同时通过轮询的方式不断地检查I/O操作是否完成,直到完成后再处理I/O操作的结果。

下面分别举两个例子说明阻塞I/O和非阻塞I/O的应用场景:

  1. 阻塞I/O的应用场景

阻塞I/O适用于需要等待I/O操作完成后才能进行下一步处理的场景,例如:

  • 文件读写:当应用程序需要从磁盘读取大量数据时,阻塞I/O可以确保数据被完整读入内存后再进行处理,避免数据不完整或丢失。
  • 网络连接:当应用程序需要建立一个稳定的网络连接时,阻塞I/O可以确保连接成功后再进行后续的网络通信。
  1. 非阻塞I/O的应用场景

非阻塞I/O适用于需要同时处理多个I/O请求的场景,例如:

  • 网络通信:当应用程序需要同时处理多个客户端的网络请求时,非阻塞I/O可以让程序在等待I/O操作完成的同时处理其他任务,提高程序的并发性能。
  • GUI界面:当应用程序需要响应用户的操作同时又需要进行I/O操作时,非阻塞I/O可以让程序在等待I/O操作完成的同时响应用户的操作,避免界面出现卡顿的情况。

多线程和多进程的程序各有什么优缺点

多线程和多进程是并发编程中两种常用的方式,它们都有优缺点,并且适用于不同的场合。

  1. 多线程的优缺点和适用场合

优点:

  • 线程之间共享内存,数据交换和通信更加方便快捷,不需要使用IPC(进程间通信)。
  • 线程创建和销毁的开销小,执行效率高。
  • 由于共享内存,可以方便地实现数据共享和协同工作。

缺点:

  • 多个线程共享进程的资源,线程之间需要进行同步和互斥操作,否则容易出现数据竞争和死锁等问题。
  • 程序复杂度高,需要进行线程调度和管理。
  • 线程之间的调试和排错较为困难。

适用场合:

  • CPU密集型任务,例如图像处理、数值计算等。
  • 数据共享和协同工作的场合,例如多人协作的软件项目。
  • 需要高效利用CPU资源的程序。
  1. 多进程的优缺点和适用场合

优点:

  • 进程之间互不干扰,不存在线程之间的数据竞争和死锁问题。
  • 程序结构清晰,容易调试和排错。
  • 进程之间可以使用IPC进行通信。

缺点:

  • 进程间通信的开销比线程间通信要大,效率较低。
  • 进程创建和销毁的开销大,执行效率较低。

适用场合:

  • I/O密集型任务,例如网络通信、文件读写等。
  • 需要独立运行的任务,例如系统服务、后台进程等。
  • 需要在不同的机器上进行分布式处理的任务。

二分查找法检索元素速度比顺序法快还是慢

用二分查找法检索元素的速度通常比顺序查找法快,但这并不是绝对的,也要具体问题具体分析。

在一个已排序的数组中,如果需要查找一个元素,使用二分查找法的时间复杂度是O(log n),而使用顺序查找法的时间复杂度是O(n)。当n较大时,二分查找法的效率明显高于顺序查找法。

但是,如果数组本身是无序的,需要先进行排序操作,排序的时间复杂度是O(nlog n),此时二分查找法的总时间复杂度为O(nlog n),而顺序查找法的总时间复杂度也为O(nlog n),因此它们的效率相当。

此外,在某些特定场景下,顺序查找法可能比二分查找法更加高效。例如在数据集很小的情况下,二分查找法的优势可能并不明显;又如,数据的分布不均匀,最小值和最大值相差较小,也可能会导致二分查找法的效率降低。

五种linux版本

  1. Ubuntu:由 Canonical 公司开发,基于 Debian 操作系统,是最受欢迎的 Linux 发行版之一。它拥有大量的软件包和广泛的社区支持,适合用于桌面和服务器应用。
  2. CentOS:由社区维护,基于 Red Hat Enterprise Linux(RHEL),也是一种流行的服务器操作系统,提供长期支持和安全更新,广泛应用于企业服务器和云环境。
  3. Debian:Debian 是一个社区开发的操作系统,拥有大量的软件包和强大的软件管理工具。它稳定、可靠,并提供了很好的软件包更新机制。
  4. Fedora:由 Red Hat 公司赞助,是一种流行的桌面和服务器操作系统,它拥有最新的软件包和技术,并且经常更新。
  5. Arch Linux:是一个自由、轻量级和灵活的 Linux 发行版,具有简单的设计和文档化的文件结构,适合高级用户和程序员使用。它提供了最新的软件包和自定义配置选项,但需要用户有一定的 Linux 经验。

js写一个字符串大小写转换的功能

function toggleCase(str) {
  let result = "";
  for (let i = 0; i < str.length; i++) {
    let char = str.charAt(i);
    if (char === char.toUpperCase()) {
      result += char.toLowerCase();
    } else {
      result += char.toUpperCase();
    }
  }
  return result;
}

js写一个函数,输入某人生日之后,可以计算出今年还有多少天过生日

function daysUntilBirthday(birthday) {
  const today = new Date();
  const thisYear = today.getFullYear();
  const nextBirthday = new Date(birthday);
  nextBirthday.setFullYear(thisYear);
  if (nextBirthday < today) {
    nextBirthday.setFullYear(thisYear + 1);
  }
  const oneDay = 24 * 60 * 60 * 1000;
  return Math.round((nextBirthday - today) / oneDay);
}

js写一个函数,给定s1,s2,s3,三个字符串,验证s3是否由s1和s2交错组成,这个当时没实现,太麻烦了

该函数接受三个字符串参数 s1、s2 和 s3,判断 s3 是否由 s1 和 s2 交错组成。首先判断 s3 的长度是否等于 s1 和 s2 长度之和,如果不等于,则直接返回 false。然后使用动态规划的思想递归判断 s1 和 s2 的字符是否能够交错组成 s3 的前 k 个字符,其中 i、j 和 k 分别表示 s1、s2 和 s3 中当前判断的字符下标。使用 memo 对已经判断过的结果进行缓存,避免重复计算。最终返回递归的结果。

function isInterleave(s1, s2, s3) {
  if (s1.length + s2.length !== s3.length) {
    return false;
  }
  const memo = {};
  function helper(i, j, k) {
    if (i === s1.length && j === s2.length && k === s3.length) {
      return true;
    }
    if (k === s3.length) {
      return false;
    }
    const key = `${i}-${j}-${k}`;
    if (memo.hasOwnProperty(key)) {
      return memo[key];
    }
    let result = false;
    if (i < s1.length && s1[i] === s3[k]) {
      result = result || helper(i + 1, j, k + 1);
    }
    if (j < s2.length && s2[j] === s3[k]) {
      result = result || helper(i, j + 1, k + 1);
    }
    memo[key] = result;
    return result;
  }
  return helper(0, 0, 0);
}

IM有消息已读标志,请简述一下一个消息到界面展示已读,数据在客户端和网络上的流转过程

  1. 客户端发送消息:当客户端发送一条消息时,消息会首先被发送到服务器端,服务器将消息存储到数据库中,并向接收方推送消息。
  2. 客户端接收消息:接收方的客户端会接收到推送过来的消息,并在本地缓存中保存这条消息的相关信息,包括发送者、消息内容、时间戳等。
  3. 客户端读取消息:接收方在查看消息时,会向服务器发送一个已读回执,告知服务器这条消息已经被阅读了。
  4. 服务器更新消息状态:当服务器接收到已读回执后,会将这条消息的状态更新为已读,并将状态信息存储到数据库中。
  5. 客户端获取消息状态:当接收方再次打开聊天界面时,客户端会从服务器端获取消息的状态信息,并根据状态信息更新本地消息的状态,例如将消息标记为已读。
  6. 客户端展示已读状态:当消息被标记为已读后,客户端会根据已读状态来展示消息。例如,可以在消息列表中将已读的消息显示为灰色,或者在消息内容中显示已读标志。

两套后端服务程序A和B在线上长期运行,A和B之间通过TCP协议进行数据传输,A和B之前的数据传输是否可能丢失,并举例说明原因

A和B之间通过TCP协议进行数据传输时,一般情况下不会丢失数据。TCP协议是一种面向连接的可靠传输协议,它提供了数据完整性检查、数据重传、拥塞控制等机制,保证了数据的可靠传输。

然而,在极端情况下,TCP协议也可能会出现数据丢失的情况,例如:

  1. 网络故障:当网络出现故障时,可能会导致数据包无法正常传输,从而导致数据丢失。
  2. TCP连接超时:当TCP连接长时间没有活动时,可能会被网络设备或操作系统中的TCP协议栈关闭,从而导致数据丢失。
  3. 突发网络拥塞:当网络突然出现大量数据传输时,可能会导致网络拥塞,从而导致部分数据包丢失。

请用二进制计算解释为啥0.1+0.2不等于0.3

在 JavaScript 中,使用 IEEE 754 标准对浮点数进行表示,使用的是 64 位双精度浮点数,也就是说一个浮点数占用 8 个字节。

0.1 在二进制中无法精确表示,会变成一个无限循环小数,因此,当对这两个数进行加法计算时,会产生一个舍入误差,结果并不等于 0.3。

请论述含参数的宏和函数的优缺点和应用场合

  • 宏是在预处理阶段处理的。
  • 宏展开时直接替换代码文本,不需要函数调用的额外开销。
  • 宏支持参数化,可以根据不同的参数展开不同的代码文本。
  • 宏没有返回值,可以实现一些不需要返回值的简单代码块。

函数:

  • 函数是在运行时调用的。
  • 函数调用有一定的开销,包括参数传递、栈操作等。
  • 函数可以接受参数并返回值。
  • 函数的执行过程可以通过调用栈进行追踪和调试。

宏和函数的应用场合有所不同:

宏:

  • 宏适合处理一些简单的代码块,例如常量定义、条件编译等。
  • 宏可以根据不同的参数生成不同的代码,适合用于一些代码生成场景,例如字符串拼接、动态代码生成等。

函数:

  • 函数适合处理一些复杂的逻辑,例如数学计算、字符串处理等。
  • 函数可以封装一些通用的操作,例如数据结构的操作、IO操作等。
  • 函数可以进行参数传递和返回值,方便进行复杂的数据处理和逻辑控制。

对称加密和非加密的区别

对称加密是指使用相同的密钥来加密和解密数据的方式。

非对称加密是指使用一对密钥,分别为公钥和私钥,在加密和解密时使用不同的密钥。

A函数和B函数都实现相同的功能,那么如何检测两个函数的执行效率

function A() {
  // A 函数实现代码
}

function B() {
  // B 函数实现代码
}

console.time('A函数执行时间');
A();
console.timeEnd('A函数执行时间');

console.time('B函数执行时间');
B();
console.timeEnd('B函数执行时间');

数组中有一个数字出现次数超过数组长度的一半,请找出这个数字,你可以假设数组是非空的,并切给定的数组总是存在多数元素,有一个限制,数组长度大于等于1小于等于50000

可以使用摩尔投票法来解决这个问题,该算法的基本思想是:

如果一个数字出现的次数超过了数组长度的一半,那么在遍历这个数组的时候,出现次数最多的数字一定是这个数字。

我们遍历数组,使用计数器来记录多数元素的出现次数,如果当前元素等于多数元素,则计数器加1,否则减1。如果计数器为0,则将当前元素设为多数元素。遍历完数组后,多数元素即为所求。

function majorityElement(nums) {
  let count = 0; // 计数器
  let majority = null; // 多数元素

  // 摩尔投票法
  for (let i = 0; i < nums.length; i++) {
    // 如果计数器为0,则将当前元素设为多数元素
    if (count === 0) {
      majority = nums[i];
    }

    // 如果当前元素等于多数元素,则计数器加1,否则减1
    if (nums[i] === majority) {
      count++;
    } else {
      count--;
    }
  }

  // 遍历完数组后,多数元素即为所求
  return majority;
}

给定一个非负整数num,反复将各个位上的数字相加,直到结果为一位数

我们先判断num是否大于等于10,如果大于等于10,则执行循环。在循环中,我们用一个内层循环来取出num的每一位数字,并进行累加。当内层循环结束后,将累加和赋值给num。这样反复执行,直到num小于10,即可得到最终结果。

function addDigits(num) {
  while (num >= 10) {
    let sum = 0;
    while (num > 0) {
      sum += num % 10; // 取出个位数并累加
      num = Math.floor(num / 10); // 去掉个位数
    }
    num = sum; // 将累加和赋值给num
  }
  return num;
}

现在有一个div的类为cont,下面有两个子元素div,一个类名为sidebar,一个类名为content,其中content下面有两个子元素div,一个类名为header,一个类名为body,其中header下面有三个div,类名分别为avatar,name,quit,现在要求sidebar固定款段200px,content宽度自适应,header固定高度60px

我们使用了flex布局来实现两个子元素的水平排列。sidebar元素的宽度设置为200px,content元素的宽度设置为自适应。header元素的高度设置为60px,内部的三个div元素使用display:inline-block来实现水平排列。其中avatar和quit元素的宽度和高度设置为60px,用来模拟实际内容。在name元素中使用了calc()函数来计算宽度,使其剩余的空间正好够容纳后面的两个元素。

.cont {
  display: flex;
}

.sidebar {
  width: 200px;
}

.content {
  flex: 1;
}

.header {
  height: 60px;
}

.header > div {
  display: inline-block;
}

.header .avatar {
  width: 60px;
  height: 60px;
  background-color: #ccc;
}

.header .name {
  width: calc(100% - 180px);
  margin-left: 10px;
  font-size: 18px;
  line-height: 60px;
}

.header .quit {
  width: 60px;
  height: 60px;
  background-color: #ccc;
}

页面中有两个节点A和B请写代码分析两个节点的关系

const nodeA = document.querySelector('#nodeA');
const nodeB = document.querySelector('#nodeB');

if (nodeA.contains(nodeB)) {
  console.log('nodeB is a descendant of nodeA');
} else if (nodeB.contains(nodeA)) {
  console.log('nodeA is a descendant of nodeB');
} else if (nodeA.parentNode === nodeB.parentNode) {
  console.log('nodeA and nodeB are siblings');
} else {
  console.log('nodeA and nodeB have no relationship');
}

请用js实现,把下面标签中字符串为div的文字加粗<div id='content'>outherdivss<div>3adiv4d</div>d</div>

const divs = document.getElementsByTagName('div');
for (let i = 0; i < divs.length; i++) {
  if (divs[i].textContent === 'div') {
    divs[i].style.fontWeight = 'bold';
  }
}

使用promise实现一个image同步加载的函数,onload的情况下执行resolve,onerror的时候执行reject,输出错误

function loadImage(src) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => {
      resolve(img);
    };
    img.onerror = (error) => {
      reject(error);
    };
    img.src = src;
  });
}

使用proxy,reflect劫持target使proxy.getDate()和new Date().getDate()返回一致结果

const handler = {
  construct(target, args) {
    return new Proxy(new target(...args), {
      get(target, prop) {
        if (prop === 'getDate') {
          return target.getDate.bind(target);
        } else {
          return Reflect.get(target, prop);
        }
      }
    });
  }
};

const ProxyDate = new Proxy(Date, handler);

const proxy = new ProxyDate();

console.log(proxy.getDate()); // 输出当前日期的天数
console.log(new Date().getDate()); // 输出当前日期的天数

堆栈指针存储在ESP(Extended Stack Pointer)寄存器中。

DNS的默认缓存时间是根据DNS服务器的配置而定,可以是几分钟到几天不等。

dns的作用是域名和ip的相互映射 dns协议运行在UDP上 DNS的协议端口号为53

  • RESTful接口中,HTTP的method用来描述要对资源操作的方式
  • HTTP状态码304是缓存相关的状态码,表示客户端缓存资源仍然有效,无需重新传输资源
  • HTTP协议是无状态协议,即每个请求都是独立的,服务器不会保留任何客户端的信息,需要客户端再次发送所有信息

解决元素重叠问题

clear属性可以清除元素浮动,让后面的元素不再和前面的浮动元素重叠。
.outer {
  clear: both;
}
给.outer添加position:relative属性,可以使其成为相对定位元素,从而改变其在文档流中的位置,
同时,z-index属性可以控制元素的层叠顺序,使元素出现在浮动元素之上。
.outer {
  position: relative;
  z-index: 1;
}
可以为其添加上内边距,避免与浮动元素重叠。代码如下:
.outer {
  padding-top: 200px;
}

全部评论
为什么顺序查找无序数组的时间复杂度是nlogn?从头查到尾也只需要n啊,为什么要先排序呢
点赞
送花
回复
分享
发布于 2023-02-26 00:08 广东
多线程和多进程这个感觉考的蛮多啊
点赞
送花
回复
分享
发布于 2023-03-28 16:54 上海
网易互娱
校招火热招聘中
官网直投

相关推荐

点赞 评论 收藏
转发
4 18 评论
分享
牛客网
牛客企业服务