百度商业化面试

大家好,我是Range,今天带来一篇社招面经,由团队 Uncle13老师整理,已获取学员授权发布。

学员工作一年半,本科,计算机专业。当时毕业的时候错过了校招,在一个三流的小公司搬砖一年多。9月份找到我们,想实现大厂梦,准备了两个月,参加了百度的社招。技术面试已经通过,给到T4。前两面面试比较基础,注重coding能力,第三面让设计一个简单的购物车,更注重考察项目能力。下面将面经总结一下,供接下来找工作的小伙伴参考一下~

一面

  1. webpack 中 chunkHash 与 contentHash 区别:

    • chunkHash 是根据 chunk 的内容而生成的哈希值,当 chunk 的内容发生变化时,该哈希值也会改变。通常用于在文件名中添加哈希以实现浏览器缓存的更新。
    • contentHash 是根据文件的内容而生成的哈希值,当文件的内容发生变化时,该哈希值也会改变。通常用于将哈希值作为静态资源的文件名,以保证只有内容发生变化时才会重新请求新的资源。
  2. 写过 webpack 的 loader 和 plugin 么,讲一下:

Loader:Loader 是用于对特定类型的文件进行转换和处理的函数。它们作为模块的中间件,将源文件作为输入,并输出转换后的模块代码。Webpack 的核心原则是一切皆模块,因此 loader 主要用于处理非 JavaScript 文件(如 CSS、图像、字体等)。

Loader 可以在 module.rules 字段中配置,每个规则包含一个 test 属性来匹配需要转换的文件类型,以及一个 use 属性指定使用哪些 loader 进行转换。常见的 loader 有:

  • babel-loader:用于将 ES6+ 语法转换为兼容的 JavaScript 代码。
  • css-loader:用于解析处理 CSS 文件,处理其中的依赖关系并加载 CSS 到页面中。
  • file-loader:将文件复制到输出目录,并返回文件的路径。
  • style-loader:将 CSS 代码注入到 HTML 页面中。

Plugin:Plugin 是用于扩展 Webpack 功能的插件,它可以在整个构建过程中执行自定义任务,包括优化资源、修改输出文件、注入环境变量等。与 loader 不同,plugin 在整个构建过程中通过钩子函数(hooks)与 Webpack 进行交互。

Plugin 可以通过实例化一个类来创建,并在 Webpack 配置中的 plugins 字段中进行配置。常见的 plugin 有:

  • HtmlWebpackPlugin:生成 HTML 文件,并自动将打包后的资源文件(如 JavaScript、CSS)注入到 HTML 中。
  • MiniCssExtractPlugin:将 CSS 提取为独立的文件,而不是将其嵌入到 JS bundle 中。
  • CleanWebpackPlugin:在每次构建前清理输出目录,以避免旧文件残留。

Loader 和 Plugin 的区别:

  • Loader 用于处理特定类型的文件,在模块解析的过程中转换源代码。它们作用于特定的文件模块,以支持对不同类型资源的处理和转换。
  • Plugin 是对 Webpack 整个构建过程的扩展,通过钩子函数与 Webpack 进行交互,实现优化、资源管理、环境配置等功能。它们作用于构建过程中的某个特定阶段,以实现额外的任务和功能。
  1. webpack 处理 image 是用哪个 loader,限制成 image 大小的是哪个参数:

    在 webpack 中,可以使用 file-loader 或者 url-loader 来处理图片。file-loader 将图片复制到输出目录,并返回图片的 URL;而 url-loader 可以根据指定的大小阈值将图片转换为 base64 编码或者继续使用 file-loader 进行处理。若要限制图片的大小,可以在 url-loader 的配置中使用 limit 参数。该参数指定一个阈值,当图片大小小于等于该阈值时,会将图片转换为 base64 编码,否则会使用 file-loader 进行处理。

  2. webpack 如何将多个 css 合并成一个:

    要将多个 CSS 文件合并成一个,可以使用 mini-css-extract-plugin 插件。该插件会将每个 CSS 文件转换为独立的文件,并通过 <link> 标签将它们插入 HTML 页面中。通过配置插件的 filename 选项,可以指定输出的合并后的 CSS 文件名。

  3. webpack 的摇树对 CommonJS 和 ES6 Module 都生效么,原因是什么?:

    • Webpack 的 Tree Shaking 只能对 ES6 Module 进行优化,而对于 CommonJS 模块则不能进行有效的摇树优化。
    • 原因在于 ES6 Module 是静态导入和导出的,而 CommonJS 模块是动态加载的。Webpack 在打包过程中,可以通过静态分析 ES6 Module 的依赖关系,从而确定哪些未被使用的代码可以被剔除。但对于 CommonJS 模块,由于其动态加载特性,Webpack 无法在编译时确定运行时导入的模块,因此无法进行摇树优化。
  4. 简单实现一下「模版字符串」功能:

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.
  1. 实现一下 Promise.all (Promise 不用写):
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, 1002));
const promise3 = Promise.reject('Error');
  
