字节-本地生活-校招 · 第 1 轮 · 二面

← 第 1 轮 · 返回本次面经 · 已是最后一轮 → 本轮要点: 本轮面试重点考察候选人的项目经验、技术深度和工程化思维。主要包括: 算法编程能力:通过合并有序数组等题目考察基本编程功底 项目经验:深入考察实习期间的技术选型、架构设计等关键决策 微前端技术:重点关注跨应用通信、样式隔离等核心问题 AI 应用实践:考察将新技术应用到工程实践中的能力 跨窗口通信:考察复杂场景下的技术方案设计能力 本轮共 15 道题。答案默认折叠,便于先自行作答。 1. 将两个升序数组合并为一个升序数组 题目要点 数组操作能力 双指针算法应用 原地修改的实现 边界条件处理 参考答案 这道题可以使用双指针从后向前遍历的方法,将较大的数放入nums1的末尾。代码实现如下: function merge(nums1, m, nums2, n) { let p1 = m - 1; // nums1的指针 let p2 = n - 1; // nums2的指针 let p = m + n - 1; // 合并后数组的指针 while (p2 >= 0) { // 只要nums2还有元素需要合并 if (p1 >= 0 && nums1[p1] > nums2[p2]) { nums1[p] = nums1[p1]; p1--; } else { nums1[p] = nums2[p2]; p2--; } p--; } return nums1; } 算法分析: 时间复杂度:O(m+n),只需遍历一次 空间复杂度:O(1),原地修改不需要额外空间 关键点:从后向前遍历避免覆盖nums1中的有效元素 2. 上一段实习在联想主要做的是什么产品?采用了什么技术架构? 题目要点 这是一道主观性问题,面试官主要考察: 项目描述能力:是否能清晰表达项目背景、目标和价值 技术选型能力:是否有合理的技术决策思路 架构设计能力:是否理解系统设计的关键点 业务理解能力:是否深入理解业务需求和痛点 答题建议:建议从项目背景、技术架构、实现亮点和项目成果四个方面展开。重点说明项目的业务价值和技术特点,展现自己在项目中的贡献和成长。 参考答案 “在联想实习期间,我主要负责一个企业级IT资产管理平台的开发工作。这是一个服务于公司内部的中后台系统,日均活跃用户约2000人,管理着超过10万台设备资产。这个平台的核心价值在于提升IT资产管理效率,降低运维成本。 ...

June 24, 2025

JSX

时下虽然接入 JSX 语法的框架越来越多,但与之缘分最深的毫无疑问仍然是 React。 2013 年,当 React 带着 JSX 横空出世时,社区曾对 JSX 有过不少的争议,但如今,越来越多的人面对 JSX 都要说上一句“真香”!我们就来一起认识下这个“真香”的 JSX,聊一聊“JSX 代码是如何‘摇身一变’成为 DOM 的”。 开始之前请先思考几个问题: JSX 的本质是什么,它和 JS 之间到底是什么关系? 为什么要用 JSX?不用会有什么后果? JSX 背后的功能模块是什么,这个功能模块都做了哪些事情? 面对以上问题,如果无法形成清晰且系统的思路,那么很可能是把 JSX 想得过于简单了。大多数人只是简单地把它理解为模板语法的一种,但事实上,JSX 作为 React 框架的一大特色,它与 React 本身的运作机制之间存在着千丝万缕的联系,上述 3 个问题的答案,就恰恰隐藏在这层“联系”中。 JSX 的本质:JavaScript 的语法扩展 JSX 到底是什么?看看 React 官网给出的一段定义: JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力。 “语法扩展”这一点在理解上几乎不会产生歧义,不过“它充分具备 JavaScript 的能力”这句,却总让人摸不着头脑,JSX 和 JS 怎么看也不像是“一路人”啊?这就引出了“JSX 语法是如何在 JavaScript 中生效的”这个问题。 JSX 语法是如何在 JavaScript 中生效的:认识 Babel Facebook 公司给 JSX 的定位是 JavaScript 的“扩展”,而非 JavaScript 的“某个版本”,这就直接决定了浏览器并不会像天然支持 JavaScript 一样地支持 JSX。那么,JSX 的语法是如何在 JavaScript 中生效的呢?React 官网其实早已给过我们线索: ...

December 17, 2024

浏览器渲染过程

渲染流程 首先要了解的概念: 渲染引擎:它是浏览器最核心的部分是 “Rendering Engine”,不过我们一般习惯将之称为 “浏览器内核” 渲染引擎主要包括的线程: 各个线程主要职责: GUI渲染线程:GUI 渲染线程负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。 JavaScript引擎线程: JavaScript 引擎线程主要负责解析 JavaScript 脚本并运行相关代码。 JavaScript 引擎在一个Tab页(Renderer 进程)中无论什么时候都只有一个 JavaScript 线程在运行 JavaScript 程序。需要提起一点就是,GUI线程与JavaScript引擎线程是互斥的,这也是就是为什么JavaScript操作时间过长,会造成页面渲染不连贯,导致页面出现阻塞的原理。 事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。 通常JavaScript引擎是单线程的,所以这些事件都会排队等待JS执行。 定时器触发器: 我们日常使用的setInterval 和 setTimeout 就在该线程中,原因可能就是:由于JS引擎是单线程的,如果处于阻塞线程状态就会影响记时的准确,所以需要通过单独的线程来记时并触发响应的事件这样子更为合理。 Http请求线程: 在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求,这个线程就Http请求线程,它 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理。 有了上述的概念,对接下我们讲渲染流水线会有所帮助 简略版的渲染机制 很久之前就把浏览器工作原理读完了,看了很多博客,文章,当时简简单单的梳理一些内容,如下👇 简略版渲染机制一般分为以下几个步骤 处理 HTML 并构建 DOM 树。 处理 CSS 构建 CSSOM 树。 将 DOM 与 CSSOM 合并成一个渲染树。 根据渲染树来布局,计算每个节点的位置。 调用 GPU 绘制,合成图层,显示在屏幕上。 接下来大概就是这么说: 在构建 CSSOM 树时,会阻塞渲染,直至 CSSOM 树构建完成。并且构建 CSSOM 树是一个十分消耗性能的过程,所以应该尽量保证层级扁平,减少过度层叠,越是具体的 CSS 选择器,执行速度越慢。 ...

December 17, 2024

前端模块化

一、模块化的理解 1.什么是模块? 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起 块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信 2.模块化的进化过程 全局function模式 : 将不同的功能封装成不同的全局函数 编码: 将不同的功能封装成不同的全局函数 问题: 污染全局命名空间, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系 function m1(){ //... } function m2(){ //... } namespace模式 : 简单对象封装 作用: 减少了全局变量,解决命名冲突 问题: 数据不安全(外部可以直接修改模块内部的数据) let myModule = { data: 'www.baidu.com', foo() { console.log(`foo() ${this.data}`) }, bar() { console.log(`bar() ${this.data}`) } } myModule.data = 'other data' //能直接修改模块内部的数据 myModule.foo() // foo() other data 这样的写法会暴露所有模块成员,内部状态可以被外部改写。 IIFE模式:匿名函数自调用(闭包) 作用: 数据是私有的, 外部只能通过暴露的方法操作 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口 问题: 如果当前这个模块依赖另一个模块怎么办? // index.html文件 <script type="text/javascript" src="module.js"></script> <script type="text/javascript"> myModule.foo() myModule.bar() console.log(myModule.data) //undefined 不能访问模块内部数据 myModule.data = 'xxxx' //不是修改的模块内部的data myModule.foo() //没有改变 </script> // module.js文件 (function(window) { let data = 'www.baidu.com' //操作数据的函数 function foo() { //用于暴露有函数 console.log(`foo() ${data}`) } function bar() { //用于暴露有函数 console.log(`bar() ${data}`) otherFun() //内部调用 } function otherFun() { //内部私有的函数 console.log('otherFun()') } //暴露行为 window.myModule = { foo, bar } //ES6写法 })(window) 最后得到的结果: ...

October 5, 2024

Vue实例挂载

一、思考 我们都听过知其然知其所以然这句话 那么不知道大家是否思考过new Vue()这个过程中究竟做了些什么? 过程中是如何完成数据的绑定,又是如何将数据渲染到视图的等等 一、分析 首先找到vue的构造函数 源码位置:src\core\instance\index.js function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } options是用户传递过来的配置项,如data、methods等常用的方法 vue构建函数调用_init方法,但我们发现本文件中并没有此方法,但仔细可以看到文件下方定定义了很多初始化方法 initMixin(Vue); // 定义 _init stateMixin(Vue); // 定义 $set $get $delete $watch 等 eventsMixin(Vue); // 定义事件 $on $once $off $emit lifecycleMixin(Vue);// 定义 _update $forceUpdate $destroy renderMixin(Vue); // 定义 _render 返回虚拟dom 首先可以看initMixin方法,发现该方法在Vue原型上定义了_init方法 源码位置:src\core\instance\init.js Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge options // 合并属性,判断初始化的是否是组件,这里合并主要是 mixins 或 extends 的方法 if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { // 合并vue属性 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { // 初始化proxy拦截器 initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm // 初始化组件生命周期标志位 initLifecycle(vm) // 初始化组件事件侦听 initEvents(vm) // 初始化渲染方法 initRender(vm) callHook(vm, 'beforeCreate') // 初始化依赖注入内容,在初始化data、props之前 initInjections(vm) // resolve injections before data/props // 初始化props/data/method/watch/methods initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } // 挂载元素 if (vm.$options.el) { vm.$mount(vm.$options.el) } } 仔细阅读上面的代码,我们得到以下结论: ...

October 4, 2024

Let/Const

Var 在 ES6 出现之前,必须使用 var 声明。但是,前端开发出现的一些问题与使用 var 声明的变量有关。这就是为什么必须要有新的方法来声明变量。首先,让我们在讨论这些问题之前更多地了解 var。 var 的作用域 作用域本质上是指变量/函数可供访问的范围。var可以在全局范围声明或函数/局部范围内声明。 当在最外层函数的外部声明var变量时,作用域是全局的。这意味着在最外层函数的外部用var声明的任何变量都可以在windows中使用。 当在函数中声明var时,作用域是局部的。这意味着它只能在函数内访问。 要进一步了解,请查看下面的示例。 var greeter = 'hey hi'; function newFunction() { var hello = 'hello'; } 这里,greeter是全局范围的,因为它存在于函数外部,而hello是函数范围的。因此,我们无法在函数外部访问变量hello。因此,如果我们这样做: var tester = 'hey hi'; function newFunction() { var hello = 'hello'; } console.log(hello); // error: hello is not defined 我们会收到错误消息,这是由于函数外部没有hello导致的。 var 变量可以重新声明和修改 这意味着我们可以在相同的作用域内执行下面的操作,并且不会出错 var greeter = 'hey hi'; var greeter = 'say Hello instead'; 又比如 var greeter = 'hey hi'; greeter = 'say Hello instead'; var 的变量提升 变量提升是 JavaScript 的一种机制:在执行代码之前,变量和函数声明会移至其作用域的顶部。这意味着如果我们这样做: console.log(greeter); var greeter = 'say hello'; 生面的代码会被解释为: var greeter; console.log(greeter); // greeter is undefined greeter = 'say hello'; 因此,将var声明的变量会被提升到其作用域的顶部,并使用 undefined 值对其进行初始化. ...

October 2, 2024

XSS防御

一、简述 跨站脚本(Cross-site scripting,简称为:CSS, 但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,跨站脚本攻击缩写为XSS)是一种网站应用程序的安全漏洞攻击。 XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、 LiveScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。 二、XSS类型 最常见的几种分类:反射型(非持久型)XSS、存储型(持久型)XSS、DOM型XSS、通用型XSS、突变型XSS。 反射型XSS 反射型XSS只是简单的把用户输入的数据从服务器反射给用户浏览器,要利用这个漏洞,攻击者必须以某种方式诱导用户访问一个精心设计的URL(恶意链接),才能实施攻击。 举例来说,当一个网站的代码中包含类似下面的语句: <?php echo "<p>hello,$_GET['user']</p>"; ?> 如果未做防范XSS,用户名设为<script>alert("Tz")</script>,则会执行预设好的JavaScript代码。 漏洞成因 当用户的输入或者一些用户可控参数未经处理地输出到页面上,就容易产生XSS漏洞。主要场景有以下几种: 将不可信数据插入到HTML标签之间时;// 例如div, p, td; 将不可信数据插入到HTML属性里时;// 例如:<div width=$INPUT></div> 将不可信数据插入到SCRIPT里时;// 例如:<script>var message = ” $INPUT “;</script> 还有插入到Style属性里的情况,同样具有一定的危害性;// 例如<span style=” property : $INPUT ”></span> 将不可信数据插入到HTML URL里时,// 例如:<a href=”[http://www.abcd.com?param=](http://www.ccc.com/?param=) $INPUT ”></a> 使用富文本时,没有使用XSS规则引擎进行编码过滤。 对于以上的几个场景,若服务端或者前端没有做好防范措施,就会出现漏洞隐患。 攻击流程 反射型XSS通常出现在搜索等功能中,需要被攻击者点击对应的链接才能触发,且受到XSS Auditor(chrome内置的XSS保护)、NoScript等防御手段的影响较大,所以它的危害性较存储型要小。 存储型XSS ​ 存储型(或 HTML 注入型/持久型)XSS 攻击最常发生在由社区内容驱动的网站或 Web 邮件网站,不需要特制的链接来执行。黑客仅仅需要提交 XSS 漏洞利用代码(反射型XSS通常只在url中)到一个网站上其他用户可能访问的地方。这些地区可能是博客评论,用户评论,留言板,聊天室,HTML 电子邮件,wikis,和其他的许多地方。一旦用户访问受感染的页,执行是自动的。 漏洞成因 ​ 存储型XSS漏洞的成因与反射型的根源类似,不同的是恶意代码会被保存在服务器中,导致其它用户(前端)和管理员(前后端)在访问资源时执行了恶意代码,用户访问服务器-跨站链接-返回跨站代码。 攻击流程 DOM型XSS 通过修改页面的DOM节点形成的XSS,称之为DOM Based XSS。 ...

October 2, 2024

数据类型

前言 请讲下 JavaScript 中的数据类型? 前端面试中,估计大家都被这么问过。 答:Javascript 中的数据类型包括原始类型和引用类型。其中原始类型包括 Null、Undefined、Boolean、String、Symbol、BigInt。引用类型指的是 Object。 没错,我也是这么回答的,只是这通常是第一个问题,由这个问题可以引出很多很多的问题,比如 Null 和 Undefined 有什么区别?前端的判空有哪些需要注意的? typeof null 为什么是 object? 为什么 ES6 要提出 Symbol? BigInt 解决了什么问题? 为什么 0.1 + 0.2 !== 0.3? 你如何解决这个问题? 如何判断一个值是数组? … 弱类型语言 因为 JavaScript 是弱类型语言或者说是动态语言。这意味着你不需要提前声明变量的类型,在程序运行的过程中,类型会被自动确定,也就是说你可以使用同一个变量保存不同类型的值 var foo = 42; // foo is a Number now foo = "bar"; // foo is a String now foo = true; // foo is a Boolean now 这一特性给我们带来便利的同时,也给我们带来了很多的类型错误。试想一下,假如 JS 说是强类型语言,那么各个类型之间没法转换,也就有了一层隔阂或者说一层保护,会不会更加好维护呢?——这或许就是 TypeScript 诞生的原因。 对 JavaScript 的数据类型掌握,是一个前端最基本的知识点 null 还是 undefined 定义 undefined 表示未定义的变量。null 值表示一个空对象指针。 ...

October 1, 2024

盒子模型

前端盒子模型是网页布局的基础概念,对于前端开发者来说,理解盒子模型是至关重要的。 1. 盒子模型的组成部分 内容(Content): 盒子中实际显示的文本或图像部分。内容区的大小由 width 和 height 属性确定。 内边距(Padding): 内容与边框之间的空间。它用于增加内容与边框之间的距离,可以通过 padding、padding-top、padding-right、padding-bottom 和 padding-left 属性进行设置。 边框(Border): 环绕在内边距和内容周围的边界。可以设置边框的宽度、样式和颜色。相关属性包括 border、border-width、border-style 和 border-color。 外边距(Margin): 盒子外部的空间。用于控制元素与元素之间的间距。可以通过 margin、margin-top、margin-right、margin-bottom 和 margin-left 属性进行设置。 2. 标准盒子模型与替代盒子模型 标准盒子模型(content-box): 默认的盒子模型。width 和 height 只包含内容区,不包括内边距和边框。因此,元素的实际占用空间是 width + padding + border。 替代盒子模型(border-box): 通过设置 box-sizing: border-box,使得 width 和 height 包括内边距和边框,这样内容区的大小会自动调整以适应总高度和宽度。这样做通常可以避免许多布局问题。 示例 .box { box-sizing: content-box; /* 默认值 */ width: 200px; padding: 20px; border: 5px solid black; } 在标准模型下: 总宽度 = 200px + 20px (左内边距) + 20px (右内边距) + 5px (左边框) + 5px (右边框) = 250px .box { box-sizing: border-box; /* 替代模型 */ width: 200px; padding: 20px; border: 5px solid black; } 在替代模型下: ...

September 30, 2024

HTML 5 新特性

HTML5 新特性 语义标签 增强型表单 视频和音频 Canvas绘图 SVG绘图 地理定位 拖拽API WebWorker WebStorage WebSocket 语义标签 语义化标签使得页面的内容结构化,可以使开发者更方便清晰地构建页面的布局 标签 描述 <header> 定义了文档的头部区域 <footer> 定义了文档的尾部区域 <nav> 定义文档的导航 <section> 定义文档中的书 <acticle> 定义页面独立的内容区域 <aside> 定义页面侧边栏内容 <details> 定义用户可以看到或隐藏的额外细节 <summary> 标签包含details元素的标题 <dialog> 定义对话框 <figure> 定义自包含内容,如图表 <main> 定义文档主内容 增强型表单 HTML5新增input输入特性,改善更好的输入控制和验证 输入类型 描述 color 主要用于选取颜色 date 选取日期 datetime 选取日期(UTC时间) datetime-local 选取日期(无时区) month 选择一个月份 week 选择周和年 time 选择一个时间 email 包含email地址的输入域 number 数值的输入域 url url地址的输入域 tel 定义输入电话号码的字段 search 用于搜索域 range 一个范围内数字值的输入域 HTML5也新增了五个表单元素 ...

September 24, 2024