【前段面试3+1】21 前端的布局方式有哪些、vw和vh、原型链和类继承的区别、【删除有序数组中的重复项】

发布于:2024-08-02 ⋅ 阅读:(63) ⋅ 点赞:(0)

一、前端的布局方式有哪些?

以下将列出部分布局方式及其简单举例

1.流式布局(Flow Layout):

        传统的HTML布局,元素按照它们在文档中出现的顺序排列。

<div>这是一个流式布局的div。</div>
<p>这是段落文本。</p>
优点:
  • 灵活性:流式布局可以根据不同的屏幕尺寸和分辨率自适应调整,提供更灵活的显示效果。
  • 响应性:流式布局是响应式设计的基础,能够适应各种设备,包括手机、平板和桌面电脑。
  • 用户体验:由于布局能够自适应,用户在不同设备上浏览网页时可以获得一致的体验。
  • 维护简便:相比固定宽度的布局,流式布局通常只需要设置一套样式规则,减少了维护的复杂性。
  • 内容优先:流式布局强调内容的重要性,布局会根据内容的多少自动调整,避免出现过多的空白。
缺点:
  • 控制性差:由于布局元素的尺寸会随浏览器窗口变化,因此在某些情况下可能难以精确控制元素的布局和间距。
  • 设计限制:对于一些需要固定布局或特定排版的设计,流式布局可能无法满足需求。
  • 性能问题:在某些情况下,流式布局可能需要更多的计算来适应不同的屏幕尺寸,这可能会影响页面加载速度。
  • 兼容性问题:尽管现代浏览器普遍支持流式布局,但在一些老旧的浏览器上可能会出现兼容性问题。
  • 过度滚动:在小屏幕上,流式布局可能导致内容过宽,用户需要水平滚动来查看全部内容,影响阅读体验。

2.弹性布局(Flexible Box Layout,Flexbox)

        一种更加高效的布局方式,允许容器内元素在不同屏幕尺寸下动态调整大小和排列。

<div class="container">
    <div>Flex 1</div>
    <div>Flex 2</div>
</div>
.container {
    display: flex;
    flex-direction: row; /* 或者 column */
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
}
优点:
  • 空间分配:易于在容器内分配空间。
  • 对齐与分布:提供丰富的对齐和分布选项。
  • 响应式设计:支持不同屏幕尺寸的自适应布局。
  • 简化代码:减少CSS代码量,提高开发效率。
  • 兼容性:现代浏览器广泛支持。
缺点:
  • 学习曲线:需要时间学习Flexbox的属性和行为。
  • 旧浏览器兼容性:可能需要额外的前缀或polyfills支持。
  • 过度使用:可能导致布局复杂,难以维护。
  • 性能影响:在处理大量元素时可能影响性能。
  • 特定布局限制:对于某些复杂布局,可能不如CSS Grid直观。

3.网格布局(Grid Layout):

一种二维布局系统,可以创建复杂的行和列布局,非常适合复杂的页面布局。

html:

<div class="grid-container">
    <div>Grid 1</div>
    <div>Grid 2</div>
    <div>Grid 3</div>
</div>

css:

.grid-container {
    display: grid;
    grid-template-columns: auto 1fr auto; /* 定义三列 */
    grid-gap: 10px;
}
优点:
  • 二维布局:同时控制行和列的布局方式。
  • 灵活性:可以创建复杂的布局结构。
  • 对齐与分布:提供丰富的对齐和分布选项。
  • 响应式:易于实现响应式设计。
  • 控制性:对布局的控制力强,易于实现精确布局。
缺点:
  • 学习曲线:相对于其他布局模型,学习成本较高。
  • 浏览器兼容性:尽管现代浏览器支持良好,但一些旧浏览器可能不支持或需要polyfills。
  • 性能问题:在处理大量网格项时可能影响性能。
  • 过度使用:可能导致布局过于复杂,难以维护。

4.浮动布局(Float Layout)

通过CSS的float属性使元素脱离文档流,通常用于创建多列布局。

html:

<div class="float-left">浮动到左边</div>
<div>右侧内容</div>

css:

.float-left {
    float: left;
    width: 50%;
}
优点
  • 简单性:浮动是CSS中的基本特性,易于理解和使用。
  • 兼容性:几乎所有的浏览器都支持浮动。
缺点
  • 布局限制:难以创建复杂的布局结构。
  • 维护性:浮动元素脱离文档流,可能导致布局混乱。
  • 响应性:需要额外的技巧来实现响应式设计。
  • 清除浮动:需要使用clear属性或额外的元素来清除浮动,以避免布局问题。

5.定位布局(Positioned Layout)

使用CSS的position属性(如relativeabsolutefixedsticky)来控制元素的位置。

html:

<div class="relative">
    <div class="absolute">绝对定位元素</div>
</div>

css:

.relative {
    position: relative;
}
.absolute {
    position: absolute;
    top: 20px;
    left: 20px;
}

6.响应式布局(Responsive Layout)

利用媒体查询(Media Queries)来根据设备特性(如屏幕尺寸、分辨率)调整布局。

css:

@media (max-width: 600px) {
    .container {
        flex-direction: column;
    }
}
优点
  • 自适应性:能够适应不同屏幕尺寸和设备。
  • 用户体验:提供一致的浏览体验,无论设备大小。
  • 维护性:通常只需要一套样式表,简化维护。
  • 搜索引擎优化:响应式设计有助于提高网站的SEO表现。
缺点
  • 复杂性:实现响应式设计可能需要更复杂的CSS和JavaScript。
  • 性能:响应式图片和媒体查询可能会影响页面加载速度。
  • 测试:需要在多种设备和分辨率上进行测试,以确保兼容性。
  • 控制性:在某些情况下,响应式设计可能难以实现精确的布局控制。