promiseAll([promise1, promise2])
  .then((results) => {
    console.log(results); // Output: [1, 2]
  })
  .catch((error) => {
    console.error(error); // Output: Error
  });
  1. 日常项目中怎么实现响应式布局的?

实现响应式布局的方法有多种,下面是一些常见的方法:

  1. 使用CSS媒体查询:使用CSS的@media规则来根据不同的屏幕宽度应用不同的样式。通过定义不同的CSS规则,可以为不同的屏幕尺寸提供不同的布局和样式。

    /* 默认样式 */
    .container {
      width100%;
      max-width960px;
    }

    /* 媒体查询 */
    @media (max-width: 768px) {
      .container {
        max-width480px;
      }
    }

    在上述示例中,容器的最大宽度在不同的屏幕尺寸下会变化。

  2. 使用栅格系统:流行的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>

    在上述示例中,列的宽度在不同屏幕尺寸下会自动调整。

  3. 使用弹性盒子布局(Flexbox):Flexbox是一种灵活的布局模型,可以根据容器的大小和内容自动调整项目的位置和大小。通过设置适当的Flexbox属性,可以实现响应式布局。

    .container {
      display: flex;
      flex-wrap: wrap;
    }

    .item {
      flex-basis50%/* 每个项目占据50%的宽度 */
    }

    @media (max-width: 768px) {
      .item {
        flex-basis100%/* 将每个项目的宽度改为100% */
      }
    }

在上述示例中,项目的宽度在不同的屏幕尺寸下会自动调整。

  1. 讲一下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 相同。
  2. 解释一下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(同时应用 forwardsbackwards)。
  • animation-play-state:指定动画的播放状态,可选值为 running(默认值,动画正在运行)和 paused(动画暂停)。
  1. CSS 如何实现让一个元素旋转并横向移动,如果只用一个 CSS 属性:
  • 可以使用 animation 属性来实现。通过定义关键帧动画,在不同的关键帧设置元素的旋转角度和水平偏移量,然后通过 animation 属性将动画应用到元素上。
  • 以下是一个示例:
