163期
1. 怎么预防用户快速连续点击,造成数据多次提交?
2. 说说你对渐进式框架的理解?
3. Electron 有哪些特点和优势?
上面问题的答案会在第二天的公众号(程序员每日三问)推文中公布
也可以小程序刷题,已收录500+面试题及答案
162期问题及答案
1. 能否简单实现一个commonJs的导出函数和require函数?
在 Node.js 中,CommonJS 是模块化的标准,用于在服务器端编写模块代码。module.exports
用于导出模块,而 require
函数用于引入模块。
下面是一个简化版的 CommonJS 导出函数 exports
和 require
函数的实现:
const fs = require('fs');
const vm = require('vm');
// 模拟缓存
const moduleCache = {};
function requireModule(filePath) {
// 使用完整路径以避免模块重复加载
const absolutePath = require.resolve(filePath);
// 如果模块已经被加载过了,直接返回缓存中的导出对象
if (moduleCache[absolutePath]) {
return moduleCache[absolutePath].exports;
}
// 模拟module对象和exports对象
const module = { exports: {} };
// 缓存模块
moduleCache[absolutePath] = module;
// 读取模块内容
const moduleCode = fs.readFileSync(absolutePath, 'utf8');
// 包装模块代码
const wrapper = `(function(module, exports, require) {
${moduleCode}
})`;
// 执行包装后的代码
const script = new vm.Script(wrapper, { filename: absolutePath });
const moduleFunction = script.runInThisContext();
moduleFunction.call(module.exports, module, module.exports, requireModule);
// 返回exports对象
return module.exports;
}
// 覆写原有的require以解析文件完整路径
requireModule.resolve = function(filePath) {
// 这里简化处理,只处理.js后缀,实际Node.js的路径解析远比这复杂
// 在这里并没有考虑各种边缘情况,例如文件/目录存在性检查、node_modules递归查找等
return `${filePath}.js`;
};
// 测试模块
// foo.js:
// module.exports = {
// message: 'Hello CommonJS',
// sayMessage: function() {
// console.log(this.message);
// }
// };
// main.js:
// const foo = requireModule('./foo');
// foo.sayMessage();
// 假设我们有上述的 foo 模块,你可以通过 requireModule('./foo') 来加载模块
需要注意的是,这个实现是一个非常简化的 CommonJS 模块系统示例。它仅用于概念演示,Node.js 中的模块系统比这个实现复杂得多,考虑到许多其他的细节和边缘情况,例如文件递归解析、处理文件夹和package.json
中的main
字段、错误处理等等。
在真实的 Node.js 环境中,你不需要自己实现这些功能,因为 Node.js 提供了这些功能的内建实现,你只需要使用 require
关键字和 module.exports
来进行模块的导入和导出即可。
2. 请实现一个简单的轮播图组件?
实现一个简单的轮播图组件,我们可以使用 HTML、CSS 和 JavaScript。以下是一个基本实现的样例:
HTML (index.html):
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>简单轮播图组件</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="carousel-container">
<div class="carousel-slide">
<img src="image1.jpg" alt="Image 1">
<img src="image2.jpg" alt="Image 2">
<img src="image3.jpg" alt="Image 3">
</div>
<a class="prev" onclick="moveSlide(-1)">❮</a>
<a class="next" onclick="moveSlide(1)">❯</a>
</div>
<script src="script.js"></script>
</body>
</html>
CSS (styles.css):
.carousel-container {
position: relative;
max-width: 600px;
margin: auto;
overflow: hidden;
}
.carousel-slide {
display: flex;
}
.carousel-slide img {
max-width: 100%;
display: block;
}
.prev, .next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
padding: 16px;
margin-top: -22px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
}
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.8);
}
JavaScript (script.js):
let slideIndex = 0;
showSlides(slideIndex);
function moveSlide(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
function showSlides(n) {
let i;
let slides = document.getElementsByClassName("carousel-slide")[0].children;
if (n >= slides.length) {slideIndex = 0}
if (n < 0) {slideIndex = slides.length - 1}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
slides[slideIndex].style.display = "block";
}
// 可选:自动滚动
let autoSlideInterval = setInterval(function() {
moveSlide(1);
}, 3000); // 3000ms 内滚动一次
// 可选:停止自动滚动
function stopAutoSliding() {
clearInterval(autoSlideInterval);
}
// 可选:开始自动滚动
function startAutoSliding() {
autoSlideInterval = setInterval(function() {
moveSlide(1);
}, 3000);
}
图片 image1.jpg
、image2.jpg
和 image3.jpg
应替换为你自己的图片路径。
上述代码中:
HTML构建了轮播图基础结构,包含图片和控制按钮。
CSS设置了轮播图的样式,包含图片排列、滑动效果和控制按钮样式。
JavaScript负责轮播图的逻辑实现,提供了滑动功能和自动滚动机制。
要增强这个轮播图组件的功能,你可以加入指示器、响应式设计、触摸滑动支持、不同的动画效果等等。这只是一个简单的起点。
3. script标签放在header里和放在body底部里有什么区别?
<script>
标签放在 <head>
里面和放在 <body>
底部的主要区别在于页面加载时脚本的执行时机以及对页面渲染的影响。
放在
<head>
中:
当解析器遇到
<head>
中的<script>
标签时,它会立即停止解析 HTML,转而下载并执行 JavaScript 代码。这个过程会阻塞后续页面内容的加载和渲染,直到脚本执行完毕。如果脚本较大或者网络条件不佳,用户可能会看到一个空白页面,直到 JavaScript 加载并执行完毕,从而影响用户体验。
尽管将
<script>
放在<head>
中是合法的,但由于上述原因,一般不推荐这种方式,除非你确保了脚本非常小或者需要很早地执行(例如,现代库和框架可能会采用这种方式)。
放在 <body>
底部(即结束标签</body>
前):
当
<script>
放在页面底部时,HTML 解析器会先加载完所有的文档内容,然后才开始下载并执行 JavaScript 代码。这意味着用户可以更快地看到页面内容,而不必等待脚本加载完毕。这种方式减少了页面渲染的阻塞时间,因此用户感受到的加载时间通常会更短,提高了用户体验。
此外,这也意味着脚本在执行时,DOM 已经完全构建完毕,脚本中可以安全地访问页面上的所有元素。
随着现代化的网页开发和最佳实践的推广,许多网页倾向于使用异步 (async
) 或延迟 (defer
) 加载的脚本来进一步优化加载性能,这些属性可以放置在 <head>
中的 <script>
标签上:
async
:这个属性会并行下载脚本,但下载完成后会立即执行,并阻塞文档的解析。适合那些不依赖于其他脚本且其他脚本也不依赖于它的第三方脚本,如广告或计数器脚本。defer
:含有这个属性的脚本会并行下载,但延迟执行,直到文档解析完成后才执行。适合那些需要访问 DOM 但不需要在文档解析完成前立即执行的脚本。在现代浏览器中,defer
脚本会按照它们在文档中出现的顺序执行。
综上所述,在大多数情况下,建议将 <script>
标签放在 <body>
底部来改善页面加载,或者在 <head>
中使用 async
或 defer
属性来加载脚本。
因为微信公众号修改规则,如果不标星或点在看,你可能会收不到我公众号文章的推送,原创不易,请大家将本公众号星标,看完文章后记得点下赞或者在看,谢谢各位!
学习不打烊,充电加油只为遇到更好的自己,每天早上9点纯手工发布面试题,每天坚持花20分钟来学习与思考,在千变万化,类库层出不穷的今天,不要等到找工作时才狂刷题,提倡每日学习。