前端面经真题解析10-字节/抖音电商/前端/超详细记录

文章目录

1. 自我介绍

当在前端面试中进行自我介绍时,你可以按照以下结构进行组织:

  • 介绍自己:

开始时,简要介绍自己的姓名和背景。你可以提及自己的学历、专业背景以及工作经验。

  • 技术专长:

提及你在前端领域的技术专长和经验。列举你熟悉的编程语言、前端框架、库和工具等。可以着重强调你在哪些方面特别擅长,例如前端开发、响应式设计、用户界面设计、性能优化等。

  • 项目经验:

介绍你在过去的项目中所承担的角色和贡献。可以选择其中几个有代表性的项目来详细介绍,包括项目的规模、技术栈、解决的问题以及你在项目中的具体工作。

  • 学习与成长:

提及你的学习态度和成长经历。你可以分享你如何持续学习新技术、参与开发社区、阅读技术文章和书籍等。强调你对于前端领域的持续关注和追求,以及你在学习过程中遇到的挑战和如何克服它们。

  • 团队合作与沟通能力:

强调你在团队合作中的角色和能力。你可以提及你在过去的团队合作中的贡献,以及你如何与其他团队成员协作、解决问题和有效沟通的能力。

  • 总结与展望:

总结你的自我介绍,并表达你对前端领域的热情和对未来的展望。你可以提及你对前端技术的热爱和对新技术的追求,以及你希望在未来的工作中继续学习和成长。

自我介绍需要简明扼要地表达你的关键信息,同时展现你的技术实力、学习能力和团队合作能力。在介绍过程中,注意保持清晰的表达、自信的姿态和积极的态度。记得准备并练习自我介绍,以确保你能在面试中自信地展现自己。

2. 介绍下自己的项目

  • 项目背景:首先,简要介绍你所参与的项目的背景和目的。说明该项目是什么,它解决了什么问题,以及它在业务或技术上的重要性。

  • 技术栈:列出你在项目中使用的主要技术栈和工具。这包括编程语言、框架、库和其他相关技术。说明你选择这些技术的原因以及它们在项目中的作用。

  • 功能和模块:简要描述项目的主要功能和模块。说明你在项目中负责的具体部分,以及你如何与团队协作开发这些功能和模块。

  • 技术亮点:强调项目中的一些技术亮点或你在项目中遇到的挑战。这可以是你解决的技术难题、性能优化、跨浏览器兼容性或响应式设计等方面的问题。

  • 成果和影响:列举项目的成果和影响。这可以是用户数量增长、用户反馈的改善、网站性能提升等。如果有具体的数据或统计结果支持,更好地展示项目的价值。

  • 学习和成长:分享你在项目中学到的经验和取得的成长。说明你在项目中遇到的挑战,以及你如何克服这些挑战并学到新的知识和技能。

  • 自我评价:最后,对你在项目中的贡献和经验进行自我评价。突出你在项目中发挥的作用,以及你对前端开发的热情和承诺。

确保在介绍项目时,简明扼要地表达你的观点,并与面试官保持良好的沟通。提供实际的例子和具体的细节,以支持你的介绍。同时,准备好回答与项目相关的问题,展示你对项目的深入了解。

3. 看你项目里面用了axios,说下请求拦截和响应拦截怎么做?

在前端项目中,可以使用axios库进行网络请求。Axios提供了请求拦截器和响应拦截器的功能,用于在请求发送之前和响应返回之后对请求进行拦截、修改或处理。

请求拦截器(Request Interceptor)可以用于在发送请求之前对请求进行处理,比如添加请求头、设置公共参数等。响应拦截器(Response Interceptor)则可以用于在接收到响应后对响应进行处理,比如统一处理错误信息、数据格式转换等。

下面是一个示例代码,展示了如何在前端项目中使用axios的请求拦截器和响应拦截器:

import axios from 'axios';

// 创建axios实例
const instance = axios.create({
  baseURL: 'https://api.example.com', // 设置接口根路径
  timeout: 5000 // 设置请求超时时间
});

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么
    // 可以在这里添加请求头、设置公共参数等
    config.headers['Authorization'] = 'Bearer ' + getToken(); // 示例:添加身份验证token
    return config;
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 响应拦截器
instance.interceptors.response.use(
  response => {
    // 对响应数据做些什么
    // 可以在这里统一处理响应数据、错误信息等
    return response.data;
  },
  error => {
    // 对响应错误做些什么
    return Promise.reject(error);
  }
);

export default instance;

在上面的示例中,我们首先创建了一个axios实例,并设置了一些基本配置,如接口根路径和请求超时时间。然后,通过调用interceptors.request.use方法来添加请求拦截器,使用interceptors.response.use方法添加响应拦截器。

在请求拦截器中,我们可以对config进行修改,例如在请求头中添加身份验证token。在响应拦截器中,我们可以对响应数据进行处理,例如提取出返回的数据部分。通过return语句可以将修改后的请求配置或处理后的响应数据传递给下一个拦截器或请求调用处。

这样,每次发送请求时,请求拦截器会先执行,然后发送请求,接收到响应后会依次执行响应拦截器,从而实现对请求和响应的拦截和处理。

4. 说下项目里面前后端交互过程及设计?

5. 怎么处理切换分页请求数据的,优化手段?

6. 说下你爬取别人网站数据的时候,别人如果设置了拦截,你的解决方案是?