@keyframes rotatingAndMoving {
  0% {
    transformrotate(0degtranslateX(0);
  }
  50% {
    transformrotate(180degtranslateX(100px);
  }
  100% {
    transformrotate(360degtranslateX(0);
  }
}

.element {
  animation: rotatingAndMoving 5s linear infinite;
}

上述代码将元素进行旋转和横向移动,每次旋转一周并向右移动100像素,总共耗时5秒,并无限循环播放。

  1. Less 与 Sass 区别,技术选型时如何取舍?
  • Less 和 Sass 都是 CSS 预处理器,提供了类似编程语言的特性,例如变量、嵌套规则、函数等,可以更方便地编写和维护 CSS。
  • 主要区别在于语法风格不同,Less 使用的是类似 CSS 的语法,而 Sass 则使用了更加严谨和灵活的缩进语法。另外,Sass 还支持 SCSS 语法,它与 Less 的语法相似,使用大括号和分号来定义规则和声明。
  • 在选择时,可以考虑以下因素:
    • 项目需求:如果项目已经使用了某个预处理器,那么继续使用该预处理器可能更为简单和连贯。如果没有限制,则可以根据团队的熟悉程度和个人喜好进行选择。
    • 生态系统:Sass 具有更为成熟和广泛的生态系统,包括大量的第三方库和工具支持。Less 也有一定的生态系统,但相对较小。
    • 工具集成:考虑预处理器是否与所使用的构建工具、编辑器和其他工具集成良好。
    • 学习曲线:如果团队中已经熟悉了某个预处理器,那么继续使用该预处理器可能会减少学习和适应的时间。
  1. ES6 Symbol 如何使用以及使用场景:
  • 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 的私有属性的键。可以通过方括号语法访问该私有属性。

    • 创建对象的私有属性或方法:由于 Symbol 的唯一性,可以用作对象的私有属性或方法的键,以避免与其他属性冲突。
    • 定义常量:使用 Symbol 作为常量的值,可以确保其唯一性,防止被意外修改。
    • 迭代器和生成器:Symbol 可以用作迭代器对象的键,实现自定义的迭代行为。
  1. JavaScript 中如何实现继承:

    • 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
    • 构造函数继承:在子类构造函数中调用父类构造函数,并使用 callapply 方法设置正确的上下文,从而实现继承。子类将继承父类构造函数中的属性和方法。
      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
    • 在选择继承方式时,可以根据项目需求、代码结构和个人偏好来决定使用哪种方式。原型链继承简单易懂,但存在属性共享的问题;构造函数继承解决了属性共享问题,但不能继承父类原型上的方法;组合继承结合了两者的优点,但会调用两次父类构造函数。ES6 的 extendssupersuper` 关键字提供了更加简洁和直观的语法,推荐在使用 ES6 时使用。
  2. React 中函数组件和类组件有什么区别,何时选择使用哪种组件:

    • 函数组件通常更简洁、易于编写和理解。如果组件只需要根据输入的 props 进行渲染,并且不需要管理内部状态或生命周期方法,那么函数组件是一个很好的选择。
    • 类组件适合具有复杂的状态管理、需要进行生命周期操作或处理用户交互的场景。如果组件需要使用组件的生命周期方法,例如 componentDidMountcomponentDidUpdate,或者需要定义事件处理函数来响应用户操作,那么类组件是更合适的选择。
    • 在 React 16.8 版本引入的 Hooks API 中,函数组件也可以使用状态和生命周期方法,以及其他功能,使得函数组件具备了类组件的部分能力。因此,如果函数组件需要管理状态或处理生命周期,可以选择使用 Hooks 来编写函数组件。
    • 函数组件和类组件是 React 中两种常见的组件形式。
    • 函数组件是一个纯粹的 JavaScript 函数,用于接收输入参数(props),并返回一个 React 元素。函数组件没有自己的状态(state)和生命周期方法,只依赖外部传入的数据进行渲染。
    • 类组件是通过继承 React.Component 类创建的组件类。类组件可以拥有自己的状态和生命周期方法,以及响应用户交互的事件处理函数。
    • 何时选择函数组件或类组件取决于具体需求:
    • React 中的 context 是什么,如何使用它传递数据给子组件:

      • 如果没有提供默认值,当没有匹配的 Provider 时,context 的值将为 undefined
      • context 可以在组件树中的任何地方被访问,但应注意避免过度使用 context,以免导致组件之间的紧密耦合。
      • React 中的 context 是一种跨组件层级共享数据的机制。它可以传递数据给组件树中的所有子组件,而无需显式地通过 props 一层层传递。
      • 使用 context 可以避免 props 属性在多个层级组件之间传递的麻烦,特别适合用于共享全局数据、主题样式等情况。
      • 要使用 context,首先需要通过 React.createContext() 方法创建一个 context 对象,并提供一个默认值作为初始值。
        const MyContext = React.createContext(defaultValue);
      • 然后,在父组件中使用 MyContext.Provider 组件包裹子组件,并通过 value 属性传递要共享的数据。
        <MyContext.Provider value={data}>
          // 子组件
        </MyContext.Provider>
      • 在子组件中,可以通过两种方式来获取传递的 context 数据:
      • 注意事项:
        1. 使用 MyContext.Consumer 组件并在其内部使用函数来接收 context 值。
          <MyContext.Consumer>
            {value => (
              // 使用 context 值
            )}
          </MyContext.Consumer>
        2. 使用 useContext 钩子函数(仅适用于函数组件)。
          import React, { useContext } from 'react';

          function MyComponent({
            const value = useContext(MyContext);
            
            // 使用 context 值
          }

      二面

      1. 介绍一下项目:

        • 简要描述项目的背景和目标。
        • 指出在项目中承担的角色和责任。
        • 提及使用的技术栈和框架。
        • 强调在项目中取得的成就和面临的挑战。
        • 最后,总结项目的结果和对你的成长产生的影响。
        • 这个问题需要根据实际参与的项目来回答,没有具体答案。但是,大家可以按照以下方式来介绍你的项目:
        • 在给定的 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;
            }
          • 上述代码通过生成包含 1 到 n 的数组,并随机选取索引位置,将选中的数添加到结果数组中,并从原数组中移除选中的数。重复这个过程 m 次,即可得到等概率的随机数。
        • 手写一下防抖节流函数:

          • 防抖(debounce)函数在事件触发后延迟一定时间执行回调函数,如果在延迟期间再次触发该事件,则重新计时。
          • 节流(throttle)函数在一定时间间隔内多次触发事件,只会执行一次回调函数。
          • 这里给出一个简单的实现:
            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' }}
                   >
                     &#9733;
                   </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 属性设置星星的总数。
        1. 讲一下 HTTP 缓存:

          • 对于不经常更新的静态资源,可以设置较长的缓存时间,以提高性能。
          • 对于经常变动的资源,可以使用协商缓存来验证资源是否已修改。
          • 可以通过设置合适的缓存策略来平衡性能和数据实时性之间的权衡。
          • 减少网络带宽消耗,提高网站性能和加载速度。
          • 降低服务器负载,减少对服务器资源的需求。
          • 提供更好的用户体验,减少页面加载时间和延迟。
          • 强缓存是通过设置响应头来实现的。例如,Cache-Control 头字段用于控制资源在客户端的缓存策略,Expires 头字段指定资源的过期时间。
          • 协商缓存是通过在请求和响应头中使用条件标头来实现的。例如,If-Modified-SinceLast-Modified 标头用于检查资源是否已更改,ETagIf-None-Match 标头可以提供更强大的验证机制。
          • HTTP 缓存是一种机制,用于减少网络请求并提高网站性能。它允许浏览器或代理服务器在一段时间内存储已请求资源的副本,并在下次请求时重用这些副本,而不必再次从服务器获取。
          • HTTP 缓存可以分为两种类型:强缓存和协商缓存。
          • HTTP 缓存的优点:
          • 注意事项:
          • 讲一下call、apply、bind 三者的区别,如何实现 bind:

            • call 方法接受一个或多个参数列表。
              func.call(thisArg, arg1, arg2, ...);
            • apply 方法接受一个数组作为参数。
              func.apply(thisArg, [arg1, arg2, ...]);
            • callapplybind 都是 JavaScript 函数的方法,用于改变函数的上下文(即 this 的值)并立即调用该函数。

            • callapply 的作用类似,都是改变函数的上下文,并传递参数给函数。区别在于传递参数的方式不同:

            • bind 方法与 callapply 不同,它返回一个新的函数,而不是立即调用函数。绑定的函数可以稍后调用,并拥有指定的上下文和参数。

              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 中已经有了更好的替代方案,如箭头函数来解决上下文问题。

            三面

            1. 解释一下闭包,并举一个实际的例子。

              闭包是指内部函数可以访问外部函数作用域中的变量,即使在外部函数执行完毕后仍然保持对这些变量的引用。闭包由函数和定义该函数时创建的词法环境(包含了所有局部变量)组成。

              例子:

              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

            2. 什么是跨域请求?如何解决跨域问题?

              跨域请求是浏览器安全机制所限制的一种情况,当你通过 AJAX、WebSockets 或其他方式在一个域名下请求另一个域名的资源时,浏览器会阻止这种跨域请求。跨域请求通常是从一个源(协议、域名、端口)向另一个源发送 AJAX 请求。

              解决跨域问题的常见方法包括:

              • JSONP:通过动态创建 <script> 标签,利用 HTML 中的 <script> 标签不受同源策略限制,从而实现跨域请求。但 JSONP 只支持 GET 请求,并且需要服务端配合返回特定格式的数据。
              • CORS:使用跨域资源共享(CORS)机制,在服务器响应头中添加 Access-Control-Allow-Origin 字段,允许指定的域名访问资源。CORS 提供了更为灵活和安全的跨域请求解决方案。
              • 代理服务器:使用后端代理服务器,将前端的请求转发到同一域名下,然后再由后端发送请求至目标服务器,并将结果返回给前端。这种方式需要后端的配合来进行代理配置。
              • WebSocket:通过使用 WebSocket 协议进行跨域通信,WebSocket 不受同源策略限制。
            3. 解释一下事件委托(事件代理)的概念及其优势。

              事件委托(也称为事件代理)是一种将事件处理程序绑定到一个父元素而不是每个子元素上的技术。当子元素触发事件时,该事件会冒泡到父元素,并由父元素的事件处理程序来处理。

              事件委托的优势包括:

              • 内存占用更少:在大量子元素上添加事件处理程序可能会导致内存消耗过大,而使用事件委托只需要为父元素绑定一个事件处理程序。
              • 动态添加的元素也能被监听到:对于通过 JavaScript 动态创建的子元素,如果使用事件委托,无需再次绑定事件处理程序,因为它们会自动继承父元素的事件处理。
              • 提高性能:减少了 DOM 操作和事件绑定的 当然,以下是一道前端场景面试题及详细作答:

            4,讲一下思路和方案:假设你正在开发一个电子商务网站的购物车功能。每当用户点击“加入购物车”按钮时,应该如何将商品信息添加到购物车中,并实时更新购物车的总价和数量?请给出你的思路和实现方案。:

            思路和实现方案如下:

            1. 在页面上创建一个购物车容器,用于显示购物车的商品列表、总价和数量。
            2. 给每个商品的“加入购物车”按钮添加一个点击事件监听器,在点击时执行相应的处理函数。
            3. 在处理函数中,获取当前商品的信息(如名称、价格等),并创建一个新的商品对象。
            4. 将新的商品对象添加到购物车的商品列表中。
            5. 更新购物车的总价和数量:
              • 遍历购物车中的每个商品,累加它们的价格和数量。
              • 更新购物车界面上显示总价和数量的元素内容。
            6. (可选)为了提高用户体验,可以使用动画效果来展示购物车的变化,如展开/收起购物车的动画,商品添加到购物车的动画效果等。
            7. (可选)如果需要保持购物车的状态,可以考虑使用本地存储(如浏览器的 localStorage)来保存购物车数据,以便在页面刷新或用户重新访问时恢复购物车中的商品。

            关键点:

            • 点击事件监听器:使用 JavaScript 给按钮添加点击事件监听器,可以使用原生的 addEventListener 方法或库/框架提供的相应方法。
            • 商品信息获取:根据网站的具体实现方式,可以通过 DOM 操作获取商品信息,或者从后端获取商品数据。
            • 购物车数据管理:可以使用数组、对象或其他数据结构来管理购物车中的商品列表。在处理函数中,将新的商品对象添加到购物车列表中,并更新总价和数量。
            • 购物车界面更新:通过修改购物车界面上显示总价和数量的元素内容来实现界面的更新。可以通过操作 DOM 元素的属性或使用库/框架提供的相应方法来实现。
            • 动画效果(可选):可以使用 CSS3 的过渡和动画特性,或使用 JavaScript 库/框架来实现购物车的动画效果。
            • 本地存储(可选):使用浏览器的本地存储机制(如 localStorage)可以在页面刷新或用户重新访问时保持购物车的状态。购物车数据的读写操作可以在适当的时机进行,例如在添加商品到购物车时保存数据,在页面加载时从本地存储中恢复数据。

            注意事项:

            • 在处理函数中,需要确保商品信息的正确性。可以进行输入验证或从可信任的数据源获取商品信息。
            • 在实现购物车功能时,应该充分考虑用户体验,包括界面友好性、操作流畅性以及错误处理等方面。

            最后

            也给我们的辅导服务打个广告,现在报名支持指定导师哦~