Skip to content

常面常新-1021

问题汇总

上午场:

  • position 有几种
  • Css 盒子模型
  • v-for为什么要有key
  • 什么是闭包
  • 父子组件传参
  • Vuex 有什么
  • Vue 底层双向绑定逻辑
  • Vue 生命周期钩子
  • 前端性能优化有什么

下午场:

  • box-size
  • 怪异盒子模型的长宽是怎么计算的
  • 闭包
  • position 有几种
  • 垂直居中的方式(只让说了绝对定位和flex
  • js的数据类型有哪些
  • typeofinstanceof
  • 深拷贝、浅拷贝(深拷贝实现方式)
  • 变量提升、暂时性死区
  • callapplybind 区别
  • mapfilterreduce区别
  • new的过程
  • Vue 底层双向绑定逻辑
  • 两道代码考察

项目相关:

  • 大文件上传是怎么做到的
  • 请求合并是怎么做到的

上午场

Css 盒子模型

CSS 盒子模型(Box Model)是网页设计和布局的基础概念之一。它描述了一个元素在网页中所占据的空间,包括内容、内边距(padding)、边框(border)和外边距(margin)。理解盒子模型对于正确地布局和样式化网页元素至关重要。

  • 盒子模型的计算

    默认情况下,元素的总宽度和总高度计算如下:

    • 总宽度 = 内容宽度 + 左右内边距 + 左右边框 + 左右外边距
    • 总高度 = 内容高度 + 上下内边距 + 上下边框 + 上下外边距

v-for为什么要有key

  1. 高效的DOM更新:

    • Vue.js 使用虚拟DOM来追踪和更新实际DOM。当列表数据发生变化时,Vue.js需要确定哪些元素发生了变化、哪些元素需要被添加或删除。
    • key属性帮助Vue.js更高效地识别每个列表项,从而进行最小化的DOM更新操作。如果没有keyVue.js 会使用默认的就地复用策略,可能会导致不必要的DOM操作和渲染错误。
  2. 保持组件状态:

    • 当列表项是组件时,key属性可以确保组件在更新时保持其内部状态。如果没有key,组件可能会被错误地复用,导致状态丢失或混乱。
  3. 避免渲染错误:

    • 在某些情况下,缺少key可能会导致渲染错误或性能问题。使用唯一的key可以确保每个列表项在更新时都能正确地被识别和处理。

什么是闭包

闭包(Closure)JavaScript中的一个重要概念,它允许函数访问其词法作用域(Lexical Scope)中的变量,即使这个函数在其词法作用域之外执行。闭包使得函数可以“记住”并访问定义它们时的环境。

  • 定义

    闭包是指那些能够访问自由变量的函数。自由变量是指在函数中使用的,但既不是函数参数也不是函数内部声明的变量。

  • 特性

    • 函数嵌套:闭包通常是通过在一个函数内部定义另一个函数来创建的。

    • 词法作用域:闭包可以访问其词法作用域中的变量,即使在其外部执行时也是如此。

    • 持久化变量:闭包可以使得函数内部的局部变量在函数执行完毕后依然存在。

下午场

box-size

box-sizing CSS中的一个属性,用于定义元素的盒子模型的计算方式。它有两个常用值:content-box border-box

  • content-box(默认值):

仅内容区域的宽度和高度由widthheight属性设置,内边距和边框不包括在内。 这是W3C标准盒子模型的默认行为。

  • border-box

内容区域、内边距和边框的总宽度和总高度由widthheight属性设置,内边距和边框包括在内。 这种计算方式更符合直觉,特别是在处理复杂布局时。

怪异盒子模型的长宽是怎么计算的

在怪异盒子模型(即 box-sizing: border-box)中,元素的宽度和高度包括内容、内边距和边框。具体计算方式如下:

总宽度 = width(包括内容宽度、内边距和边框) 总高度 = height(包括内容高度、内边距和边框)

js的数据类型有哪些

  • 基本数据类型

    numberstringbooleanundefinednullsymbolbigint

  • 引用数据类型

    object

typeof、instanceof

  • typeof

    typeof 运算符可以用来判断基本数据类型和函数类型,但对于null和数组等引用类型的判断有局限性。

    js
    console.log(typeof 42); // 输出: "number"
    console.log(typeof "hello"); // 输出: "string"
    console.log(typeof true); // 输出: "boolean"
    console.log(typeof undefined); // 输出: "undefined"
    console.log(typeof null); // 输出: "object" (这是一个历史遗留的 bug)
    console.log(typeof {}); // 输出: "object"
    console.log(typeof []); // 输出: "object"
    console.log(typeof function () {}); // 输出: "function"
    console.log(typeof Symbol("sym")); // 输出: "symbol"
    console.log(typeof 123n); // 输出: "bigint"
  • instanceof

    instanceof运算符可以用来判断对象是否是某个构造函数的实例。

    js
    console.log([] instanceof Array); // 输出: true
    console.log({} instanceof Object); // 输出: true
    console.log(function () {} instanceof Function); // 输出: true
    console.log(new Date() instanceof Date); // 输出: true

instanceof底层实现

  • 获取构造函数的prototype属性。

  • 获取对象的原型链。

  • 沿着对象的原型链向上查找,看是否能找到与构造函数的prototype属性相同的对象。

  • 如果找到,返回true;否则,返回false

map、filter、reduce 区别

  • map 方法用于创建一个新数组,其结果是原数组中的每个元素调用一个提供的函数后的返回值。

  • filter 方法用于创建一个新数组,其中包含所有通过提供函数实现的测试的元素。

  • reduce 方法对数组中的每个元素执行一个提供的函数(从左到右),将其结果汇总为单个返回值。

方法返回值类型作用是否改变原数组
map新数组对数组中的每个元素执行提供的函数,并返回结果组成的新数组
filter新数组对数组中的每个元素执行提供的函数,返回值为 true 的元素组成的新数组
reduce单一值对数组中的每个元素执行提供的函数,并将其结果汇总为单个返回值

new 的过程

  1. 创建一个空对象:创建一个新的空对象,并将其**proto**属性设置为构造函数的prototype属性。

  2. 绑定 this:将构造函数的作用域赋给新对象(即this指向这个新对象)。

  3. 执行构造函数:执行构造函数中的代码,为新对象添加属性和方法。

  4. 返回新对象:如果构造函数返回一个对象,则返回该对象;否则,返回新创建的对象。

  • 手动实现new操作符
js
function myNew(constructor, ...args) {
  // 创建一个空对象,并将其 __proto__ 属性设置为构造函数的 prototype 属性
  const obj = Object.create(constructor.prototype);

  // 将构造函数的作用域赋给新对象,并执行构造函数
  const result = constructor.apply(obj, args);

  // 如果构造函数返回一个对象,则返回该对象;否则,返回新创建的对象
  return result instanceof Object ? result : obj;
}

// 测试
const bob = myNew(Person, "Bob", 25);
console.log(bob); // 输出: Person { name: 'Bob', age: 25 }

闭包代码考察

html
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>
<script>
  var list = document.querySelectorAll("li");
  for (var i = 0; i < list.length; i++) {
    list[i].onclick = function () {
      console.log(i); //点击每个列表项的时候,控制台会输出 6
    };
  }
</script>
  • 解决方法一:使用闭包

    需要创建一个新的作用域,这样每次迭代的i值才能被独立地保存

html
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>
<script>
  var list = document.querySelectorAll("li");
  for (var i = 0; i < list.length; i++) {
    (function (index) {
      list[index].onclick = function () {
        console.log(index);
      };
    })(i);
  }
</script>
  • 解决方法二:使用let
html
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>
<script>
  var list = document.querySelectorAll("li");
  for (let i = 0; i < list.length; i++) {
    list[i].onclick = function () {
      console.log(i);
    };
  }
</script>

变量提升、暂时性死区

变量提升(Hoisting)

变量提升 是JavaScript中的一个行为,它将变量和函数声明提升到其作用域的顶部。变量提升意味着在代码执行之前,变量和函数声明已经在内存中被分配了空间

  • 变量提升的特点

    • 变量声明提升:变量声明会被提升到其作用域的顶部,但变量赋值不会被提升。

    • 函数声明提升:函数声明会被提升到其作用域的顶部,并且整个函数体都会被提升。

暂时性死区

暂时性死区,是指在使用letconst声明变量时,从变量进入作用域到变量声明之间的这段时间。在这段时间内,访问这些变量会导致引用错误(ReferenceError)

  • 暂时性死区的特点

    • 块级作用域:letconst声明的变量具有块级作用域。
    • 在声明之前不可访问:在变量声明之前访问它们会导致引用错误。

代码考察

js
function test(m) {
  // 函数的参数按值传递
  // m -> {k: 30}
  m = { v: 5 }; // m 重写了, 不再跟外层的var m指向同一个地址
  // m -> {v: 5}
}

var m = { k: 30 };
test(m);
alert(m.v); // undefined  m -> {k:30}

//---下面为百度的另一个题---//
function test(m) {
  // 函数的参数按值传递
  // m -> {k: 30}
  m.q = 6;
  // m -> {k: 30, q: 6}
}

var m = { k: 30 };
test(m);
alert(m.q); //6

Vue 底层双向绑定逻辑

  1. 响应式系统(Reactivity System):
    Vue 使用了一个高效的响应式系统来追踪数据的变化。在Vue 2.x中,这个系统依赖于Object.defineProperty来劫持数据对象的属性访问器。当数据变化时,Vue会自动更新视图。在 Vue 3.x 中,响应式系统得到了重写,使用了基于 Proxy 的更通用和强大的方法。

  2. 虚拟 DOM(Virtual DOM):
    Vue维护了一个虚拟DOM树,它是一个轻量级的JavaScript对象,代表了真实DOM树的状态。当数据变化时,Vue会创建一个新的虚拟DOM树,并与旧的虚拟DOM树进行比较(这个比较过程称为 Diffing)。Vue 会计算出最小的更新操作来更新真实DOM,从而提高性能。

  3. 数据劫持(Data Hijacking): 在Vue 2.x中,Vue实例被创建时,它会递归遍历数据对象的所有属性,并使用Object.defineProperty来劫持这些属性的 gettersetter。这样,每当数据变化时,Vue都能检测到变化并触发视图更新。

  4. 依赖收集(Dependency Collection): 当组件被渲染时,Vue会记录哪些数据被用到了(即依赖收集)。这些信息被存储在一个依赖列表中。当数据变化时,只有依赖于这些数据的组件才会重新渲染。

  5. 异步更新队列(Async Update Queue): Vue将数据变化的更新操作放入一个异步队列中,然后批量执行。这样可以避免不必要的重复渲染,提高性能。

如有转载或 CV 的请标注本站原文地址