7. 你说下http请求的refer字段?

Origin,Host和Referer是两个常见的HTTP请求头字段,它们在Web开发中有不同的作用和含义。

Origin字段:

1.含义:

Origin字段用于指示发起跨域请求的源(即发起请求的页面所在的域)。

由三部分组成

scheme:协议,如 http、https。
host:域名或 IP 地址。如 127.0.0.1、juejin.cn。
port:端口,可选。如果省略,默认为当前协议的默认端口(如 HTTP 的 80、HTTPS 的 443)

在非 GET 和 HEAD 方法的同源请求中,浏览器也会加上 Origin(不理解,奇奇怪怪)

2.作用:
主要用于跨域请求的安全控制。当浏览器发起跨域请求时,会自动添加Origin头字段,用于告知服务器请求的来源。服务器可以根据Origin字段来判断是否允许跨域请求,并进行相应的处理。在服务器端设置响应头Access-Control-Allow-Origin来控制是否允许来自特定源的跨域请求。

3.示例:
请求头:Origin: https://www.example.com

Referer字段:

1.含义:

Referer字段用于指示请求的来源页面(即上一个页面的URL)。

2.作用:

主要用于跟踪和统计,以及防盗链等场景。服务器可以通过检查Referer字段来了解请求是从哪个页面跳转过来的,从而进行一些操作,如统计流量来源、防止恶意请求、防止盗链等。

3.注意:

Referer字段的值并不可信,因为它可以被客户端篡改或者浏览器设置中禁用。服务器端在使用Referer字段时应谨慎处理,并不依赖于其完全准确的值。

4.示例:
请求头:Referer: https://www.example.com/page1.html

Host字段

Host 由两部分组成:

host:域名或 IP 地址
port:端口,可选项。

Host: a.com:5500
Host: a.com

在 HTTPS 下,你在浏览器的开发者工具可能会看到这个玩意::authority。这是 HTTP2 协议中定义的伪头字段,向后兼容 HTTP1,对应 Host。

Host 可以用于代理,当多个域名指向同一个 IP (后端)时,Web Server 可以通过 Host 来识别并提供不同的服务。

区别:

Origin字段和Referer字段都提供了请求来源的信息,但含义和用途略有不同。
Origin字段主要用于跨域请求的安全控制,服务器可以根据Origin字段来判断是否允许跨域请求,并进行相应的处理。

Referer字段主要用于标识请求的来源页面,服务器可以通过检查Referer字段来了解请求是从哪个页面跳转过来的,用于统计、(图片)防盗链(refer来源白名单)等场景。

Host字段一般是在后端服务中,用于区分请求来源(后端)。

需要注意的是,由于安全和隐私的考虑,浏览器在某些情况下可能会限制或篡改这些头字段的值,因此在服务器端处理时应谨慎对待,并不依赖于其完全准确的值。

8. 看你做了路由懒加载,怎么实现的?

前端路由懒加载是一种优化技术,它通过将页面组件的加载延迟到实际需要时才进行,而不是一次性将所有页面组件都加载到初始包中。这可以减少初始加载时间和资源消耗,提升应用性能和用户体验。

传统的路由加载方式会在初始加载时将所有路由组件一起打包到主应用程序包中,这意味着无论用户是否访问某个特定页面,所有页面的代码都会被下载。而使用路由懒加载,则能够在用户导航到某个特定路由时再进行相关组件的加载,从而实现按需加载,减少初始加载时间。

在React中,可以使用React.lazy和Suspense来实现路由懒加载。React.lazy是一个高级组件,用于动态加载组件。Suspense是一个包裹在懒加载组件周围的组件,用于在组件加载过程中显示加载状态。

以下是一个示例,展示了如何在React中实现路由懒加载:

import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

// 懒加载组件
const Home = lazy(() => import('./components/Home'));
const About = lazy(() => import('./components/About'));
const Contact = lazy(() => import('./components/Contact'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
        </Switch>
      </Suspense>
    </Router>
  );
}

export default App;

在上面的示例中,我们使用React.lazy动态加载了Home、About和Contact组件。在Router组件中,我们使用Suspense组件将这些懒加载组件进行包裹,并通过fallback属性指定了一个加载状态的组件,当懒加载组件加载时会显示该加载状态。

在路由配置中,我们通过Route组件指定了不同路径下对应的懒加载组件。

这样,当用户导航到某个路由时,相关的懒加载组件会在需要时进行加载和渲染,而不是在初始加载时一次性加载所有组件,从而实现了按需加载,优化了应用的加载性能。

9. 你项目中做了图片懒加载,怎么实现的?

前端中的图片懒加载是一种优化技术,它延迟加载页面上的图片,直到用户滚动到可见区域才进行加载。这样可以减少初始页面加载时的网络请求和资源消耗,提升页面加载速度和用户体验。

图片懒加载的作用是避免在初始加载时同时请求和加载大量的图片资源,特别是当页面包含很多图片或图片较大时,可以显著减少初始加载时间和带宽消耗。只有当用户滚动到图片所在的可见区域时,才加载对应的图片,这样可以实现按需加载,优化页面性能。

以下是一个基于Intersection Observer API的图片懒加载示例,使用JavaScript实现:

