字节 飞书前端 一面 凉经
因为有个低代码平台的项目经验,一上来疯狂问项目
然后问了几个vue相关的,感觉就像是随便问的。
最后写了一道简单的算法,
二叉树最近公共父节点
找到二叉树中给定两个节点的最近公共祖先是一种常见的算法问题。下面是一个简单的 JavaScript 实现:
class TreeNode {
constructor(val) {
this.val = val;
this.left = null;
this.right = null;
}
}
function lowestCommonAncestor(root, p, q) {
if (root === null || root === p || root === q) {
return root;
}
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
if (left === null) {
return right;
} else if (right === null) {
return left;
} else {
return root;
}
}
// 示例用法
const root = new TreeNode(3);
root.left = new TreeNode(5);
root.right = new TreeNode(1);
root.left.left = new TreeNode(6);
root.left.right = new TreeNode(2);
root.right.left = new TreeNode(0);
root.right.right = new TreeNode(8);
root.left.right.left = new TreeNode(7);
root.left.right.right = new TreeNode(4);
const p = root.left;
const q = root.right;
const result = lowestCommonAncestor(root, p, q);
console.log(result.val); // 应该输出3,即最近公共祖先节点的值
这段代码首先定义了一个 TreeNode 类来表示树节点,然后实现了 lowestCommonAncestor 函数来找到给定两个节点的最近公共祖先。找到二叉树中给定两个节点的最近公共祖先是一种常见的算法问题。下面是一个简单的 JavaScript 实现:
class TreeNode {
constructor(val) {
this.val = val;
this.left = null;
this.right = null;
}
}
function lowestCommonAncestor(root, p, q) {
if (root === null || root === p || root === q) {
return root;
}
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
if (left === null) {
return right;
} else if (right === null) {
return left;
} else {
return root;
}
}
// 示例用法
const root = new TreeNode(3);
root.left = new TreeNode(5);
root.right = new TreeNode(1);
root.left.left = new TreeNode(6);
root.left.right = new TreeNode(2);
root.right.left = new TreeNode(0);
root.right.right = new TreeNode(8);
root.left.right.left = new TreeNode(7);
root.left.right.right = new TreeNode(4);
const p = root.left;
const q = root.right;
const result = lowestCommonAncestor(root, p, q);
console.log(result.val); // 应该输出3,即最近公共祖先节点的值
这段代码首先定义了一个 TreeNode 类来表示树节点,然后实现了 lowestCommonAncestor 函数来找到给定两个节点的最近公共祖先。
基本上全答出来了
然后面试完大概5min,显示流程终止,感觉明显就是不想要人,建议投飞书的避避坑
作者:马屁山山主
链接:www.nowcoder.com/feed/main/d…
来源:牛客网
字节前端一二三面面经
一面
说一下你理解的div
说一下flex
说一下权重,看代码(父子和子的权重问题)
在前端开发中,"权重"通常指的是 CSS 样式表中选择器的优先级。CSS 选择器的优先级决定了哪些样式将被应用到元素上。
通常,选择器的优先级由以下几个因素决定,按重要性递减排列:
- 内联样式:在 HTML 元素中直接使用
style属性设置的样式,其优先级最高,权重为 1000。 - ID 选择器:通过 ID 选择器(如
#element-id)设置的样式具有较高的优先级,权重为 100。 - 类选择器、属性选择器和伪类选择器:类选择器(如
.class-name)、属性选择器(如[attribute])和伪类选择器(如:hover)具有中等优先级,权重为 10。 - 元素选择器和伪元素选择器:元素选择器(如
div)和伪元素选择器(如::before)具有最低的优先级,权重为 1。
如果多个样式规则应用于同一个元素,浏览器将根据选择器的权重确定最终应用的样式。具体来说,权重越高的样式将覆盖权重较低的样式。例如,ID 选择器的样式将覆盖类选择器的样式,类选择器的样式将覆盖元素选择器的样式,依此类推。
在某些情况下,可能会遇到父子元素之间的样式冲突。这时,可以通过以下方式解决:
- 使用更具体的选择器:如果父子元素之间存在样式冲突,可以尝试使用更具体的选择器来限制样式的范围,以确保样式应用于正确的元素。
- 使用
!important关键字:可以在样式规则中使用!important关键字来提高样式的优先级,使其覆盖其他具有相同或更低权重的样式。但应慎重使用,因为它会增加样式维护的复杂性。
如果你有具体的代码示例或问题,我可以帮助你更详细地解决问题。
说一下promise,手写并发控制
Promise 是 JavaScript 中用于处理异步操作的对象,它代表了一个异步操作的最终完成或失败,并且可以获取其结果。
Promise 的特点包括:
- 状态:Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。状态一旦改变,就不会再变。
- 异步操作:Promise 可以处理异步操作,通过 then() 方法注册回调函数,在异步操作完成后执行。
- 错误处理:可以通过 catch() 方法捕获异步操作中的错误。
- 链式调用:可以通过 then() 方法链式调用多个异步操作。
下面是一个手写并发控制的示例:
// 创建一个包装异步任务的 Promise 函数
function asyncTask(taskName) {
return new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
console.log(`${taskName} 完成`);
resolve();
}, Math.random() * 1000); // 使用随机时间模拟不同的异步任务
});
}
// 并发控制函数,接受任务数组和并发数量作为参数
function concurrentControl(tasks, concurrency) {
let index = 0; // 记录当前执行的任务索引
const results = []; // 保存任务执行结果的数组
// 递归执行任务函数
function executeTask() {
// 如果所有任务都已执行完毕,则返回结果
if (index >= tasks.length) {
return Promise.resolve(results);
}
// 截取当前并发数量的任务进行执行
const currentTasks = tasks.slice(index, index + concurrency);
// 更新索引,准备下一次执行
index += concurrency;
// 使用 Promise.all 执行并发任务
return Promise.all(
currentTasks.map(task => {
return task()
.then(result => {
results.push(result); // 保存任务执行结果
});
})
).then(() => executeTask()); // 递归执行下一批任务
}
// 返回执行任务的 Promise
return executeTask();
}
// 定义多个异步任务
const tasks = [
() => asyncTask('任务1'),
() => asyncTask('任务2'),
() => asyncTask('任务3'),
() => asyncTask('任务4'),
() => asyncTask('任务5')
];
// 控制并发数量为2
concurrentControl(tasks, 2)
.then(results => {
console.log('所有任务执行完毕');
console.log('任务执行结果:', results);
})
.catch(error => {
console.error('任务执行出错:', error);
});
在上面的示例中,我们定义了一个并发控制函数 concurrentControl(),它接受任务数组和并发数量作为参数,然后递归执行任务并控制并发数量。
怎么判断数组
要判断一个变量是否是数组,可以使用 JavaScript 中的 Array.isArray() 方法。这个方法接受一个参数,并返回一个布尔值,指示该参数是否为数组。
示例代码如下:
const arr = [1, 2, 3];
const obj = { key: 'value' };
console.log(Array.isArray(arr)); // true,arr 是数组
console.log(Array.isArray(obj)); // false,obj 不是数组
在上面的示例中,Array.isArray(arr) 返回 true,因为 arr 是一个数组;而 Array.isArray(obj) 返回 false,因为 obj 不是一个数组。
typeof和instanceof的区别
写代码,分割url
typeof 和 instanceof 是 JavaScript 中用于类型检测的两种不同方法,它们之间有以下区别:
-
typeof是一个操作符,返回一个表示未经计算的操作数的类型的字符串。它通常用于确定变量的类型,可以用于检测 JavaScript 的基本数据类型(如字符串、数字、布尔值等),以及函数和未定义的值。但typeof对于数组和对象返回的都是 "object"。 -
instanceof是一个运算符,用于检测一个对象是否是某个构造函数的实例。它检测的是原型链,如果对象的原型链中包含指定构造函数的原型,则返回 true,否则返回 false。
示例代码如下:
// 使用 typeof 检测变量类型
console.log(typeof "Hello"); // 输出 "string"
console.log(typeof 42); // 输出 "number"
console.log(typeof true); // 输出 "boolean"
console.log(typeof undefined); // 输出 "undefined"
console.log(typeof null); // 输出 "object"
console.log(typeof []); // 输出 "object",但不是数组类型
console.log(typeof {}); // 输出 "object"
// 使用 instanceof 检测对象是否为某个构造函数的实例
console.log([] instanceof Array); // 输出 true,[] 是 Array 的实例
console.log({} instanceof Object); // 输出 true,{} 是 Object 的实例
console.log("Hello" instanceof String); // 输出 false,"Hello" 不是 String 的实例,因为它是一个原始值而不是对象
关于分割 URL 的代码,你想要分割 URL 的哪些部分呢?常见的包括协议、主机、路径、查询参数等。
要分割 URL,可以使用 JavaScript 中的 URL 对象或者正则表达式来实现。以下是两种方法的示例:
- 使用
URL对象:
const url = new URL("https://www.example.com/path/to/resource?query=value");
console.log("Protocol:", url.protocol); // 输出 "https:"
console.log("Host:", url.host); // 输出 "www.example.com"
console.log("Path:", url.pathname); // 输出 "/path/to/resource"
console.log("Query:", url.search); // 输出 "?query=value"
- 使用正则表达式:
const url = "https://www.example.com/path/to/resource?query=value";
// 正则表达式匹配
const matches = url.match(/^(https?:\/\/[^\/]+)(\/[^?]*)(\?.*)?$/);
if (matches) {
console.log("Protocol:", matches[1]); // 输出 "https:"
console.log("Host:", matches[2]); // 输出 "/path/to/resource"
console.log("Query:", matches[3]); // 输出 "?query=value"
} else {
console.log("URL 格式不正确");
}
这两种方法都可以用于分割 URL,根据实际情况选择适合的方法。
二面
场景题react
手写自定义hook,实现登陆拦截
自定义 Hook 是一个函数,名称以 “use” 开头,它可以让你使用 React 的一些特性。要实现登陆拦截的自定义 Hook,你可以按照以下步骤操作:
- 创建一个名为
useAuth的自定义 Hook。 - 在该 Hook 中,使用 React 的
useState来存储用户的登陆状态。 - 实现一个登陆函数,用于设置用户的登陆状态。
- 实现一个登出函数,用于清除用户的登陆状态。
- 在自定义 Hook 中,编写逻辑以检查用户是否已经登陆,如果未登录则跳转到登陆页面。
- 在组件中使用
useAuthHook,以实现登陆拦截功能。
下面是一个简单的示例代码:
import { useState } from 'react';
// 自定义 Hook
const useAuth = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// 登陆函数
const login = () => {
setIsLoggedIn(true);
};
// 登出函数
const logout = () => {
setIsLoggedIn(false);
};
// 检查用户是否已经登陆
const checkAuth = () => {
if (!isLoggedIn) {
// 未登录则跳转到登陆页面
window.location.href = '/login'; // 这里假设登陆页面的路径是 '/login'
}
};
return { isLoggedIn, login, logout, checkAuth };
};
export default useAuth;
然后,在你的组件中使用 useAuth Hook,调用 checkAuth 函数即可实现登陆拦截:
import React, { useEffect } from 'react';
import useAuth from './useAuth'; // 引入自定义 Hook
const ProtectedComponent = () => {
const { isLoggedIn, checkAuth } = useAuth(); // 使用自定义 Hook
// 在组件加载时检查用户登陆状态
useEffect(() => {
checkAuth();
}, []); // 空数组表示仅在组件加载时执行一次
return (
<div>
{isLoggedIn ? (
<p>Welcome, user!</p>
) : (
<p>Please log in to view this content.</p>
)}
</div>
);
};
export default ProtectedComponent;
以上代码是一个简单的登陆拦截功能的实现,。 你可以根据自己的需求进行扩展和修改
手写千分位格式化
下面是一个 JavaScript 函数,可以实现对数字的千分位格式化:
function formatNumber(number) {
// 先将数字转换为字符串
let str = number.toString();
// 正则表达式匹配数字的整数部分和小数部分
let parts = str.split('.');
let integerPart = parts[0];
let decimalPart = parts.length > 1 ? '.' + parts[1] : '';
// 对整数部分添加千分位分隔符
let integerWithCommas = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// 将整数部分和小数部分拼接起来
return integerWithCommas + decimalPart;
}
// 示例用法
console.log(formatNumber(1234567.89)); // 输出 "1,234,567.89"
console.log(formatNumber(9876543210)); // 输出 "9,876,543,210"
该函数接受一个数字作为参数,并返回一个格式化后的字符串,其中整数部分有千分位分隔符。
三面
扫一扫登陆流程
扫码登录流程通常涉及以下步骤:
-
生成登录二维码: 服务器端生成一个唯一的登录标识,并生成对应的登录二维码,通常包含了登录标识和一些其他信息。
-
展示二维码: 前端页面展示生成的登录二维码供用户扫描。
-
用户扫描二维码: 用户使用移动设备上的扫码工具扫描二维码。
-
确认登录: 扫描成功后,用户需要确认是否进行登录操作。
-
向服务器发送请求: 扫描后确认登录,移动设备会向服务器发送一个包含了登录标识的请求。
-
服务器验证登录标识: 服务器接收到请求后,验证登录标识的有效性。
-
返回登录结果: 如果登录标识有效,服务器返回登录成功的信息,否则返回登录失败的信息。
-
前端处理结果: 前端页面根据服务器返回的登录结果,展示登录成功或者失败的提示。
-
登录成功后续操作: 如果登录成功,前端页面可能会跳转到登录成功后的页面或者进行其他相关操作。
整个流程中,前后端需要进行有效的通信和数据验证,以确保登录操作的安全性和可靠性。
react通信,单项数据流
React 中的单向数据流是指数据在应用中的传递方向是单向的,通常从父组件流向子组件。这种单向数据流有助于代码的可维护性和可预测性。
在 React 中,父组件可以通过 props 将数据传递给子组件,子组件则无法直接修改父组件传递过来的 props。如果子组件需要修改数据,通常会通过触发事件或调用父组件传递过来的回调函数来实现。
单向数据流的优点包括:
- 可维护性: 数据流的方向清晰明确,易于理解和维护。
- 可预测性: 数据的流向是可预测的,减少了程序的复杂度和出错的可能性。
- 组件解耦: 父组件和子组件之间的数据传递通过 props 进行,降低了组件之间的耦合度。
- 数据单一来源: 数据的修改只能通过特定的途径进行,避免了数据的混乱和不一致性。
在实际开发中,单向数据流是 React 组件之间通信的主要方式,有助于构建可维护和可扩展的应用程序。
看代码,变量问题,pomise问题
写代码,数组里的0移到最后
大部分都忘记了。。。 以下是将数组中的所有 0 移到数组末尾的 JavaScript 代码:
function moveZerosToEnd(arr) {
let count = 0; // 记录非零元素的个数
// 遍历数组,将非零元素依次向前移动
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== 0) {
arr[count++] = arr[i];
}
}
// 将剩余位置填充为 0
while (count < arr.length) {
arr[count++] = 0;
}
return arr;
}
// 示例用法
const array = [0, 1, 0, 3, 12];
console.log(moveZerosToEnd(array)); // 输出:[1, 3, 12, 0, 0]
这段代码首先遍历数组,将非零元素依次向前移动,然后将剩余位置填充为 0,从而实现了将数组中的所有 0 移到数组末尾的功能。
作者:夏目又三
链接:www.nowcoder.com/feed/main/d…
来源:牛客网