7.自适应布局(Adaptive Layout)

根据设备类型或屏幕尺寸范围提供不同的布局设计。

.container {
    width: 100%;
    max-width: 1200px; /* 最大宽度 */
    margin: 0 auto; /* 水平居中 */
}

8.框架布局(Frame Layout)

使用框架(如<iframe><frame>)来嵌入其他页面或内容。

9.单列布局(Single-column Layout)

将内容分成多个垂直列,类似于报纸的布局。

<div class="single-column">
    内容从上到下排列。
</div>

10.圣杯布局(Holy Grail Layout)

一个经典的三列布局,中间列宽度可变,两侧列固定宽度。

html:

<div class="container">
    <div class="sidebar">侧边栏1</div>
    <div class="content">主要内容</div>
    <div class="sidebar">侧边栏2</div>
</div>

css:

.container {
    display: flex;
    justify-content: space-between;
}
.sidebar {
    flex: 1;
}
.content {
    flex: 3;
}

11.CSS框架布局

使用CSS框架(如Bootstrap, Foundation, Tailwind CSS等)提供的预定义类和组件来快速构建布局。

html:

<div class="container">
    <div class="row">
        <div class="col-md-4">列1</div>
        <div class="col-md-4">列2</div>
        <div class="col-md-4">列3</div>
    </div>
</div>

12.视口单位布局(Viewport Units Layout)

使用视口宽度(vw)、视口高度(vh)等单位来创建响应式元素尺寸。

html:

<div class="full-screen">全屏元素</div>

css:

.full-screen {
    width: 100vw; /* 视口宽度 */
    height: 100vh; /* 视口高度 */
}

二、vw、vh是什么?

vwvh 是CSS中用于布局的视口单位(Viewport Units)。

  • vw:视口宽度的百分比(Viewport Width)。1vw 等于视口宽度的1%。例如,如果视口宽度是100个单位,那么 10vw 就是10个单位。这个单位常用于创建响应式设计,因为它会根据视口宽度的变化而变化。

  • vh:视口高度的百分比(Viewport Height)。1vh 等于视口高度的1%。与 vw 类似,vh 也是根据视口高度来计算的。这允许元素的高度能够根据视口的高度自适应变化。

三、JavaScript中原型链和类继承有什么区别?

JavaScript中的原型链和类继承是两种不同的概念,它们用于实现代码的复用和继承特性。以下是原型链和类继承的主要区别:

1.实现方式

  • 原型链:基于原型的对象继承。在JavaScript中,每个对象都有一个内部属性[[Prototype]](可以通过__proto__访问),它指向了创建该对象时所用构造函数的原型对象。属性和方法的查找会沿着这个原型链进行。
  • 类继承:基于ES6中引入的class语法。类继承实际上是语法糖,其背后仍然使用原型链来实现。class允许你定义一个构造函数,并使用extends关键字来实现继承。

2.语法

  • 原型链:使用构造函数和显式的原型对象(prototype)来定义对象的结构和行为。
  • 类继承:使用class关键字来定义类,并通过extends关键字来实现类的继承。

3.可读性

  • 原型链:原型链的继承可能难以追踪,尤其是在多层继承的情况下。
  • 类继承:提供了更清晰和直观的语法,使代码更容易理解和维护。

4.构造函数的使用

  • 原型链:在原型链继承中,构造函数可以通过callapply方法被调用,以确保能正确设置this的上下文。
  • 类继承:在类继承中,子类的构造函数会自动调用父类的构造函数(使用super()),确保this的上下文被正确设置。

5.方法访问

  • 原型链:方法必须定义在原型对象上,所有实例共享这些方法。
  • 类继承:方法可以定义在类体内,每个子类实例都可以有自己的方法实例,也可以通过static关键字定义静态方法。

6.私有属性和方法

  • 原型链:没有真正的私有属性或方法,但可以使用闭包来模拟。
  • 类继承:可以使用#前缀定义私有字段和方法,提供了更好的封装性。

7.多重继承

  • 原型链:可以通过组合多个原型来实现多重继承的效果。
  • 类继承:不支持直接的多重继承,但可以通过混入(mixins)或其他模式来实现类似的效果。

8.性能

  • 原型链:由于原型链的查找可能涉及多个对象,可能会有轻微的性能开销。
  • 类继承:由于JavaScript引擎的优化,类继承的性能通常与原型链相当。

9.兼容性

  • 原型链:所有JavaScript引擎都支持原型链。
  • 类继承:较新的JavaScript引擎支持class关键字,但一些旧的浏览器可能需要转译。

四、【算法】删除有序数组中的重复项

题目:

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 非严格递增 排列
int removeDuplicates(int* nums, int numsSize) {
    
}

题解:

我们可以使用双指针的方法。

  1. 首先,检查数组的大小,如果为 0,则返回 0。
  2. 初始化一个指针 k 为 1,因为我们至少有一个唯一的元素。
  3. 遍历数组,从第二个元素开始,比较当前元素和前一个元素:如果两个元素不同,就意味着找到了一个新的唯一元素,我们将其放到 k 指向的位置,并将 k 增加 1。
  4. 循环结束后,返回 k,这就是去重后的数组中唯一元素的个数。
int removeDuplicates(int* nums, int numsSize) {  
    if (numsSize == 0) {  
        return 0;  // 如果数组为空,返回 0  
    }  

    int k = 1;  // k 初始化为 1,因为至少存在一个唯一元素  

    for (int i = 1; i < numsSize; i++) {  
        // 只要当前元素与前一个元素不同,就将其放置在 k 的位置  
        if (nums[i] != nums[k - 1]) {  
            nums[k] = nums[i];  
            k++;  // 增加 k 的大小  
        }  
    }  

    return k;  // 返回唯一元素的个数  
}