<!DOCTYPE html>
<html>
  <head>
    <style>
      .image-container {
        height: 400px;
        overflow-y: scroll;
      }
      .image-item {
        height: 200px;
        margin-bottom: 20px;
        background-color: #eee;
      }
      .lazy-image {
        opacity: 0;
        transition: opacity 0.3s;
      }
      .lazy-image.loaded {
        opacity: 1;
      }
    </style>
  </head>
  <body>
    <div class="image-container">
      <div class="image-item">
        <img
          class="lazy-image"
          data-src="path_to_image1.jpg"
          alt="Image 1"
        />
      </div>
      <div class="image-item">
        <img
          class="lazy-image"
          data-src="path_to_image2.jpg"
          alt="Image 2"
        />
      </div>
      <!-- 更多图片项 -->
    </div>

    <script>
      const lazyImages = document.querySelectorAll('.lazy-image');

      // 创建 Intersection Observer 实例
      const observer = new IntersectionObserver(
        (entries, observer) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              // 加载图片
              const image = entry.target;
              image.src = image.dataset.src;
              image.classList.add('loaded');

              // 停止观察已加载的图片
              observer.unobserve(image);
            }
          });
        },
        {
          rootMargin: '0px 0px 100px 0px', // 观察区域的边距
        }
      );

      // 开始观察每个懒加载图片
      lazyImages.forEach((image) => {
        observer.observe(image);
      });
    </script>
  </body>
</html>

在上述示例中,首先使用Intersection Observer API创建了一个观察器(observer),该观察器会观察特定的图片元素是否进入可见区域。当图片元素进入可见区域(isIntersecting为true)时,将加载对应的图片资源,并添加loaded类以显示图片。

在HTML中,每个懒加载图片使用data-src属性来指定待加载的图片路径,而src属性保留为空。当图片进入可见区域时,通过JavaScript将data-src的值赋给src属性,从而加载图片。

通过这种方式,图片在用户滚动到可见区域时才会加载,从而提升页面加载性能和用户体验。

10. 图片懒加载是什么时候开始加载的?

图片懒加载是在满足特定条件时开始加载的。通常情况下,这个条件是当图片进入用户的可见区域时开始加载。

具体来说,当用户滚动页面时,浏览器会检测页面中的图片是否进入了可见区域(即视口)。一旦图片进入可见区域,就会触发加载动作,开始下载图片资源。

这种延迟加载的策略可以避免在初始加载时同时请求和加载大量的图片资源,从而减少页面的初始加载时间和带宽消耗。只有当用户滚动到图片所在的可见区域时,才会进行图片的加载,这样可以实现按需加载,优化页面性能。

通过使用Intersection Observer API或其他监听滚动事件的方式,可以在图片进入用户可见区域时触发加载动作。这样可以确保页面上仅加载用户实际可见的图片,避免了不必要的资源消耗。

11. 你说下express和koa的区别?

Express和Koa都是Node.js的Web应用程序框架,用于简化和加速构建Web应用程序的过程。它们在某些方面有一些相似之处,但也有一些明显的区别。

Express框架:

介绍:

Express是一个简洁、灵活的Web应用程序框架,是Node.js社区最受欢迎的框架之一。它建立在Node.js的核心HTTP模块之上,提供了用于处理HTTP请求、路由、中间件等功能的简单而直观的API。

特点:

  • 简单易用:Express提供了直观的API和中间件机制,使得构建Web应用程序变得简单且易于理解。
    灵活性:Express提供了足够的灵活性,允许开发人员根据项目的需要进行自定义和扩展。

  • 社区活跃:Express拥有庞大的开发者社区,因此有很多插件和中间件可以用于增强应用程序的功能。

示例代码:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello, Express!');
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

Koa框架:

介绍:

Koa是一个新一代的Web应用程序框架,由Express的原作者开发。它旨在提供更简洁、更优雅的编程体验,基于ES6的生成器(Generators)和异步函数(async/await)等特性,使异步流程控制变得更加简单。洋葱模型的特点使其更加灵活。

特点:

  • 异步流程控制:Koa利用ES6的生成器和异步函数,以更直观和优雅的方式处理异步操作和中间件流程控制,避免了回调地狱。

  • 更小的核心:相比Express,Koa的核心更小,提供了更少的功能,更多的功能可以通过中间件来扩展。

  • 更好的错误处理:Koa提供了内置的错误处理机制,使得错误处理更加简单和一致。

