文章目录
- 写在前文
- CSS怪异盒模型
- JS闭包
- 闭包的形成
- 闭包注意点
- CSS选择器及优先级
- 优先级
- 说说flex布局及相关属性
- Flex 容器相关属性:
- Flex 项目相关属性
- 响应式布局如何实现
- 是否用过tailwindcss,有哪些好处
- 好处
- 缺点
- 说说对象的 prototype属性及原型
- 说说 promise
- Proxy 代理对象
- TS是怎么运行在浏览器的,tsc里面有哪些配置项
- 常见配置项
写在前文
这是一篇前端开发真实面试题,我会一直收集,持续更新。
CSS怪异盒模型
CSS 盒模型是指在网页布局时,元素的外部尺寸如何计算。
标准盒模型下,元素的 width 和 height 仅包括 内容区域,而不包括 内边距(padding)、边框(border)和 外边距(margin)。
怪异盒模型(border-box)是与标准盒模型相对的一种模型。在怪异盒模型下,元素的 width 和 height 包括了内容、内边距和边框,但不包括外边距。
标准盒模型:box-sizing: content-box,width 和 height 不包括 padding 和 border。
怪异盒模型:box-sizing: border-box,width 和 height 包括了 padding 和 border。
JS闭包
闭包(Closure)是指一个函数可以“记住”并访问其词法作用域中的变量,即使这个函数在词法作用域之外执行。
简而言之,闭包是一个 函数与其引用环境的组合。这个环境就是函数定义时所处的作用域,而不是调用时的作用域。
闭包的形成
- 当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量时,这个内部函数就是闭包。
闭包注意点
- 内存问题:闭包会保持对外部作用域的引用,因此如果闭包长时间存在且包含大量数据,可能会导致内存泄漏。在使用闭包时,要特别注意不必要的内存占用。
- 性能问题:过多的闭包可能会影响性能,特别是在高频率的调用中。
CSS选择器及优先级
1.基本选择器:
- *(通配符,选择所有元素)
- element(元素选择器,选择指定元素)
- #id(ID选择器,选择特定ID的元素)
- .class(类选择器,选择特定类的元素)
2.组合选择器:
- element1, element2(组合选择器,选择多个元素)
- element > element(子元素选择器,选择直接子元素)
- element + element(相邻兄弟选择器,选择紧接在某元素后的同级元素)
- element ~ element(通用兄弟选择器,选择所有同级元素)
3.伪类选择器:
- :hover(当鼠标悬停在元素上时选择)
- :focus(元素获得焦点时选择)
- :nth-child(n)(选择父元素下第n个子元素)
4.伪元素选择器:
::before(在元素内容之前插入内容)
::after(在元素内容之后插入内容)
优先级
- 内联样式(style=“…”) > ID选择器 > 类选择器、伪类选择器 > 元素选择器、伪元素选择器。
- 合成选择器:如果有多个相同类型的选择器,优先级会相加。
- 重要性(!important):如果某条CSS规则声明了!important,无论其优先级如何,它的优先级都会高于其他规则。
说说flex布局及相关属性
Flexbox(弹性盒布局)是一种 CSS 布局模型,主要用于分配空间并对齐项目,特别适合于一维布局。它通过容器(称为“flex
容器”)和容器内的子项(称为“flex 项目”)来进行布局。
Flex 容器相关属性:
这些属性在父容器(即 Flex 容器)上设置,用于控制容器内元素的布局。
1.display: flex; 或 display: inline-flex
- flex:设置为 Flex 容器,所有直接子元素都成为 Flex 项目。
- inline-flex:设置为行内弹性容器,容器本身为 inline,但是其中的子项仍为弹性布局。
2.flex-direction: 控制项目的排列方向,决定主轴方向。
-
row:默认值,项目沿主轴水平排列(从左到右)。
-
row-reverse:项目沿主轴水平排列,但顺序反向(从右到左)。
-
column:项目沿副轴垂直排列(从上到下)。
-
column-reverse:项目沿副轴垂直排列,但顺序反向(从下到上)。
3.flex-wrap: 控制是否允许换行,如果项目超出容器的宽度或高度时,是否允许换行。
-
nowrap:默认值,所有项目都在一行内显示,不换行。
-
wrap:项目换行显示(从上到下或者从左到右)。
-
wrap-reverse:项目换行显示,换行的顺序与 wrap 相反。
4.justify-content: 控制主轴(横向或纵向)上的对齐方式。
-
flex-start:默认值,项目从主轴的起点开始排列。
-
flex-end:项目从主轴的终点开始排列。
-
center:项目在主轴上居中排列。
-
space-between:项目在主轴上均匀分布,第一个项目放在起点,最后一个项目放在终点,项目之间的空白平均分配。
-
space-around:项目在主轴上均匀分布,项目之间的空白相等,但两端的空白为项目之间空白的一半。
-
space-evenly:项目在主轴上均匀分布,项目之间的空白和两端的空白都相等。
5.align-items: 控制交叉轴(垂直于主轴方向)的对齐方式。
-
stretch:默认值,项目拉伸以填满容器(如果项目的高度未被指定)。
-
flex-start:项目在交叉轴的起点对齐。
-
flex-end:项目在交叉轴的终点对齐。
-
center:项目在交叉轴上居中对齐。
-
baseline:项目在基线对齐(基于文本行的基线)。
6.align-content: 控制多行项目的对齐方式,当存在多行时使用(与 align-items 不同,后者仅对单行项目起作用)。
-
stretch:默认值,行间距拉伸以填满容器。
-
flex-start:行集对齐到交叉轴的起点。
-
flex-end:行集对齐到交叉轴的终点。
-
center:行集居中对齐。
-
space-between:行间距均匀分布,第一行放在交叉轴的起点,最后一行放在终点,其他行平均分布。
-
space-around:行间距均匀分布,行之间的空白相等,行两端的空白为其他空白的一半。
Flex 项目相关属性
这些属性应用于 Flex 容器中的每个子项(即 Flex 项目)上,用于控制项目的排列和缩放。
1.flex-grow: 定义项目的放大比例,默认值为 0(即项目不放大)。如果所有项目的 flex-grow 都为 1,它们会平分可用空间。
- flex-grow: 1;:项目会放大以占据多余空间。
- flex-grow: 0;:项目不会放大。
2.flex-shrink: 定义项目的缩小比例,默认值为 1(即项目会缩小以适应容器)。如果容器空间不足,项目会按比例缩小。
flex-shrink: 1;:项目会缩小以适应容器。
flex-shrink: 0;:项目不会缩小。
3.flex-basis: 定义项目在主轴方向上的初始大小,默认值为 auto(即项目的本来大小)。
-
flex-basis: 100px;:项目的基础大小是 100px。
-
flex-basis: auto;:项目的基础大小是其内容的自然大小。
4.flex: 是 flex-grow、flex-shrink 和 flex-basis 的简写。
-
flex: none;:flex-grow: 0;,flex-shrink: 0;,flex-basis:
auto;(即不放大、不缩小,基础大小是内容大小)。 -
flex: 1;:flex-grow: 1;,flex-shrink: 1;,flex-basis:
0;(即项目可以放大,且占据可用空间)。
5.align-self: 控制单个项目在交叉轴上的对齐方式,覆盖 align-items。
-
auto:使用 align-items 的值。
-
flex-start:项目在交叉轴的起点对齐。
-
flex-end:项目在交叉轴的终点对齐。
-
center:项目在交叉轴上居中对齐。
-
baseline:项目在基线对齐。
-
stretch:项目拉伸以填满容器。
响应式布局如何实现
1.媒体查询(Media Queries)
媒体查询是实现响应式布局最常用的方法之一。 它根据不同的屏幕宽度、分辨率等条件应用不同的 CSS 样式。 通过 @media规则,开发者可以为不同设备条件定义不同的样式。
2.百分比布局
使用百分比单位来布局,可以使元素的宽度和高度相对于父容器自适应,从而实现响应式布局。
当容器宽度变化时,子元素会相应地调整自己的宽度,适应不同的设备和屏幕大小。
3.Flexbox 布局
Flexbox 是一种非常强大的布局方式,可以非常方便地实现响应式设计。使用 Flexbox,你可以轻松地控制子元素的大小、顺序和对齐方式,自动调整布局。
.container {display: flex;flex-wrap: wrap; /* 让元素换行 */
}.column {flex: 1; /* 每个子元素占据相等的空间 */min-width: 200px; /* 最小宽度,避免在小屏幕上太窄 */
}@media (max-width: 768px) {.column {flex: 0 0 100%; /* 在小屏幕上,每列占满一行 */}
}
4.CSS Grid 布局
CSS Grid 是一种更高级的布局技术,适用于更复杂的响应式布局。它可以让你定义网格(rows 和 columns),并控制内容如何在网格中排列,能够实现精确的响应式设计。
5.Viewport 单位
使用 vw(视口宽度)和 vh(视口高度)单位可以根据视口的尺寸动态调整元素的尺寸。例如,1vw 表示视口宽度的 1%,1vh 表示视口高度的 1%。
是否用过tailwindcss,有哪些好处
Tailwind CSS 是一种非常流行的 实用类(utility-first) CSS框架,旨在通过提供一组预定义的类,帮助开发者快速构建响应式、定制化的界面。与传统的 CSS 方法不同,Tailwind 通过直接在 HTML元素中使用小的类来设置样式,而不需要在外部定义复杂的 CSS 类。
好处
1.快速开发
实用类(Utility-first):Tailwind 提供了大量的小类,每个类实现一个特定的样式(如 text-center, bg-blue-500, p-4)。这些类可以组合起来快速构建布局和样式,而不需要写大量的自定义 CSS。
2. 高效的定制化
高度可定制:Tailwind 的配置文件 tailwind.config.js 允许你根据项目需求定制颜色、间距、字体、断点等。你可以轻松地扩展框架,创建符合设计规范的自定义类。
3.响应式设计的简便性
内置响应式类:Tailwind 提供了内置的响应式设计工具,使用非常简单。例如,使用 sm:, md:, lg: 等前缀来为不同的屏幕大小设置不同的样式。
4. 不冗余的 CSS
按需生成 CSS:Tailwind 使用工具,如 PurgeCSS,来删除未使用的 CSS 类,这样你最终打包的 CSS 文件会尽可能小。这个过程确保了即使你在 HTML 中使用了大量的类,也只会生成你实际使用的 CSS 样式,减少了冗余代码。
5. 提高可维护性
避免重复的样式:在传统 CSS 开发中,可能会出现多个类似的 CSS 类或者重复的样式规则,而 Tailwind 通过原子类的方式避免了这些重复。每个类仅做一件事,这样可以避免样式冲突和多余的代码。
6. 更高的可读性和可操作性
快速迭代:由于 Tailwind 让开发者直接在 HTML 中定义样式,开发者可以更直观地看到元素的外观和布局。你可以在编辑 HTML 的同时立即看到变化,这种方式有助于快速原型设计和快速迭代。
7. 社区支持和生态系统
庞大的社区:Tailwind 拥有一个活跃的社区,很多开发者共享资源、组件、工具和插件。你可以轻松找到现成的解决方案来快速实现复杂的布局和样式。
UI 组件库:像 Tailwind UI、DaisyUI 等组件库也为开发者提供了大量的预制 UI 组件,快速构建界面。
缺点
虽然 Tailwind 有很多优点,但它也有一些缺点:
- HTML 文件中类名过多:由于大量的样式类嵌套在 HTML 中,某些开发者可能会觉得代码过于冗长,尤其是在较复杂的页面中,类名的堆叠会让
HTML 变得不够简洁。 - 学习曲线:对于刚接触 Tailwind 的开发者,学习如何使用和组合这些类可能需要一些时间。特别是对于那些习惯了传统 CSS 或其他
CSS 框架的人来说,可能需要适应。 - 不适合小项目:如果是一个小型项目或者一个简单的静态页面,Tailwind 可能会显得有些过于庞大,可能不需要其全部功能。
- 无法直观看到样式:有些开发者认为直接在 HTML 中使用类并没有传统的 CSS 样式表那么直观,尤其是在大型项目中,很多样式都分散在不同的
HTML 元素中。
说说对象的 prototype属性及原型
在 JavaScript 中,每个对象都有一个内部属性 [[Prototype]],通常通过 proto 或 prototype 来访问。理解原型(prototype)是理解 JavaScript 面向对象编程的关键。
1. prototype 属性
定义: prototype 是一个对象,它是由构造函数创建的,且每个 JavaScript 函数(构造函数)都有一个 prototype属性。这个 prototype 对象会作为新对象的原型,赋给新对象的 [[Prototype]](即通过 __proto__可以访问到的对象)。 当你使用构造函数创建一个新对象时,新对象会继承该构造函数的 prototype 对象中的属性和方法。
2.原型(Prototype)
原型是 JavaScript 中实现继承的核心机制。每个对象都有一个原型(即 [[Prototype]],可以通过 __proto__来访问)。当你访问对象的某个属性或方法时,JavaScript 会首先查找该对象本身是否有该属性。如果没有,它会查找对象的原型(即[[Prototype]]),如果原型中也没有,就继续向原型链上查找,直到 null 为止。
原型链:
原型链是由一系列对象组成的链条,每个对象都有一个指向其原型的引用。原型链的终点是 null,它表示没有更多的原型了。
3.原型链的构造
JavaScript 中的每个对象都是由一个构造函数创建的。构造函数会通过 prototype 属性来为该对象的实例添加方法或属性。当你使用new 关键字时,会创建一个新对象,并将其 [[Prototype]] 设置为构造函数的 prototype 对象。
4.如何访问对象的原型
- proto:可以通过 proto 访问对象的原型链。注意,proto 是非标准的,但大多数现代浏览器都支持它。
- Object.getPrototypeOf():这是访问对象原型的标准方法,推荐使用。
说说 promise
Promise 是 JavaScript 中用于处理异步操作的对象,它代表一个 异步操作的最终完成(或失败)及其结果值的表示。Promise
提供了更为优雅和可维护的方式来处理回调函数,避免了传统的回调地狱(callback hell)。
1. Promise 的状态
Promise 对象有三种状态:
- pending(待定):表示异步操作正在进行中,Promise 处于未完成的状态。
- fulfilled(已完成):表示异步操作已成功完成,Promise 已经解决(resolved),并且有一个结果值。
- rejected(已拒绝):表示异步操作失败,Promise 被拒绝(rejected),并且有一个错误原因。
状态转换:
pending → fulfilled(成功)
pending → rejected(失败)
一旦 Promise 从 pending 转换为 fulfilled 或 rejected,它的状态就不能再变化了。
2. 创建 Promise
Promise 对象通过构造函数创建,构造函数接受一个 executor(执行器) 函数作为参数。这个执行器函数有两个参数:resolve 和 reject,分别用于改变 Promise 的状态为 fulfilled 或 rejected。
let promise = new Promise((resolve, reject) => {let success = true;if (success) {resolve("操作成功");} else {reject("操作失败");}
});promise.then((value) => {console.log(value); // 如果 promise 状态为 fulfilled,输出:操作成功
}).catch((error) => {console.log(error); // 如果 promise 状态为 rejected,输出:操作失败
});// resolve(value):当异步操作成功时,调用 resolve,将状态从 pending 改为 fulfilled,并将结果值传递给 then() 方法。
// reject(error):当异步操作失败时,调用 reject,将状态从 pending 改为 rejected,并将错误原因传递给 catch() 方法。
3. Promise 的方法
Promise 提供了几个链式方法,用于处理异步操作的结果。
- then(onFulfilled, onRejected):该方法用于指定当 Promise 状态变为 fulfilled
时的回调函数(onFulfilled),以及当状态变为 rejected 时的回调函数(onRejected)。 - catch(onRejected):该方法是 .then(null, onRejected) 的别名,用于指定当 Promise被拒绝时的回调函数。
- finally(onFinally):无论 Promise 最终是成功还是失败,都会执行 finally
中的回调函数,通常用于执行清理操作(例如隐藏加载动画)。
Proxy 代理对象
Proxy 是 JavaScript 中用于创建一个对象的 代理,它可以通过拦截对象的基本操作(如访问属性、赋值、删除属性等)来增强或改变对象的行为。通过 Proxy,你可以控制对目标对象的访问、修改或者监控。
Proxy 是 ECMAScript 6(ES6)引入的,它是一个非常强大的工具,能够帮助你实现很多高级功能,比如数据验证、属性拦截、性能优化等。
1.Proxy 的特点
- 透明性:Proxy
使得目标对象的操作可以被拦截和自定义,但可以让外部调用看起来是透明的,也就是通过代理对象进行操作不会破坏目标对象的行为。 - 灵活性:Proxy 提供了非常多的拦截方法,几乎可以拦截对象的所有操作。你可以根据需要灵活地定制行为。
- 性能:虽然 Proxy 可以提供很多强大的功能,但它也会带来一定的性能开销,尤其是当你在大量操作上使用代理时。
2.创建一个 Proxy
Proxy 构造函数接受两个参数:
target:被代理的目标对象,它是你实际操作的对象。
handler:一个对象,定义了代理对象的行为(拦截操作)。
// 目标对象
let target = {message: "Hello, Proxy!"
};// handler 对象,定义代理的行为
let handler = {get: function(target, prop, receiver) {if (prop in target) {return target[prop];} else {return `Property "${prop}" does not exist on target`;}}
};// 创建 Proxy 对象
let proxy = new Proxy(target, handler);console.log(proxy.message); // 输出:Hello, Proxy!
console.log(proxy.nonExistentProperty); // 输出:Property "nonExistentProperty" does not exist on target
3.常见的拦截方法
Proxy 通过 handler 对象中的一系列方法来拦截对象的操作。每个方法代表了对不同操作的拦截。常用的拦截方法如下:
- get 用于拦截对目标对象属性的访问。
- set用于拦截对目标对象属性的赋值操作。
- deleteProperty用于拦截 delete 操作,即删除目标对象的属性。
- has用于拦截 in 操作,检查属性是否存在。
- ownKeys用于拦截 Object.keys()、Object.getOwnPropertyNames() 等方法,返回目标对象的所有键。
- apply(用于函数代理)用于拦截函数调用操作。当代理对象是一个函数时,apply 可以拦截函数调用。
- construct(用于构造函数代理)用于拦截构造函数调用。
TS是怎么运行在浏览器的,tsc里面有哪些配置项
TypeScript 代码本身不能直接在浏览器中运行。浏览器只能理解 JavaScript,而 TypeScript 是JavaScript 的超集,包含类型检查和其他一些功能。在浏览器中运行 TypeScript 代码的过程通常需要通过以下步骤:
1. TypeScript 编译为 JavaScript
使用 tsc(TypeScript Compiler)命令行工具或者构建工具(如 Webpack、Vite 等)将 TypeScript 代码编译成 JavaScript 代码。tsc 会读取你的 TypeScript 文件(.ts 或 .tsx)并生成相应的 JavaScript 文件(.js)。
2.在浏览器中运行 JavaScript
一旦 TypeScript 被编译为 JavaScript 文件,浏览器就能够直接执行这些 JavaScript 文件。你可以通过
3. 使用模块化工具
对于现代的前端开发,TypeScript 代码往往涉及模块化。你可以使用像 Webpack、Vite、Rollup 等构建工具,将多个 TypeScript 文件打包成浏览器可以理解的单个或多个 JavaScript 文件。
4.配置 tsconfig.json
为了控制 TypeScript 编译过程,通常会使用一个 tsconfig.json 配置文件来指定编译选项,确保 TypeScript 正确地编译成符合浏览器要求的 JavaScript 代码。
常见配置项
compilerOptions 是 tsconfig.json 中最重要的部分,它指定了 TypeScript 编译器的行为。常见的配置项包括:
1.target:指定编译后的 JavaScript 版本。可以设置为:
- ES3
- “ES5”
- “ES6” / “ES2015”
- “ES2016”, “ES2017”, “ES2018”, “ES2019”, “ES2020”, “ES2021”
- “ESNext”(最新的 ECMAScript 版本)
2.module:指定模块系统,默认是 “CommonJS”,可以设置为:
- “CommonJS”(用于 Node.js)
- “ESNext”(支持 ES6 模块)
- “AMD”, “System”, “UMD”, “ES6”, “ES2015”
3.moduleResolution:指定如何解析模块。可以设置为:
- “node”:基于 Node.js 的模块解析规则。
- “classic”:旧版的模块解析规则。
4.strict:启用所有严格的类型检查选项。这会提高代码的类型安全性。
5.esModuleInterop:启用 ECMAScript 模块与 CommonJS 模块的互操作性。允许使用 import 导入 CommonJS 模块。
6.outDir:指定输出目录。编译后的 JavaScript 文件将被输出到该目录。
7.sourceMap:生成源映射文件,用于调试,帮助浏览器调试时将编译后的代码映射回原始的 TypeScript 代码。
8.removeComments:在编译时移除代码中的注释。
9.lib:指定包含的库文件,如 “ES6”, “DOM”, “ESNext” 等。可以使用此选项来添加或移除 JavaScript 的内置类型定义。
include 和 exclude 配置项
1.include:指定要包含的文件或文件夹路径,可以使用通配符。默认情况下,TypeScript 会包括所有 .ts、.tsx 和 .d.ts 文件。
2.exclude:指定不包含的文件或文件夹路径。默认情况下,node_modules 会被排除。