1. CSS 中两个 .class1 .class2
从哪个开始解析
在 CSS 中,选择器 .class1 .class2
表示所有 class 为 class1
的元素中的 class 为 class2
的子元素。浏览器解析这个选择器时,从右向左解析。也就是说,浏览器首先找到所有 class 为 class2
的元素,然后检查这些元素的父级元素是否有 class1
,从而应用样式。
.class1 .class2 {
color: red;
}
2. Flex 布局相关的问题
Flexbox 是一种一维布局模型,可以在一个方向上高效地分配空间并对齐内容。以下是一些常见的 Flex 布局属性:
display: flex
: 将容器变为 flex 容器。flex-direction
: 定义主轴方向。取值有row
(默认)、row-reverse
、column
、column-reverse
。justify-content
: 定义主轴上的对齐方式。取值有flex-start
、flex-end
、center
、space-between
、space-around
、space-evenly
。align-items
: 定义交叉轴上的对齐方式。取值有stretch
(默认)、flex-start
、flex-end
、center
、baseline
。flex-wrap
: 定义是否换行。取值有nowrap
(默认)、wrap
、wrap-reverse
。
3. JS 的事件循环
JavaScript 是单线程的,使用事件循环来处理异步操作。事件循环的基本步骤:
- 执行栈:同步代码逐行执行,函数调用形成执行栈。
- 任务队列:异步操作(如
setTimeout
、Promise
)完成后,将回调函数放入任务队列。 - 事件循环:不断检查执行栈是否为空,如果为空则从任务队列中取出第一个任务并执行。
console.log('start');
setTimeout(() => {
console.log('timeout');
}, 0);
console.log('end');
// 输出顺序: start -> end -> timeout
4. 写题:解析 URL,history 模式和 hash 模式的混在一起
function parseURL(url) {
const parser = document.createElement('a');
parser.href = url;
return {
protocol: parser.protocol,
hostname: parser.hostname,
port: parser.port,
pathname: parser.pathname,
search: parser.search,
hash: parser.hash,
host: parser.host
};
}
const url = 'https://www.example.com:8080/pathname/?search=test#hash';
console.log(parseURL(url));
5. 写题:解析时间
function formatDate(date) {
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
const hh = String(date.getHours()).padStart(2, '0');
const min = String(date.getMinutes()).padStart(2, '0');
const ss = String(date.getSeconds()).padStart(2, '0');
return `${yyyy}-${mm}-${dd} ${hh}:${min}:${ss}`;
}
const date = new Date();
console.log(formatDate(date));
6. 缓存相关问题
强缓存和协商缓存是 HTTP 缓存机制中的两种缓存策略:
- 强缓存:不会向服务器发送请求,直接从缓存中读取资源。常见的头部字段有
Expires
和Cache-Control
。 - 协商缓存:会向服务器发送请求,服务器根据资源的状态决定是否使用缓存。常见的头部字段有
Last-Modified
和ETag
。
一般存储较静态且不频繁更新的数据,如静态资源(CSS、JS、图片等)。
7. async
和 defer
的区别
async
:异步加载脚本,脚本下载完成后立即执行,不保证顺序。defer
:异步加载脚本,脚本下载完成后按顺序执行,保证在文档解析完成后执行。
<script src="script1.js" async></script>
<script src="script2.js" async></script>
<script src="script1.js" defer></script>
<script src="script2.js" defer></script>
8. Vue 的渲染机制
Vue 的渲染机制分为以下几个步骤:
- 初始化:解析模板,生成渲染函数。
- 响应式数据:数据变更时触发视图更新。
- 虚拟 DOM:每次渲染都会生成虚拟 DOM。
- diff 算法:比较新旧虚拟 DOM,找出变化。
- 更新 DOM:应用 diff 结果,更新真实 DOM。
HTML 的 doctype
有什么用
<!DOCTYPE html>
声明位于 HTML 文档的开头,用于告诉浏览器当前文档使用的是 HTML5 标准。它的主要作用是让浏览器以标准模式(standard mode)来解析和渲染网页,避免进入怪异模式(quirks mode),确保网页在不同浏览器中有一致的表现。
script
和 link
标签的加载顺序
-
<script>
标签:默认情况下,脚本会按顺序同步加载和执行。如果在<head>
中包含<script>
标签,页面会在脚本执行完成后才继续加载。可以通过async
和defer
属性来改变默认行为:async
:脚本异步加载,加载完成后立即执行,不保证顺序。defer
:脚本异步加载,加载完成后按顺序执行,保证在文档解析完成后执行。
-
<link>
标签:用于加载外部资源(如 CSS)。默认情况下,CSS 是同步加载和解析的,会阻塞渲染。
iframe
有了解过吗
iframe
元素用于在当前网页中嵌入另一个 HTML 文档。常见用途包括嵌入第三方内容(如广告、视频)、隔离 CSS 和 JavaScript 的作用域。使用 iframe
时需要注意安全问题,如跨站脚本(XSS)攻击,可以通过设置 sandbox
属性来限制 iframe
的行为。
行内元素水平垂直居中
对于一个行内元素或行内块元素,可以使用以下方法来实现水平和垂直居中:
- 水平居中:使用
text-align: center
; - 垂直居中:如果父元素是一个 flex 容器,可以使用
align-items: center
和justify-content: center
。
例如:
<div style="display: flex; justify-content: center; align-items: center; height: 200px;">
<span>Centered</span>
</div>
innerHTML
和 innerText
区别
innerHTML
:返回或设置元素的 HTML 内容,包括 HTML 标签。innerText
:返回或设置元素的文本内容,不包含 HTML 标签,受 CSS 样式影响(如display: none
的元素不会包含在内)。
修改 DOM 耗时?为什么
修改 DOM 可能是耗时的,因为每次对 DOM 的修改都会触发浏览器的重绘(repaint)或回流(reflow),尤其是回流会涉及布局计算、重新渲染等一系列操作。频繁操作 DOM 会导致性能下降,建议使用批量操作或虚拟 DOM 来优化性能。
重绘(Repaint)和回流(Reflow)
- 重绘:元素的外观发生改变(如颜色、背景)但没有影响布局时,浏览器会重新绘制元素。
- 回流:元素的布局或几何属性发生改变(如大小、位置),会触发浏览器重新计算布局和渲染树,代价比重绘高。
Promise
的意义(回调地狱)
Promise
用于处理异步操作,以更直观和可读的方式管理异步任务,避免回调地狱。回调地狱是指回调函数嵌套过深,代码难以维护和调试。Promise
通过链式调用 .then()
方法解决这一问题:
fetchData()
.then(response => process(response))
.then(result => display(result))
.catch(error => handleError(error));
回调和闭包
- 回调:将一个函数作为参数传递给另一个函数,并在特定事件发生时调用该函数。常用于处理异步操作。
- 闭包:函数和其外部环境的引用组合形成闭包。闭包允许函数访问外部函数作用域中的变量,即使在外部函数执行完毕之后。闭包常用于创建私有变量和函数。
function createCounter() {
let count = 0;
return function() {
return ++count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
这个示例中,返回的匿名函数形成闭包,持有对 count
变量的引用,因此可以在每次调用时增加 count
的值。
6. Webpack为什么打包、进行压缩之后,性能就会提高呢
Webpack 打包和压缩提高性能的原因
-
文件合并:
- 减少 HTTP 请求:Webpack 将多个模块和依赖合并成一个或多个文件,减少了浏览器发起的 HTTP 请求次数。现代浏览器对并行请求的数量有限制,减少请求数可以降低延迟和开销。
-
代码压缩:
- 文件大小减少:Webpack 使用工具(如 UglifyJS、Terser)压缩代码,去掉空格、注释和冗余代码,极大地减少了文件大小。文件越小,传输时间越短。
-
代码分割:
- 按需加载:Webpack 可以根据应用的需要进行代码分割(Code Splitting),只加载当前页面所需的代码。这样可以避免一次性加载所有代码,提升页面初始加载性能。
-
树摇(Tree Shaking):
- 去除无用代码:Webpack 通过静态分析模块的依赖关系,去除没有实际使用的代码(Dead Code),从而减少打包后的文件体积。
-
模块热替换(HMR):
- 开发效率:Webpack 提供模块热替换功能,在开发过程中只更新变化的部分,而不是整个页面,提升开发效率和体验。
示例配置:
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: 'production',
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
splitChunks: {
chunks: 'all',
},
},
};
7. 为什么使用 Webpack
使用 Webpack 的理由
-
模块化支持:
- 多种模块格式:支持 ES6 模块、CommonJS、AMD 等模块格式,使开发者可以使用最新的 JavaScript 语法和标准。
-
强大的插件系统:
- 功能扩展:Webpack 提供丰富的插件(Plugins),如压缩、分包、代码分割等,方便扩展打包功能。
-
代码分割和懒加载:
- 优化性能:通过代码分割(Code Splitting)和懒加载(Lazy Loading),提升应用性能,减少初始加载时间。
-
开发工具集成:
- 热模块替换(HMR):实时更新代码,提高开发效率和体验。
- 开发服务器:内置开发服务器(Webpack Dev Server),支持快速开发和调试。
-
配置灵活:
- 高可定制性:可以根据项目需求灵活配置,适应各种复杂场景。
-
生态系统:
- 社区和插件:强大的社区支持和丰富的插件库,方便解决各种问题。
8. 手写:数组实现 reduce
、map
手写 reduce
实现
reduce
方法接收一个回调函数和一个可选的初始值,通过回调函数累计处理数组的每个元素,最终返回累计结果。
Array.prototype.myReduce = function(callback, initialValue) {
let accumulator = initialValue === undefined ? this[0] : initialValue;
let startIndex = initialValue === undefined ? 1 : 0;
for (let i = startIndex; i < this.length; i++) {
accumulator = callback(accumulator, this[i], i, this);
}
return accumulator;
};
// 使用示例
const arr = [1, 2, 3, 4];
const sum = arr.myReduce((acc, curr) => acc + curr, 0);
console.log(sum); // 10
手写 map
实现
map
方法接收一个回调函数,返回一个新数组,每个元素为回调函数处理后的结果。
Array.prototype.myMap = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this));
}
return result;
};
// 使用示例
const arr = [1, 2, 3, 4];
const doubled = arr.myMap(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
这些实现展示了如何用原生 JavaScript 来实现常用的数组方法,了解其内部工作原理。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 前端面试题汇总2
发表评论 取消回复