示例代码:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx) => {
  ctx.body = 'Hello, Koa!';
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

异同点:

1.异步流程控制:

Koa在处理异步流程控制方面更加强大和直观,通过ES6的生成器和异步函数实现了更好的异步代码编写体验。而Express则依赖于回调函数和Promise来处理异步操作。

2.中间件:
Express和Koa都支持中间件,但Koa的中间件使用起来更加简单和灵活,而且Koa的中间件可以利用ES6的生成器和异步函数来处理异步操作。

3.核心功能:

Express提供了更丰富的功能和API,更适合构建大型应用程序,而Koa的核心更小,提供了更少的功能,更多的功能可以通过中间件来扩展,适合构建轻量级和更加灵活的应用程序。

4.社区生态:

由于Express的历史更长,因此它拥有更大的开发者社区和更丰富的插件生态系统,可以找到大量的第三方中间件和扩展。而Koa相对较新,虽然社区也活跃,但可用的扩展和中间件相对较少。

选择使用Express还是Koa取决于项目的需求和个人偏好。如果你更喜欢简洁、直观的API,并希望处理异步流程更加方便,那么可以选择Koa。如果你需要更多的功能和更丰富的插件生态系统,或者对于传统的回调和Promise编程模型更熟悉,那么可以选择Express。

12. 后端怎么实现的跨域?除了用koa-cors这个包?你说下具体实现?

1.使用cors中间件

在Node.js后端中,可以通过设置HTTP响应头来实现跨域请求。下面是一种常见的实现方式:

安装cors模块:

npm install cors

在应用程序中使用cors中间件:

const express = require('express');
const cors = require('cors');

const app = express();

// 使用cors中间件
app.use(cors());

// 其他路由和中间件

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

2.手动设置响应头

通过使用cors中间件,服务器将在响应中包含一组适当的跨域请求头,允许来自其他域的请求访问你的API。这样可以解决跨域请求的问题。

除了使用cors模块,你也可以手动设置响应头来实现跨域请求。以下是一个示例:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  // 设置允许跨域访问的域名
  res.setHeader('Access-Control-Allow-Origin', 'http://example.com');

  // 设置允许的请求方法
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');

  // 设置允许的请求头
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  // 允许发送身份验证凭据(如cookies)
  res.setHeader('Access-Control-Allow-Credentials', true);

  // 继续处理请求
  next();
});

// 其他路由和中间件

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

在上述示例中,我们使用res.setHeader()来设置跨域请求所需的响应头。其中包括Access-Control-Allow-Origin用于指定允许访问的域名,Access-Control-Allow-Methods用于指定允许的请求方法,Access-Control-Allow-Headers用于指定允许的请求头,以及Access-Control-Allow-Credentials用于允许发送身份验证凭据。

需要注意的是,跨域请求可能存在安全风险,因此在设置跨域访问时应谨慎,确保只允许必要的域名和请求进行访问。

3.使用代理服务器:

另一种常见的方法是在前端使用代理服务器来转发请求。这种方法在开发环境中特别常见,因为前端通常运行在不同的端口或域上,而代理服务器可以在后端与前端之间充当中间层。

具体步骤如下:

1.在你的Node.js后端中启动一个服务器,监听某个端口(例如:3000)。
2.在你的前端开发环境中,配置代理服务器,将所有请求转发到后端服务器。
3.前端通过代理服务器发送请求,而代理服务器会将请求转发到后端服务器,并将响应返回给前端。

这种方式的好处是可以避免跨域请求的问题,因为前端请求实际上是在同一域下发送的(即代理服务器所在的域),而后端与代理服务器之间的通信是在同一域下进行的。

以下是一个使用http-proxy-middleware库配置代理服务器的示例:

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 配置代理服务器
app.use(
  '/api',
  createProxyMiddleware({
    target: 'http://backend-server:3000', // 后端服务器的地址
    changeOrigin: true,
  })
);

app.listen(8000, () => {
  console.log('Proxy server started on port 8000');
});

在上述示例中,我们配置了一个代理服务器,将所有以/api开头的请求转发到后端服务器(在target中指定的地址)。这样,前端可以通过发送请求到代理服务器上的/api路径来与后端进行通信,而代理服务器会负责将请求转发到后端服务器,并将响应返回给前端。

需要注意的是,代理服务器只是在开发环境中使用的一种解决方案,当部署到生产环境时,应该使用其他方法来处理跨域请求,比如使用cors中间件或配置反向代理服务器。

4.Nginx反向代理

1.安装和启动Nginx。

2.打开Nginx的配置文件(通常是nginx.conf或位于/etc/nginx目录下的文件)。

3.在配置文件中添加一个新的服务器块来处理反向代理和跨域请求:

server {
  listen 80;
  server_name your-domain.com; // 替换为你的域名

  location /api {
    proxy_pass http://backend-server:3000; // 替换为你的后端服务器地址和端口
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 解决跨域请求
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
  }
}

在上述配置中,我们创建了一个新的服务器块,并使用listen指令指定监听的端口和服务器名。然后,我们使用location指令来匹配以/api开头的请求,并使用proxy_pass将请求转发到后端服务器。接下来,我们使用proxy_set_header指令来设置请求头,以便在后端服务器中正确获取客户端的真实IP和其他信息。

为了解决跨域请求,我们使用add_header指令添加了一些额外的响应头。上述示例中的配置是允许所有域的请求访问,如果你希望限制跨域请求的来源,可以根据需要修改Access-Control-Allow-Origin的值。

保存配置文件并重新启动Nginx服务。

通过以上配置,当你在前端应用中发送请求到Nginx服务器上的/api路径时,Nginx将会将请求转发到指定的后端服务器,并在响应中添加跨域请求所需的响应头,从而解决跨域问题。

需要注意的是,使用Nginx进行反向代理还有其他许多配置选项和功能,上述示例仅展示了一个基本的配置用于跨域请求。在实际部署中,你可能需要根据你的需求进行更详细的配置,比如处理SSL证书、缓存设置等。请参考Nginx的官方文档以获取更多信息和配置选项。

5.中间层应用(页面渲染、接口代理)

13. 你说下css的flex布局api? 你项目怎么使用的?

CSS中的Flex布局(弹性布局)提供了一种灵活的方式来进行元素的布局和对齐。以下是Flex布局中常用的属性和值的介绍:

1.display: flex;

用于定义一个容器元素为Flex容器,使其内部的子元素成为Flex项目。

2.flex-direction: row | row-reverse | column | column-reverse;

