学员工作一年半,本科,计算机专业。当时毕业的时候错过了校招,在一个三流的小公司搬砖一年多。9月份找到我们,想实现大厂梦,准备了两个月,参加了百度的社招。技术面试已经通过,给到T4。前两面面试比较基础,注重coding能力,第三面让设计一个简单的购物车,更注重考察项目能力。下面将面经总结一下,供接下来找工作的小伙伴参考一下~
webpack 中 chunkHash 与 contentHash 区别:
chunkHash
是根据 chunk 的内容而生成的哈希值,当 chunk 的内容发生变化时,该哈希值也会改变。通常用于在文件名中添加哈希以实现浏览器缓存的更新。contentHash
是根据文件的内容而生成的哈希值,当文件的内容发生变化时,该哈希值也会改变。通常用于将哈希值作为静态资源的文件名,以保证只有内容发生变化时才会重新请求新的资源。写过 webpack 的 loader 和 plugin 么,讲一下:
Loader:Loader 是用于对特定类型的文件进行转换和处理的函数。它们作为模块的中间件,将源文件作为输入,并输出转换后的模块代码。Webpack 的核心原则是一切皆模块,因此 loader 主要用于处理非 JavaScript 文件(如 CSS、图像、字体等)。
Loader 可以在 module.rules
字段中配置,每个规则包含一个 test 属性来匹配需要转换的文件类型,以及一个 use 属性指定使用哪些 loader 进行转换。常见的 loader 有:
Plugin:Plugin 是用于扩展 Webpack 功能的插件,它可以在整个构建过程中执行自定义任务,包括优化资源、修改输出文件、注入环境变量等。与 loader 不同,plugin 在整个构建过程中通过钩子函数(hooks)与 Webpack 进行交互。
Plugin 可以通过实例化一个类来创建,并在 Webpack 配置中的 plugins
字段中进行配置。常见的 plugin 有:
Loader 和 Plugin 的区别:
webpack 处理 image 是用哪个 loader,限制成 image 大小的是哪个参数:
在 webpack 中,可以使用 file-loader
或者 url-loader
来处理图片。file-loader
将图片复制到输出目录,并返回图片的 URL;而 url-loader
可以根据指定的大小阈值将图片转换为 base64 编码或者继续使用 file-loader
进行处理。若要限制图片的大小,可以在 url-loader
的配置中使用 limit
参数。该参数指定一个阈值,当图片大小小于等于该阈值时,会将图片转换为 base64 编码,否则会使用 file-loader
进行处理。
webpack 如何将多个 css 合并成一个:
要将多个 CSS 文件合并成一个,可以使用 mini-css-extract-plugin
插件。该插件会将每个 CSS 文件转换为独立的文件,并通过 <link>
标签将它们插入 HTML 页面中。通过配置插件的 filename
选项,可以指定输出的合并后的 CSS 文件名。
webpack 的摇树对 CommonJS 和 ES6 Module 都生效么,原因是什么?:
简单实现一下「模版字符串」功能:
function templateString(strings, ...values) {
return strings.reduce((result, string, index) => {
const value = values[index] !== undefined ? values[index] : '';
return result + string + value;
}, '');
}
const name = 'John';
const age = 25;
const greeting = templateString`Hello, my name is ${name} and I'm ${age} years old.`;
console.log(greeting); // Output: Hello, my name is John and I'm 25 years old.
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let resolvedCount = 0;
if (promises.length === 0) {
resolve(results);
}
for (let i = 0; i < promises.length; i++) {
promises[i]
.then((value) => {
results[i] = value;
resolvedCount++;
if (resolvedCount === promises.length) {
resolve(results);
}
})
.catch((error) => {
reject(error);
});
}
});
}
// Example usage
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2));
const promise3 = Promise.reject('Error');
promiseAll([promise1, promise2])
.then((results) => {
console.log(results); // Output: [1, 2]
})
.catch((error) => {
console.error(error); // Output: Error
});
实现响应式布局的方法有多种,下面是一些常见的方法:
使用CSS媒体查询:使用CSS的@media
规则来根据不同的屏幕宽度应用不同的样式。通过定义不同的CSS规则,可以为不同的屏幕尺寸提供不同的布局和样式。
/* 默认样式 */
.container {
width: 100%;
max-width: 960px;
}
/* 媒体查询 */
@media (max-width: 768px) {
.container {
max-width: 480px;
}
}
在上述示例中,容器的最大宽度在不同的屏幕尺寸下会变化。
使用栅格系统:流行的CSS框架(如Bootstrap)提供了栅格系统,可以将页面划分为等宽的列,并根据屏幕尺寸自动调整列的大小和排列顺序。通过使用栅格系统,可以轻松地创建响应式布局。
<div class="container">
<div class="row">
<div class="col-sm-6 col-md-4">内容1</div>
<div class="col-sm-6 col-md-4">内容2</div>
<div class="col-sm-12 col-md-4">内容3</div>
</div>
</div>
在上述示例中,列的宽度在不同屏幕尺寸下会自动调整。
使用弹性盒子布局(Flexbox):Flexbox是一种灵活的布局模型,可以根据容器的大小和内容自动调整项目的位置和大小。通过设置适当的Flexbox属性,可以实现响应式布局。
.container {
display: flex;
flex-wrap: wrap;
}
.item {
flex-basis: 50%; /* 每个项目占据50%的宽度 */
}
@media (max-width: 768px) {
.item {
flex-basis: 100%; /* 将每个项目的宽度改为100% */
}
}
在上述示例中,项目的宽度在不同的屏幕尺寸下会自动调整。
讲一下CSS Flex 的各个属性值:
flex-direction
:定义 flex 容器中主轴的方向,取值为 row
(水平方向,默认值)、column
(垂直方向)、row-reverse
(水平反向)、column-reverse
(垂直反向)。flex-wrap
:定义 flex 容器中 flex 元素是否换行,取值为 nowrap
(不换行,默认值)、wrap
(换行,第一行在上方)、wrap-reverse
(换行,第一行在下方)。justify-content
:定义 flex 容器中主轴上的对齐方式,取值为 flex-start
(开始位置对齐,默认值)、flex-end
(结束位置对齐)、center
(居中对齐)、space-between
(两端对齐,项目之间间距相等)、space-around
(项目周围留有间距,项目之间间距相等)。align-items
:定义 flex 容器中交叉轴上的对齐方式,取值为 flex-start
(起始位置对齐)、flex-end
(结束位置对齐)、center
(居中对齐)、baseline
(基线对齐,元素的第一行文字的基线对齐)、stretch
(拉伸填充容器高度,默认值)。align-content
:定义多根轴线的对齐方式,当有多行时生效,取值与 justify-content
相同。解释一下CSS 动画 animation 各个时间值含义:
animation-name
:指定要应用的关键帧动画名称。animation-duration
:指定一个动画周期的持续时间(秒或毫秒)。animation-timing-function
:指定动画速度曲线,常见的取值有 ease
(默认值,缓入缓出)、linear
(匀速)、ease-in
(加速)、ease-out
(减速)、ease-in-out
(先加速后减速)等。animation-delay
:指定动画开始前的延迟时间(秒或毫秒)。animation-iteration-count
:指定动画循环次数,可以使用具体的次数或者 infinite
表示无限循环。animation-direction
:指定动画播放方向,可选值为 normal
(默认值,正向播放)、reverse
(反向播放)、alternate
(在奇数次正向播放,在偶数次反向播放)和 alternate-reverse
(在奇数次反向播放,在偶数次正向播放)。animation-fill-mode
:指定动画在非活动状态下的样式,可选值为 none
(默认值,动画不会应用到非活动状态)、forwards
(动画结束时将保留最后一个关键帧的样式)、backwards
(动画开始前将应用第一个关键帧的样式)、both
(同时应用 forwards
和 backwards
)。animation-play-state
:指定动画的播放状态,可选值为 running
(默认值,动画正在运行)和 paused
(动画暂停)。animation
属性来实现。通过定义关键帧动画,在不同的关键帧设置元素的旋转角度和水平偏移量,然后通过 animation
属性将动画应用到元素上。@keyframes rotatingAndMoving {
0% {
transform: rotate(0deg) translateX(0);
}
50% {
transform: rotate(180deg) translateX(100px);
}
100% {
transform: rotate(360deg) translateX(0);
}
}
.element {
animation: rotatingAndMoving 5s linear infinite;
}
上述代码将元素进行旋转和横向移动,每次旋转一周并向右移动100像素,总共耗时5秒,并无限循环播放。
ES6 的 Symbol
是一种新的基本数据类型,用于创建唯一的标识符。可以通过 Symbol()
函数来创建一个新的 Symbol。
使用场景:
const privateProperty = Symbol();
const obj = {
[privateProperty]: 'Private value',
publicProperty: 'Public value',
};
console.log(obj.publicProperty); // Output: Public value
console.log(obj[privateProperty]); // Output: Private value
在上述示例中,privateProperty
是一个 Symbol,被用作对象 obj
的私有属性的键。可以通过方括号语法访问该私有属性。
JavaScript 中如何实现继承:
function Parent() {
this.name = 'Parent';
}
function Child() {
this.age = 10;
}
Child.prototype = new Parent();
const child = new Child();
console.log(child.name); // Output: Parent
console.log(child.age); // Output: 10
call
或 apply
方法设置正确的上下文,从而实现继承。子类将继承父类构造函数中的属性和方法。function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
const child = new Child('Child', 10);
console.log(child.name); // Output: Child
console.log(child.age); // Output: 10
function Parent(name) {
this.name = name;
}
Parent.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child = new Child('Child', 10);
console.log(child.name); // Output: Child
console.log(child.age); // Output: 10
child.sayHello(); // Output: Hello, Child
extends
关键字和 super
关键字(ES6):ES6 引入了 class
语法糖来简化继承过程。子类通过 extends
关键字继承父类,并使用 super
关键字调用父类的构造函数。class Parent {
constructor(name) {
this.name = name;
}
sayHello() {
console.log('Hello, ' + this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
const child = new Child('Child', 10);
console.log(child.name); // Output: Child
console.log(child.age); // Output: 10
child.sayHello(); // Output: Hello, Child
extends
和 super
super` 关键字提供了更加简洁和直观的语法,推荐在使用 ES6 时使用。React 中函数组件和类组件有什么区别,何时选择使用哪种组件:
componentDidMount
或 componentDidUpdate
,或者需要定义事件处理函数来响应用户操作,那么类组件是更合适的选择。React.Component
类创建的组件类。类组件可以拥有自己的状态和生命周期方法,以及响应用户交互的事件处理函数。React 中的 context 是什么,如何使用它传递数据给子组件:
undefined
。React.createContext()
方法创建一个 context 对象,并提供一个默认值作为初始值。const MyContext = React.createContext(defaultValue);
MyContext.Provider
组件包裹子组件,并通过 value
属性传递要共享的数据。<MyContext.Provider value={data}>
// 子组件
</MyContext.Provider>
MyContext.Consumer
组件并在其内部使用函数来接收 context 值。<MyContext.Consumer>
{value => (
// 使用 context 值
)}
</MyContext.Consumer>
useContext
钩子函数(仅适用于函数组件)。import React, { useContext } from 'react';
function MyComponent() {
const value = useContext(MyContext);
// 使用 context 值
}
介绍一下项目:
在给定的 n 个数中随机取出 m 个数,要求等概率:
function getRandomNumbers(n, m) {
const numbers = Array.from({ length: n }, (_, index) => index + 1); // 生成包含 1 到 n 的数组
const result = [];
for (let i = 0; i < m; i++) {
const randomIndex = Math.floor(Math.random() * numbers.length); // 随机选取索引位置
result.push(numbers[randomIndex]); // 将选中的数添加到结果数组中
numbers.splice(randomIndex, 1); // 从原数组中移除选中的数
}
return result;
}
手写一下防抖节流函数:
function debounce(func, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function throttle(func, delay) {
let shouldWait = false;
return (...args) => {
if (!shouldWait) {
func.apply(this, args);
shouldWait = true;
setTimeout(() => {
shouldWait = false;
}, delay);
}
};
}
debounce
函数利用闭包保存一个定时器 ID,并在每次触发事件时清除之前的定时器并设置新的定时器来延迟执行回调函数。throttle
函数利用一个布尔值来控制是否执行回调函数,同时设置一个定时器,在固定的时间间隔后重置布尔值。设计实现一个「星级评分」组件:
import React, { useState } from 'react';
function StarRating({ totalStars }) {
const [selectedStars, setSelectedStars] = useState(0);
const handleStarClick = (star) => {
setSelectedStars(star);
};
return (
<div>
{[...Array(totalStars)].map((_, index) => (
<span
key={index}
onClick={() => handleStarClick(index + 1)}
style={{ color: index < selectedStars ? 'yellow' : 'gray', cursor: 'pointer' }}
>
★
</span>
))}
</div>
);
}
export default
import StarRating from './StarRating';
function App() {
return (
<div>
<h2>请评分:</h2>
<StarRating totalStars={5} />
</div>
);
}
export default App;
StarRating
的函数组件,它接受 totalStars
属性表示星星的总数。通过使用 useState
钩子来跟踪用户选择的星星数量,并在点击星星时更新该数量。render
方法中,使用数组的 map
方法和 JSX 来创建一行星星图标,根据用户选择的星星数量,将已选星星的颜色设置为黄色,未选星星的颜色设置为灰色。<StarRating>
组件,并传递 totalStars
属性设置星星的总数。讲一下 HTTP 缓存:
Cache-Control
头字段用于控制资源在客户端的缓存策略,Expires
头字段指定资源的过期时间。If-Modified-Since
和 Last-Modified
标头用于检查资源是否已更改,ETag
和 If-None-Match
标头可以提供更强大的验证机制。讲一下call、apply、bind 三者的区别,如何实现 bind:
call
方法接受一个或多个参数列表。func.call(thisArg, arg1, arg2, ...);
apply
方法接受一个数组作为参数。func.apply(thisArg, [arg1, arg2, ...]);
call
、apply
和 bind
都是 JavaScript 函数的方法,用于改变函数的上下文(即 this
的值)并立即调用该函数。
call
和 apply
的作用类似,都是改变函数的上下文,并传递参数给函数。区别在于传递参数的方式不同:
bind
方法与 call
和 apply
不同,它返回一个新的函数,而不是立即调用函数。绑定的函数可以稍后调用,并拥有指定的上下文和参数。
const boundFunc = func.bind(thisArg, arg1, arg2, ...);
如何实现 bind
方法:
Function.prototype.myBind = function (thisArg, ...args) {
const func = this;
return function (...newArgs) {
return func.apply(thisArg, args.concat(newArgs));
};
}
在 myBind
方法中,首先将调用 bind
的函数保存到变量 func
中。
然后返回一个新的匿名函数,该函数会在调用时使用 apply
将指定的 thisArg
和传入的参数 args
与新的参数 newArgs
进行合并,并调用原始函数 func
。
这样就实现了一个简单的 bind
方法。
需要注意的是,直接修改内置对象的原型可能会导致一些潜在的问题,在实际项目中使用时要小心。另外,现代 JavaScript 中已经有了更好的替代方案,如箭头函数来解决上下文问题。
解释一下闭包,并举一个实际的例子。
闭包是指内部函数可以访问外部函数作用域中的变量,即使在外部函数执行完毕后仍然保持对这些变量的引用。闭包由函数和定义该函数时创建的词法环境(包含了所有局部变量)组成。
例子:
function outer() {
let name = 'John';
function inner() {
console.log('Hello, ' + name + '!');
}
return inner;
}
const greeting = outer();
greeting(); // 输出: Hello, John!
在上述例子中,内部函数 inner
可以访问外部函数 outer
的变量 name
,尽管 outer
函数已经执行完毕。这是因为 greeting
变量实际上保存了对 inner
函数的引用,而 inner
函数形成了闭包,它仍然可以访问到其父函数 outer
的作用域中的变量 name
。
什么是跨域请求?如何解决跨域问题?
跨域请求是浏览器安全机制所限制的一种情况,当你通过 AJAX、WebSockets 或其他方式在一个域名下请求另一个域名的资源时,浏览器会阻止这种跨域请求。跨域请求通常是从一个源(协议、域名、端口)向另一个源发送 AJAX 请求。
解决跨域问题的常见方法包括:
<script>
标签,利用 HTML 中的 <script>
标签不受同源策略限制,从而实现跨域请求。但 JSONP 只支持 GET 请求,并且需要服务端配合返回特定格式的数据。Access-Control-Allow-Origin
字段,允许指定的域名访问资源。CORS 提供了更为灵活和安全的跨域请求解决方案。解释一下事件委托(事件代理)的概念及其优势。
事件委托(也称为事件代理)是一种将事件处理程序绑定到一个父元素而不是每个子元素上的技术。当子元素触发事件时,该事件会冒泡到父元素,并由父元素的事件处理程序来处理。
事件委托的优势包括:
4,讲一下思路和方案:假设你正在开发一个电子商务网站的购物车功能。每当用户点击“加入购物车”按钮时,应该如何将商品信息添加到购物车中,并实时更新购物车的总价和数量?请给出你的思路和实现方案。:
思路和实现方案如下:
localStorage
)来保存购物车数据,以便在页面刷新或用户重新访问时恢复购物车中的商品。关键点:
addEventListener
方法或库/框架提供的相应方法。localStorage
)可以在页面刷新或用户重新访问时保持购物车的状态。购物车数据的读写操作可以在适当的时机进行,例如在添加商品到购物车时保存数据,在页面加载时从本地存储中恢复数据。注意事项:
也给我们的辅导服务打个广告,现在报名支持指定导师哦~