给定一个只包含0、1、2三类数字的数组,最多遍历两次完成排序,示例如下。
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
function sort(arr) {
let begin = 0, end = arr.length - 1, cur = 0;
while (0 === arr[begin]) {
begin++;
}
while (2 === arr[end]) {
end--;
}
cur = begin;
while (cur <= end) {
if (0 === arr[cur]) {
swap(arr, cur, begin);
begin++;
} else if (2 === arr[cur]) {
swap(arr, cur, end);
// 如果交换完成之后为 0,则需要继续进行交换
if (0 === arr[cur]) {
swap(arr, cur, begin);
begin++;
}
end--;
while (2 === arr[end]) {
end--;
}
}
cur++;
}
}
function swap(arr, i, j) {
let tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
const tmp = [0, 2, 1, 1, 2, 0, 2, 0, 1, 2, 2, 2, 1, 2];
sort(tmp);
双指针问题
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。
请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
典型的排序加双指针问题。题目的例子已经暗示需要为原数组按照内部数组的第一位大小进行排序。
排序之后,设置两个指针,分别指向第一个,和第二个。
然后进行遍历,在遍历时,会出现下面的情况:
arr[i]
和arr[j]
区间重合,那么需要将arr[i]
和arr[j]
进行合并。
- 选取
arr[i][0]
和arr[j][1]
,组合成结果数组[arr[i][0], arr[j][1]]
- 替换掉
arr
数组中i
的位置数据- 并将
arr
数组中j
所在位置的数据删除i
和j
保持不变arr[i]
和arr[j]
区间不重合
- 说明该位置的数据无法合并,则将
i
和j
向后挪动一位直到
j
到达arr
最右侧。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
提示:
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
function merge(intervals) {
let arr = intervals.sort((a, b) => { return a[0] < b[0] });
let i = 0, j = 1;
while (i < j && j < arr.length) {
const tmpI = arr[i];
const tmpJ = arr[j];
if (tmpI[1] >= tmpJ[0]) {
const tmp = [Math.min(tmpI[0], tmpJ[0]), Math.max(tmpI[1], tmpJ[1])];
arr[i] = tmp;
arr = [...arr.slice(0, j), ...arr.slice(j + 1)];
} else {
i++;
j++;
}
}
return arr;
}
console.log(merge([[1, 4], [4, 5]]));
防抖
节流
节流是一种函数执行频率的技术。它的原理是,当时间被频繁触发时,函数会按照一定的事件间隔执行了,而不是每次触发事件都执行。换句话说,在一个时间段内,只会执行一次事件处理函数。
场景
Promise
的 then
方法会在当前任务技术后立即执行,而 setTimeout
会在下一个红人执行中执行。跨域是什么?
Cross-Origin Resouce,CORS
)是指浏览器中,一个域下的文档或者脚本请求另一个域下的资源时,浏览器处于安全考虑对其进行的行为。这种是为了防止恶意网站通过脚本读取另一个网站的数据,保护用户的隐私和安全。导致跨域的情况
解决跨域的方法
服务器设置响应头 Access-Control-Allow-Origin
,允许特定的域访问资源
// Nodejs + Express
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');// 允许所有域
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
next();
})
JSONP(JSON with Padding)
通过动态创建 <script>
标签来绕过跨域,适用于只支持 GET
请求的场景
<script>
function handleResponse(data) {
console.log(data);
}
</script>
<script src="http://api.example.com/data?callback=handleResponse"></script>
代理服务器
websocket
websocket 协议本身不受同源策略的,可以实现跨域通信。
const socket = new WebSocket('ws://api.example.com');
socket.onmessage = function(event) {
console.log(event.data);
};
项目打包方面:
压缩 Javascript
和 CSS
文件
使用工具如 UglifyJs、Terser、CSSNano 等压缩代码,减少文件大小。
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin(), new OptimizeCSSAssetsPlugin()],
},
};
压缩项目本地图片
代码分割和按需加载
利用 webpack 尽量减少 CSS 和 Javascript 文件的数量,尽量合并
利用 preload
和 prefetch
来优化性能,减少交互延迟
什么是 preload,什么是 prefetch
何时使用 preload,何时使用 prefetch
preload 是告诉浏览器预先请求当前页面需要的资源(关键的脚本,字体,主要图片等)。
prefetch 应用场景稍微又有不同 —— 用户将来可能跳转到其他页面需要使用到的资源。如果 A 页面发起一个B页面的 prefetch 请求,这个资源获取过程和导航请求可能是同步进行的,而如果我们用 preload 的话,页面 A 离开时它就会立即停止。
项目运行期间
利用浏览器DevTools中对React项目进行内存泄露排查
Performance Monitor定性
Performance Monitor 能够在较小的性能代价下展示出网站应用的若干个影响性能和体验的关键参数随着时间变化(用户操作)的趋势,其关键指标如下所示
CPU usage 网页使用的CPU百分比。默认显示。
JS heap size JavaScript程序在页面上使用的内存量。默认显示。
DOM Nodes 浏览器中DOM节点的数量(跨选项卡)。默认显示。
JS event listeners 浏览器中JavaScript事件的数量(跨选项卡)。
Documents 浏览器中文档对象的数量(跨选项卡)。
Document Frames 浏览器中文档框架的数量(跨选项卡)。
Layouts / sec 浏览器引擎每秒构建页面布局的次数。
Style recalcs / sec 浏览器引擎每秒计算页面CSS样式的次数。
针对内存泄漏问题,可以重点关注 JavaScript 堆大小和 DOM 节点数的变化趋势,并根据以下原则对内存泄漏进行初步的定性判断:
javscript
堆大小只增不减,而 DOM 节点数量趋于平稳,则可以定性只在 Javascript
上下文中出现了内容泄漏当二者的变化趋势满足同比同频,基本可以确定是对DOM元素的引用没有清理导致的内存泄漏问题。
Detached Elements定位(现在已经在 Memory 功能中)
Detached Elements 的功能很明确,即找到所有灭有挂在在 DOM 树上,同时还没有被浏览器引擎垃圾回收的DOM元素。因为浏览器的垃圾回收本身就是周期性的行为。所以在进行问题排查前,必须手动触发一次垃圾回收行为,保证剩下的就是要排查分析的目标元素。
DOM 对象是占用内存最高的一类对象之一,因此如果在应用程序中频繁地创建和销毁DOM对象,就容易导致内存泄漏。游离的DOM引用指的是已经不在文档中的DOM节点的引用,但是这些引用仍然被保存在 JS 变量、数组和对象中,因此这些DOM节点无法被垃圾回收器回收,从而导致内存泄漏。
Memory 分析
Memory 能够简历当前应用的 JS 堆快照,用于进一步分析页面的 JS 对象以及相互之间的引用关系。在已经定位了泄漏源的基础上,可以借助该工具查明目标 DOM 被什么 JS 对象支持有了引用导致无法被垃圾回收。
LocalStorage 或 SessionStorage
使用 Web 存储机制可以在不同标签页之间共享数据。一个标签页可以将数据存储在LocalStorage或SessionStorage中,其他标签可以监听存储事件来获取更新数据。
案例
// 在一个标签页中写入数据到 LocalStorage 或 SessionStorage
localStorage.setItem("sharedData", "Hello from Tab 1");
// 在其他标签页中监听存储事件,并获取更新的数据
window.addEventListener("storage", function(event) {
if (event.key === "sharedData") {
const newData = event.newValue;
console.log('Received updated data: ', newData);
}
})
// 另一个标签中更新数据
localStorage.setItem("sharedData", "Hello from Tab2");
在这个例子中,首先在一个标签页中通过localStorage.setItem()
或 sessionStorage.setItem()
方法将数据写入到LocalStorage
或 SessionStorage
中。然后,在其他标签页中通过监听 storage 事件来捕获存储事件,并判断事件的 key 是否为我们共享的数据 sharedData,如果是,则获取更新的数据 newValue 并进行处理。
接下来,在另一个标签页中通过 localStorage.setItem()
或 sessionStorage.setItem()
方法更新数据。
Broadcast Channel API
Broadcast Channel API 允许不同标签页之间通过共享的通道进行消息广播和接受。一个标签页可以通过通道发送消息,其他订阅了相同通道的标签页可以接受这些消息。
案例
// 创建一个广播通道
const channel = new BroadCastChannel('myChannel');
// 发送消息
channel.postMessage('Hello from Tab 1');
// 接受消息的标签页中
const channel = new BroadCastChannel('MyChannel');
channel.onmessage = function(event) {
const message = event.data;
console.log('Received message', message);
}
首先在发送消息的标签页中创建一个Broadcast Channel,
并指定一个唯一的通道名称(这里使用 ‘myChannel’)。通过 channel.postMessage()
方法发送消息到该通道。
在接收消息的标签页中,同样创建一个具有相同通道名称的 Broadcast Channel。
然后,通过为 channel.onmessage 赋值一个函数,来监听消息事件。当接收到消息时,事件对象 event 中的 data 属性将包含发送的消息内容,我们可以在监听函数中获取并处理该消息。
SharedWorker
SharedWorkder 是一种在多个标签页之间共享的后台线程。标签页可以通过 SharedWorker 进行通信,发送消息和接收消息。这种方式需要使用 Javascript 的 Worker API。
案例
// 创建一个 SharedWorker
const worker = new SharedWorker('worker.js');
// 发送消息
worker.port.postMessage('Hello from Tab 1');
// worker.js
// 监听连接事件
self.onconnect = function(event) {
const port = event.ports[0];
// 监听消息事件
port.onmessage = function(event) {
const message = event.data;
console.log('Received message:', message);
};
// 发送消息
port.postMessage('Hello from Worker');
};
Window.postMessage
Window.postMessage() 方法允许在不同的窗口或标签页之间安全地传递消息。通过调用 postMessage() 方法并指定目标窗口的 origin,可以将消息发送到其他标签页,并通过监听 message 事件来接收消息。
案例
// 发送消息的标签页中
// 监听消息事件
window.addEventListener('message', function(event) {
// 确保消息来自预期的源
if (event.origin !== 'http://example.com') {
return;
}
const message = event.data;
console.log('Received message: ', message);
})
// 发送到其他标签页中
const targetWindow = window.open('http://example.com/otherpage', '_blank');
targetWindow.postMessage('Hello from Tab 1', 'http://example.com');
// 在接受信息的标签页中
window.addEventListener('message', function(event) {
// 确保消息来自预期的源
if (event.origin !== 'http://example.com') {
return;
}
const message = event.data;
console.log('Received message: ', message);
// 回复消息
event.source.postMessage('Hello from Other Tab', event.origin);
})
在发送消息的标签页中通过使用 window.addEventListener('message', ...)
监听消息事件。在事件处理函数中,可以用 event.origin
来验证消息的来源是否符合预期。然后,可以用 event.data
获取到发送的消息内容,并进行相应的操作。
在发送消息的标签页中,用 window.open()
打开了一个新的标签页(http://example.com/otherpage
),然后通用 targetWindow.postMessage()
向该标签页发送消息。在这里,我们指定了消息的目标窗口和预期的来源(即目标标签页的 URL)。
在接收消息的标签页中,同样通过 window.addEventListener('message', ...)
监听消息事件,并在事件处理函数中进行相应的操作。
父元素设置 CSS 属性
.parent {
display: flex;
justify-content: center;
align-items: center;
}
父元素设置CSS属性 position: relative
,子元素设置CSS属性
.child {
position: absolute;
left: 50%;
right: 50%;
transform: translate(-50%, -50%);
}
父元素以及子元素的宽高确定的情况下,父元素设置CSS
.parent {
margin: auto;
}
华为 OD 技术一面,主要还是侧重于基础。
对付一面,刷 leetcode 和背八股文就行了。
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “()[]{}”
输出:true
示例 3:
输入:s = “(]”
输出:false
示例 4:
输入:s = “([])”
输出:true
leetcode 里面的简单题,主要考验的是对栈的理解。栈 - 先进后出
以下是解法的关键点:
首先判断字符串为空,或者字符串长度为奇数
为空,或者长度为奇数,括号不能配对,不合法。
不为空且长度为偶数
创建字典,以
右括号
为键,左括号
为值。创建一个栈,用以存放左括号。
遍历字符串
如果字符串为空,则遍历完成。
如果字符串不为空:
如果是
右括号
,则弹出左括号
若弹出的左括号不和右括号对应,则字符串无效
对应,则继续下一个遍历
如果弹出的是
左括号
- 将该括号压入栈
判断栈的长度,如果长度为0,则合法,不为0,左括号多余,则字符串不合法。
function isVaild(s) {
if (s.length % 2 === 1) {
return false;
}
const dic = new Map();
dic.set(")", "(");
dic.set("]", "[");
dic.set("}", "{");
let stk = [];
for (let i = 0; i < s.length; i++) {
let char = s[i];
if (dic.has(char)) {
if (stk.length > 0 && stk[stk.length - 1] !== dic.get(char)) {
return false;
}
stk.pop();
} else {
stk.push(char);
}
}
return !stk.length;
}
面试题是从你以前经历过的项目,按照你简历上所写,来考的,你的简历越丰富,越难。
我反正是被问懵了,这里就举一个例子吧。
我的简历上写了,在搭建公司的基础平台的时候,状态管理库使用的是 jotai
。
那么,面试官的问题就是
从以上来看,技术二面会对你的项目技术选型提出疑问,首先是基础方面的内容,然后是使用的技术栈的基础知识的考查,再是你对该技术选型的考究,以及对比,综合考察你的项目经验和技术栈理解的深度。
所以在面技术二面的准备的时候,需要根据自己的项目经验,老老实实地去排查里面可能出现的知识点,否则可能就像我一样,一脸懵逼的开始,一脸懵逼的结束。
最后,祝正在寻求机会的各位,一帆风顺;正在岗位上发光发热的各位,稳步高升。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务