定义Flex项目的主轴方向。

  • row(默认值):水平方向,从左到右排列。
  • row-reverse:水平方向,从右到左排列。
  • column:垂直方向,从上到下排列。
  • column-reverse:垂直方向,从下到上排列。

3.justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;

定义Flex项目在主轴(flex-direction,默认水平)上的对齐方式。

  • flex-start(默认值):左对齐(水平方向)或上对齐(垂直方向)。

  • flex-end:右对齐(水平方向)或下对齐(垂直方向)。

  • center:居中对齐。

  • space-between:两端对齐,项目之间的间距相等。

  • space-around:项目周围均匀分布的间距。

  • space-evenly:项目之间和周围均匀分布的间距。

4.align-items: flex-start | flex-end | center | baseline | stretch;

定义Flex项目在交叉轴上的对齐方式。

  • flex-start:顶部对齐(水平方向)或左对齐(垂直方向)。

  • flex-end:底部对齐(水平方向)或右对齐(垂直方向)。

  • center:居中对齐。

  • baseline:基线对齐。

  • stretch(默认值):如果项目未设置固定高度或宽度,则拉伸以填充容器。

5.align-self: auto | flex-start | flex-end | center | baseline | stretch;

定义单个Flex项目在交叉轴上的对齐方式,覆盖align-items的设置。
属性值与align-items相同。

6.justify-self: auto | flex-start | flex-end | center | baseline | stretch;

定义单个Flex项目在主轴上的对齐方式,覆盖justify-content的设置。

  • auto(默认值):继承父容器的对齐方式。

  • flex-start:左对齐(水平方向)或上对齐(垂直方向)。

  • flex-end:右对齐(水平方向)或下对齐(垂直方向)。

  • center:居中对齐。

  • baseline:基线对齐。

  • stretch:拉伸以填充剩余空间。

请注意,justify-self属性是在Flex容器中针对单个Flex项目设置对齐方式。它可以覆盖Flex容器上的justify-content属性,使单个项目具有不同的对齐方式。这是一个比较新的属性,目前在一些浏览器中可能还不被完全支持,所以在实际应用中需要谨慎使用。

7.flex-wrap: nowrap | wrap | wrap-reverse;

定义Flex项目的换行方式。

  • nowrap(默认值):不换行,所有项目在一行显示。

  • wrap:换行,根据容器的宽度自动换行。

  • wrap-reverse:反向换行。

8.align-content: flex-start | flex-end | center | space-between | space-around | stretch;

定义多行Flex项目在交叉轴上的对齐方式,仅在有多行时生效。

  • flex-start:顶部对齐(水平方向)或左对齐(垂直方向)。

  • flex-end:底部对齐(水平方向)或右对齐(垂直方向)。

  • center:居中对齐。

  • space-between:两端对齐,行之间的间距相等。

  • space-around:行周围均匀分布的间距。

  • stretch(默认值):如果项目未设置固定高度或宽度,则拉伸以填充交叉轴。

这些属性和值组合在一起,可以实现弹性的布局和对齐,使得元素在不同屏幕尺寸和布局环境下都能自适应地显示和排列。

14. 盒模型?你为什么常用ie盒模型?

在前端开发中,有两种常见的盒模型:标准盒模型(W3C盒模型)和IE盒模型。

1.标准盒模型(W3C盒模型):

盒子总宽度 = content width + padding + border + margin。
在标准盒模型中,元素的宽度和高度仅包括内容区域的宽度和高度,不包括填充、边框和外边距

2.IE盒模型:

盒子总宽度 = content width + padding + border。
在IE盒模型中,元素的宽度和高度包括内容区域、填充和边框的宽度,但不包括外边距
为什么常用IE盒模型?

在早期的Web开发中,IE浏览器采用了IE盒模型作为默认的盒模型,而其他浏览器采用了标准盒模型。由于当时的IE浏览器的市场份额较大,许多开发者习惯于使用IE盒模型进行布局和样式设计。为了保持兼容性并使网页在不同浏览器中呈现一致的效果,一些开发者仍然选择使用IE盒模型。

然而,随着时间的推移和标准的普及,现代的Web开发中更常用标准盒模型。标准盒模型更符合直觉,并且在布局和样式设计上更加灵活。而之所以使用IE盒模型,是因为更方便计算宽高。

通过使用CSS的box-sizing属性,我们可以明确指定元素使用哪种盒模型(content-box表示标准盒模型,border-box表示IE盒模型)。

示例代码:

/* 使用标准盒模型 */
.box {
  box-sizing: content-box;
  width: 200px; /* 仅包括内容宽度 */
  padding: 20px;
  border: 1px solid #000;
  margin: 10px;
}

/* 使用IE盒模型 */
.box {
  box-sizing: border-box;
  width: 200px; /* 包括内容宽度、填充和边框 */
  padding: 20px;
  border: 1px solid #000;
  margin: 10px;
}

在上述示例中,.box类分别使用了标准盒模型和IE盒模型。通过设置box-sizing属性为content-box或border-box,我们可以明确指定使用哪种盒模型,并相应地定义元素的宽度、填充、边框和外边距。

15. 如果是一个弹性盒子,其中一个子元素为flex为1,请问另一个子元素的宽度?

在一个弹性盒子容器中,如果其中一个子元素设置了flex: 1;,表示该子元素的伸缩比例为1。这意味着它会占据剩余空间的比例,并且会自动调整宽度。

假设有一个弹性盒子容器,并且其中有两个子元素,一个设置了flex: 1;,另一个没有设置flex属性。那么没有设置flex属性的子元素的宽度将会根据其内容进行计算,而具有flex: 1;的子元素将会自动占据剩余的空间。

这里是一个示例代码:

<div class="container">
  <div class="child child-1">Flex 1</div>
  <div class="child child-2">Content-based</div>
</div>
css
Copy code
.container {
  display: flex;
}

.child {
  border: 1px solid #000;
  padding: 10px;
}

.child-1 {
  flex: 1;
}

.child-2 {
  /* 没有设置flex属性,默认根据内容计算宽度 */
}

在上述示例中,.child-1具有flex: 1;,它将占据剩余的空间。.child-2没有设置flex属性,它的宽度将根据内容进行计算。

如果.child-1的内容较多,.child-2的宽度将根据其内容进行调整以适应剩余空间。如果.child-1的内容很少,.child-2的宽度将根据其内容自动扩展。

因此,.child-1的宽度是可变的,而.child-2的宽度是根据内容计算的,会根据剩余空间的大小进行调整。

16. 怎么实现一个简单的导航栏?

17. 怎么实现一个三栏布局?

公共html部分:

function ThreeColum() {
  return (
    <div className="threecolum">
      <p>1.使用float实现</p>
      <div className='m1-wrapper'>
        <div className='m1-public m1-left'> </div>
        <div className='m1-public m1-center'></div>
        <div className='m1-public m1-right'> </div>
      </div>
      <div className='three-footer'>footer</div>
      <div className='three-footer2'>footer2</div>
    </div>
  );
}

公共css部分:

.three-footer {
  height: 30px;
  border: 1px solid red;
  text-align: center;
  /* clear: both; */
}
.three-footer2 {
  height: 30px;
  border: 1px solid red;
  text-align: center;
}

1.使用float实现

/* 方法一:
使用float实现,
*/
.m1-wrapper {
  border: 1px solid royalblue;
}
.m1-wrapper::after {
  content: '';
  display: table;
  clear: both;
}
.m1-public {
  float: left;
  width: 30%;
  margin: 0 1.5%;
}
.m1-left {
  border: 1px solid black;
  height: 300px;
}

.m1-center {
  border: 1px solid red;
  height: 400px;
}

.m1-right {
  border: 1px solid green;
  height: 100px;
}

给三列均设置浮动,实现三列布局。
但是要注意,三列设置浮动后,其父元素无法正确计算高度,也就是外部包裹组件的高度并没有被撑开。所以底部的footer元素位置不对。
因此,需要让footer及footer下方元素,出现在三列内容的下方。
这里提供两种方案:

1.修改footer元素css:

clear:both;

clear:both;的作用是,清除元素浮动带来的影响,恢复正常布局位置。可以将其放在浮动元素的下方,清除浮动带来的影响。表现为其左右均不能有浮动元素,如果有,则布局在浮动元素的最下方。

这个方式可以使footer元素位置正常,其后footer2元素位置正常。但是,并没有撑开三列元素的父元素,其高度仍然为0。

所以选择另一种方案。

2.修改三列的父元素m1-wrapper的css:
(1)使用伪元素

.m1-wrapper::after {
  content: '';
  display: table;
  clear: both;
}

(2)块级格式化上下文BFC

.m1-wrapper {
  border: 1px solid royalblue;
  overflow: hidden;
}

BFC在以下情况会被创建:

  • 根元素(即 元素)
  • 浮动元素(float 不为 none)
  • 绝对定位元素(position 为 absolute 或 fixed)
  • 行内块元素(display 为 inline-block)
  • 表格单元格(display 为 table-cell)
  • 表格标题(display 为 table-caption)
  • overflow 属性值不为 visible 的块级元素

2. 使用flexbox布局

.m2-wrapper {
 display: flex;
}

.m2-left {
  border: 1px solid black;
  height: 300px;
  width: 200px;

}

.m2-center {
  border: 1px solid red;
  height: 400px;
  flex: 1;
}

.m2-right {
  border: 1px solid green;
  height: 100px;
  width: 200px;
}

3.使用grid网格布局

/* 使用grid */
.m3-wrapper {
  display: grid;
  width: 100%;
  grid-template-columns: 200px auto 300px;
 }
 
 .m3-left {
  /*grid-column: 1/2;*/
   border: 1px solid black;
   height: 300px;
 
 }
 
 .m3-center {
   border: 1px solid red;
   height: 400px;
 }
 
 .m3-right {
   border: 1px solid green;
   height: 100px;
 }

4.absolute实现

.m4-wrapper {
 width: 100%;
 position: relative;
}

.m4-left {
  position: absolute;
  width: 200px;
  top: 0;
  left: 0;
}

.m4-center {
  margin: 0 300px 0 200px;
}

.m4-right {
  position: absolute;
  width: 200px;
  top: 0;
  right: 0;
}

这里,要理解absolute绝对定位,是针对最近的已定位的父元素(position不为static)定位的。所以此处要给父元素设置position: relative。通过设置top,left,top,bottom指定位置,该方式会使元素从文档中脱离,其他元素布局不会受到其影响。

5.使用table实现

 /* 5.使用table */
 .m5-wrapper {
  width: 100%;
  display: table;
 }
 
 .m5-left {
   display: table-cell;
   width: 200px;
 }
 .m5-center {
   display: table-cell;
 }
 .m5-right {
   display: table-cell;
   width: 300px;
 }

此处子元素的高度,和父元素的高度,均等于子元素的最高高度。

6. 圣杯布局

html:

<p>6.圣杯布局实现</p>
<div className='m6-wrapper'>
  <div className='m6-center'>center</div>
  <div className='m6-left'>left </div>
  <div className='m6-right'>right </div>
</div>
<div className='three-footer'>footer</div>
<div className='three-footer2'>footer2</div>

css代码

/* 6.圣杯布局 */
.m6-wrapper {
  box-sizing: border-box;
  padding: 0 200px;
  overflow: hidden;
} 

.m6-center {
  float: left;
  width: 100%;
}
.m6-left {
  width: 200px;
  float: left;
  position: relative;
  margin-left: -100%;
  left: -200px;
}

.m6-right {
  width: 200px;
  float: left;
  position: relative;
  margin-left: -200px;
  right: -200px;
}

在html中,中间center部分,写在left和right前面,确保主体部分先加载。

7.双飞翼布局

html:

<div className='m7-wrapper'>
     <div className='m7-center-wrapper'>
          <div className='m7-center'>center</div>
     </div>
     <div className='m7-left'>left </div>
     <div className='m7-right'>right </div>
</div>
/* 7.圣杯布局 */
.m7-wrapper {
box-sizing: border-box;
width: 100%;
overflow: hidden;
} 
.m7-center-wrapper {
float: left;
width: 100%;
}
.m7-center {
height: 400px;
margin: 0 200px;

}
.m7-left {
width: 200px;
float: left;
margin-left: -100%;
}

.m7-right {
width: 200px;
float: left;
margin-left: -200px;
}

双飞翼布局与圣杯布局,都是实现的三列布局,两侧固定宽度,中间主体部分自适应调整,且主体内容先行加载。但是不同的是,圣杯布局是通过padding的方式,为主体部分留出位置。而双飞翼布局是通过margin的方式,留出主体位置。并且双飞翼布局主体部分多了一个div,通过div嵌套,结合margin边距,使得两侧边栏的定位更加简单。不需要通过relative布局进行第二次移动。

18.你说下怎么判断一个对象是否等于另一个对象的?(做了一道编程题)

在 JavaScript 中,判断两个对象是否相等有两种常见的方法:引用相等(Reference Equality)和值相等(Value Equality)。

1.引用相等(Reference Equality):

引用相等是指判断两个对象是否是同一个引用,即它们指向内存中的同一个对象。可以使用全等运算符(===)来比较两个对象的引用是否相等。

const obj1 = { name: 'John' };
const obj2 = { name: 'John' };

console.log(obj1 === obj1);  // true
console.log(obj1 === obj2);  // false

在上面的示例中,obj1和obj2虽然具有相同的属性和属性值,但它们是不同的对象,因此引用相等判断结果为false。

2.值相等(Value Equality):

值相等是指判断两个对象的属性和属性值是否完全相同。可以使用深度比较的方法来判断两个对象的属性是否相等,例如使用递归遍历对象的属性进行比较,或stringify(),或使用库函数如Lodash的isEqual方法。

(1)stingify().:

const obj1 = { name: 'John', age: 25 };
const obj2 = { name: 'John', age: 25 };

function deepEqual(obj1, obj2) {

  // 示例中简化为浅比较
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}

console.log(deepEqual(obj1, obj1));  // true
console.log(deepEqual(obj1, obj2));  // true

在上面的示例中,通过比较对象的属性值是否相等,可以得出对象的值相等判断结果为true,但是如果对象属性中有函数等情况,会比较不出来。

(2)递归比较

当需要使用递归来深度比较两个对象时,可以编写一个递归函数来遍历对象的属性并进行比较。以下是一个使用递归深度比较两个对象的示例代码:

function deepEqual(obj1, obj2) {
  // 检查对象类型和属性数量
  if (typeof obj1 !== 'object' || obj1 === null ||
      typeof obj2 !== 'object' || obj2 === null) {
    return obj1 === obj2;
  }

  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  // 递归比较对象的属性
  for (let key of keys1) {
    if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
      return false;
    }
  }

  return true;
}

// 示例使用
const obj1 = {
  name: 'John',
  age: 25,
  address: {
    street: '123 Main St',
    city: 'New York'
  }
};

const obj2 = {
  name: 'John',
  age: 25,
  address: {
    street: '123 Main St',
    city: 'New York'
  }
};

console.log(deepEqual(obj1, obj1));  // true
console.log(deepEqual(obj1, obj2));  // true

在上述示例中,deepEqual函数会首先检查两个对象的类型和属性数量。如果类型不是对象或者属性数量不同,直接比较两个对象的引用。然后,通过递归遍历对象的属性,并使用递归调用deepEqual函数进行比较。如果有任何属性不相等,或者属性在一个对象中存在而在另一个对象中不存在,函数将返回false。如果所有属性都相等,则返回true。

请注意,这只是一个简单的示例,用于说明递归深度比较的概念。在实际应用中,可能需要处理更多边界情况和特殊类型的对象。对于更复杂的对象比较,可能需要使用更强大的库函数或编写更复杂的逻辑来满足需求。

19. 你说你Object.prototype.toString.call()判断一个数组,打印什么?

Object.prototype.toString.call()是一种常用的方法,用于获取一个值的具体类型信息。它的作用是返回一个表示该值类型的字符串。

该方法的基本语法如下:

Object.prototype.toString.call(value)

其中,value是要获取类型信息的值。

使用Object.prototype.toString.call()的主要目的是解决typeof操作符的一些局限性。typeof操作符通常可以用来判断值的类型,但它对于一些特殊类型的值,如数组、日期对象、正则表达式对象等,会返回"object",无法提供更具体的类型信息。而使用Object.prototype.toString.call()可以获取更准确的类型信息。

下面是一些常见值类型的Object.prototype.toString.call()返回值示例:

Object.prototype.toString.call("Hello") 返回 "[object String]"
Object.prototype.toString.call(123) 返回 "[object Number]"
Object.prototype.toString.call(true) 返回 "[object Boolean]"
Object.prototype.toString.call(null) 返回 "[object Null]"
Object.prototype.toString.call(undefined) 返回 "[object Undefined]"
Object.prototype.toString.call([]) 返回 "[object Array]"
Object.prototype.toString.call({}) 返回 "[object Object]"
Object.prototype.toString.call(new Date()) 返回 "[object Date]"
Object.prototype.toString.call(/regex/) 返回 "[object RegExp]"

通过使用Object.prototype.toString.call(),我们可以更准确地获取一个值的类型信息,以便进行相应的处理和判断。

20. 你说下箭头函数和普通函数的区别?

1.写法与简写

箭头函数使用箭头(=>)来定义,而普通函数使用关键字 function 定义。

// 箭头函数
const arrowFunc = (arg1, arg2) => {
  // 函数体
};

// 普通函数
function regularFunc(arg1, arg2) {
  // 函数体
}
/// 箭头函数省略
const arrowFunc2 = a => a + 1;

箭头函数如果只有一个参数,则小括号可以省略。如果执行部分只有一个返回值,则大括号和return都可以省略。普通函数不能省略。

2.构造函数

箭头函数不能作为构造函数使用,而普通函数可以作为构造函数来创建对象实例。
箭头函数没有自己的 new.target,并且不能使用 new 关键字调用

// 普通函数作为构造函数
function Person(name) {
  this.name = name;
}

const john = new Person('John');
console.log(john.name); // 输出 "John"

// 箭头函数不能作为构造函数
const Person = name => {
  this.name = name;
};

const john = new Person('John'); // 抛出 TypeError

3.arguments对象

每个普通函数调用后都有一个arguments对象,用来存储实际传递的参数。

const a = [1,2,3,4];
const f = function(a){
	console.log(arguments);
}
// output:
// [1,2,3,4]

箭头函数没有自己的arguments,但是可以使用reset。

const f = ( ...reset) => {
	console.log(reset);
}

f(1, 2, 3, 4);
//output:
// [1, 2, 3, 4]
const f = (param1, param2, ...reset) => {
	console.log(reset);
}

f(1, 2, 3, 4);
//output:
// [3, 4]

4.prototype原型

普通函数具有prototype原型,而箭头函数没有。从以下几个方面理解

(1)构造函数和原型链:

在 JavaScript 中,普通函数可以用作构造函数来创建对象实例,并且每个普通函数都有一个默认的 prototype 属性,它指向一个对象,该对象包含构造函数的原型方法和属性。通过原型链,对象可以继承构造函数的原型方法和属性。然而,箭头函数没有自己的 prototype,因为它们不可以用作构造函数,无法创建对象实例。

(2)继承关系:

由于普通函数有 prototype,它们可以作为原型对象,用于继承属性和方法。通过 prototype,可以将方法和属性添加到构造函数的原型上,从而使通过该构造函数创建的所有实例都能够访问这些方法和属性。而箭头函数没有 prototype,因此不能用于创建继承关系。

(3)对象的 proto 属性:

在 JavaScript 中,每个对象都有一个特殊的 proto 属性,指向其原型对象。普通函数的 prototype 对象被赋值给通过该构造函数创建的对象的 proto 属性,从而建立了原型链。但是,箭头函数没有 prototype,因此没有对应的原型对象可以赋值给 proto

综上所述,

箭头函数没有 prototype 原型是因为它们不能用作构造函数创建对象实例,也没有继承关系,无法作为原型对象。它们更适合用于简洁的函数表达式,而普通函数则更适合用于需要创建对象实例和实现继承的情况。

5.this指向

普通函数中this总是指向调用它的对象,如果作为构造函数,它指向创建的对象实例。

箭头函数的this指向的是父级作用域的this,是通过查找作用域链来确定 this 的值,它本身没有this。也就是说看的是上下文的this,指向的是定义它的对象,若无自定义上层,则代表window,而不是执行时所在的对象。

6.this指向改变

call()、apply()、bind()等方法能改变普通函数中this的指向,但不能改变箭头函数中this点指向。

21. 一道this指向的题目。

(后悔没保存下来,面试官临时改了多次题目,每改一次叫我回答一次)(函数里面return length++和return ++length区别,还有一些问题需要看代码)

22. (算法题)合并数组两个有序数组的题目?那用sort的时间复杂度?

(n<10,插排,n>=10三路快排)你再写过一直实现方式?(当时我写的双指针)

题目摘录:
作者:一世长安_学
链接:https://ac.nowcoder.com/discuss/867430?type=2
来源:牛客网

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值