前端面试遇到的问题记录(实习)

一、vue2和vue3的区别

Vue 2 和 Vue 3 之间存在一些重要的区别,这些差异主要体现在架构、API、性能优化和开发体验等方面。以下是 Vue 2 和 Vue 3 的主要区别:

  1. 数据绑定原理:

    • Vue 2 使用 ES5 的 Object.defineProperty 方法来劫持数据变化。
    • Vue 3 则使用了 ES6 的 Proxy API 来代理数据,这样可以更高效地监听整个对象和数组的变化。
  2. 支持碎片 (Fragments):

    • Vue 2 不支持组件具有多个根节点。
    • Vue 3 支持组件拥有多个根节点(即支持碎片),这在某些情况下可以带来更灵活的组件结构。
  3. API 类型:

    • Vue 2 使用基于选项的对象 API (Options API),例如 datamethodscomputedwatch 等。
    • Vue 3 引入了 Composition API,这是一种基于函数的 API,允许开发者使用 setup 函数来组织和重用组件内的逻辑。
  4. 生命周期钩子:

    • Vue 3 对生命周期钩子进行了调整,例如添加了 setup 钩子,
    • 下面我将通过具体的代码示例来展示 Vue 2 和 Vue 3 在编写组件时的主要区别,特别是围绕 Options API 和 Composition API 的使用。

      Vue 2 示例

      在 Vue 2 中,我们通常使用 Options API 来定义组件。这里是一个简单的 Vue 2 组件示例:

      1// Vue 2 示例
      2new Vue({
      3  el: '#app',
      4  data: {
      5    message: 'Hello Vue 2!'
      6  },
      7  methods: {
      8    reverseMessage() {
      9      this.message = this.message.split('').reverse().join('');
      10    }
      11  },
      12  computed: {
      13    reversedMessage() {
      14      return this.message.split('').reverse().join('');
      15    }
      16  }
      17});
      Vue 3 示例

      在 Vue 3 中,我们既可以继续使用 Options API,也可以采用 Composition API。下面是使用这两种 API 的 Vue 3 组件示例。

      Options API (Vue 3)
      1// Vue 3 示例 (Options API)
      2import { createApp } from 'vue';
      3
      4createApp({
      5  data() {
      6    return {
      7      message: 'Hello Vue 3!'
      8    };
      9  },
      10  methods: {
      11    reverseMessage() {
      12      this.message = this.message.split('').reverse().join('');
      13    }
      14  },
      15  computed: {
      16    reversedMessage() {
      17      return this.message.split('').reverse().join('');
      18    }
      19  }
      20}).mount('#app');
      Composition API (Vue 3)
      1// Vue 3 示例 (Composition API)
      2import { createApp, ref, computed } from 'vue';
      3
      4createApp({
      5  setup() {
      6    const message = ref('Hello Vue 3!');
      7    const reversedMessage = computed(() => message.value.split('').reverse().join(''));
      8
      9    function reverseMessage() {
      10      message.value = message.value.split('').reverse().join('');
      11    }
      12
      13    return {
      14      message,
      15      reversedMessage,
      16      reverseMessage
      17    };
      18  }
      19}).mount('#app');

      分析

    • Options API:

      • Vue 2 和 Vue 3 的 Options API 在语法上非常相似。
      • Vue 3 的 Options API 提供了一些新的生命周期钩子,如 beforeCreate 变为了 beforeSetup(虽然实际使用的名称为 beforeCreate,但 setup 是 Composition API 的一部分)。
    • Composition API:

      • Vue 3 的 Composition API 使用 setup 函数来组织组件逻辑。
      • ref 和 computed 是 Composition API 中的核心功能,用于创建响应式引用和计算属性。
      • Composition API 更加灵活,可以更容易地复用逻辑。
    • 总结

    • Vue 2: 使用 Options API,数据、方法和计算属性直接定义在组件对象中。
    • Vue 3: 既可以使用 Options API 也可以使用 Composition API。Composition API 提供了更多的灵活性和更好的可复用性

二、js数据 类型

JavaScript 中的数据类型分为两大类:原始类型(primitive types)和引用类型(reference types)。下面是对这两种类型的详细介绍:

原始类型 (Primitive Types)

原始类型是值类型,意味着变量直接存储值。原始类型的值是不可变的,即一旦创建就不能改变其内容,只能替换整个值。JavaScript 中的原始类型包括:

  1. number:

    • 用于表示数值,包括整数和浮点数。
    • 注意 JavaScript 中没有单独的整数类型,所有数字都是 number 类型。
  2. string:

    • 用于表示文本字符串。
    • 字符串可以用单引号 ('') 或双引号 ("") 包围。
  3. boolean:

    • 用于表示布尔值,只有两个可能的值:true 和 false
  4. null:

    • 用于表示空值或未定义的对象引用。
    • null 是一个特殊的值,表示“没有任何东西”。
  5. undefined:

    • 用于表示尚未赋值的变量。
    • undefined 表示一个变量已经声明,但还没有被赋予任何值。
  6. symbol:

    • 一种新的原始类型,引入于 ECMAScript 6 (ES6)。
    • symbol 类型的值是唯一的,主要用于作为对象的键名来创建唯一的属性标识符。
  7. bigint:

    • 一种新的原始类型,引入于 ECMAScript 10 (ES10)。
    • bigint 用于表示任意精度的大整数,适用于需要处理非常大的整数的情况。
    • bigint 类型的值以 _n 结尾来区分普通数字。

引用类型 (Reference Types)

引用类型是对象类型,意味着变量存储的是指向内存中对象的引用,而不是对象本身。引用类型的值是可以改变的,可以通过引用进行访问和修改。JavaScript 中的引用类型包括:

  1. object:

    • 所有的对象都是引用类型。
    • 对象是一组无序的属性集合,每个属性都有一个键值对的形式。
    • 常见的内置对象类型包括 ArrayFunctionDateRegExpMapSetWeakMapWeakSet 等。
  2. function:

    • 函数在 JavaScript 中也是一种对象类型,可以被赋值给变量、存储在数据结构中或作为参数传递给其他函数。
    • 函数可以被调用,并可以返回值。
  3. array:

    • 数组是一种特殊的对象类型,用于存储一系列有序的值。
    • 数组可以包含任意类型的值,包括原始类型和其他对象。
  4. date:

    • 用于表示日期和时间的内置对象类型。
  5. regexp:

    • 用于表示正则表达式的内置对象类型。

示例

下面是一些 JavaScript 数据类型的示例:

1// 原始类型
2let num = 42; // number
3let str = "Hello"; // string
4let bool = true; // boolean
5let n = null; // null
6let u = undefined; // undefined
7let sym = Symbol(); // symbol
8let bigInt = 1234567890123456789012345678901234567890n; // bigint
9
10// 引用类型
11let obj = {}; // object
12let arr = [1, 2, 3]; // array
13let date = new Date(); // date
14let regExp = /hello/i; // regexp
15let func = function() { return 'world'; }; // function
16
17console.log(typeof num); // "number"
18console.log(typeof str); // "string"
19console.log(typeof bool); // "boolean"
20console.log(typeof n); // "object" (注意:null 的 typeof 结果为 "object")
21console.log(typeof u); // "undefined"
22console.log(typeof sym); // "symbol"
23console.log(typeof bigInt); // "bigint"
24
25console.log(typeof obj); // "object"
26console.log(Array.isArray(arr)); // true
27console.log(date instanceof Date); // true
28console.log(regExp instanceof RegExp); // true
29console.log(typeof func); // "function"

总结

  • 原始类型:

    • 存储值,不可变。
    • 包括 numberstringbooleannullundefinedsymbolbigint
  • 引用类型:

    • 存储对象的引用,可变。
    • 包括 objectfunctionarraydateregexp 等。

了解 JavaScript 中的数据类型对于编写正确的代码至关重要。

三、判断类型的方法

在 JavaScript 中,判断变量或值的类型是非常常见的需求,尤其是当你需要根据数据类型采取不同的操作时。JavaScript 提供了几种内置的方法来检测一个值的类型。以下是常用的几种方法:

1. typeof 运算符

typeof 是一种简单快速的类型检测方法,它可以返回一个字符串表示变量的类型。对于基本类型,typeof 非常有用,但对于某些复杂类型(如 null 和数组)可能会产生误导。

例子
1console.log(typeof 42);          // "number"
2console.log(typeof "hello");     // "string"
3console.log(typeof true);        // "boolean"
4console.log(typeof undefined);   // "undefined"
5console.log(typeof null);        // "object" (注意: 这是一个特殊情况)
6console.log(typeof {});          // "object"
7console.log(typeof []);          // "object"
8console.log(typeof function() {});// "function"

2. instanceof 运算符

instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。这对于检测对象类型非常有用。

例子
1console.log([1, 2, 3] instanceof Array); // true
2console.log({} instanceof Object);        // true
3console.log(new Date() instanceof Date);  // true

3. Object.prototype.toString.call()

Object.prototype.toString.call() 方法可以用来获取一个对象的内部 [[Class]] 属性。这种方法可以更准确地检测数组和 null 类型。

例子
1console.log(Object.prototype.toString.call(42));          // "[object Number]"
2console.log(Object.prototype.toString.call("hello"));     // "[object String]"
3console.log(Object.prototype.toString.call(true));        // "[object Boolean]"
4console.log(Object.prototype.toString.call(undefined));   // "[object Undefined]"
5console.log(Object.prototype.toString.call(null));        // "[object Null]"
6console.log(Object.prototype.toString.call([]));          // "[object Array]"
7console.log(Object.prototype.toString.call({}));          // "[object Object]"
8console.log(Object.prototype.toString.call(function() {}));// "[object Function]"

4. Array.isArray()

Array.isArray() 方法专门用于检测一个值是否为数组。

例子
1console.log(Array.isArray([1, 2, 3])); // true
2console.log(Array.isArray({}));        // false
3console.log(Array.isArray(null));      // false

5. 自定义函数

有时,为了特定的需求,你可能需要编写自定义的函数来检测类型。例如,你可能会创建一个函数来检测一个值是否为正则表达式。

例子
1function isRegExp(value) {
2  return Object.prototype.toString.call(value) === '[object RegExp]';
3}
4
5console.log(isRegExp(/test/)); // true
6console.log(isRegExp("test")); // false

总结

  • typeof:

    • 适用于检测基本类型。
    • 对于 null 和数组可能会产生误导。
  • instanceof:

    • 适用于检测对象类型。
    • 可能受到原型链污染的影响。
  • Object.prototype.toString.call():

    • 更精确地检测类型。
    • 可以区分 null 和数组。
  • Array.isArray():

    • 专门用于检测数组。

选择合适的方法取决于你的具体需求。在大多数情况下,结合使用 typeofinstanceofObject.prototype.toString.call() 可以覆盖大部分场景。如果需要更细致的类型检查,可以考虑编写自定义函数。

四、想把块剧中,可以用什么方法

看起来您可能是想询问如何将一个块级元素(block-level element)居中。在 CSS 中,有几种方法可以实现这一目标。下面是一些常用的方法:

1. 使用 margin: auto

如果你想要水平居中一个块级元素,可以使用 margin: auto。这种方法适用于已知宽度的元素。

示例代码
1
2
3
4
5Block Level Element Centering
6
14
15
16
17
18 这是一个居中的块级元素。 19
20 21 22

2. 使用 Flexbox

Flexbox 提供了一种更加灵活的方式来对齐元素。要使用 Flexbox 居中一个块级元素,你可以将父元素设置为 flex 容器,并使用 justify-content: centeralign-items: center 来居中子元素。

示例代码
1
2
3
4
5Block Level Element Centering with Flexbox
6
18
19
20
21
22
23 这是一个居中的块级元素。 24
25
26 27 28

3. 使用 Grid

CSS Grid 也是另一种现代的方法来居中块级元素。你可以将父元素设置为 grid 容器,并使用 place-items: center 来居中子元素。

示例代码
1
2
3
4
5Block Level Element Centering with Grid
6
17
18
19
20
21
22 这是一个居中的块级元素。 23
24
25 26 27

总结

  • margin: auto:
    • 适用于已知宽度的元素,仅实现水平居中。
  • Flexbox:
    • 提供了水平和垂直居中的能力。
    • 适用于需要灵活布局的场景。
  • Grid:
    • 也提供了水平和垂直居中的能力。
    • 适用于更复杂的网格布局。

五、跨域产生原因

跨域问题是指在浏览器中尝试从一个域名(或协议、端口)加载资源时,试图访问另一个不同域名(或协议、端口)上的资源。这是由于浏览器实施的安全策略,称为“同源策略”(Same-Origin Policy),该策略限制了来自不同源的脚本之间的交互。

同源策略

同源策略规定,来自相同源的脚本可以自由地访问彼此的文档和数据,而来自不同源的脚本则不能。这里的“源”指的是协议(http 或 https)、域名(如 example.com)和端口(如 80 或 443)的组合。只有这三个组成部分完全相同,才被认为是同一个源。

跨域产生的原因

  1. 不同域名:

    • 当一个网页尝试从一个域名加载资源,而该资源位于另一个域名时,就会产生跨域问题。
    • 例如,从 example.com 加载资源 data.example.org
  2. 不同协议:

    • 如果一个网页尝试从 http 协议加载资源,而该资源位于 https 协议下,也会产生跨域问题。
    • 例如,从 http://example.com 加载资源 https://data.example.org
  3. 不同端口:

    • 即使是同一个域名,如果端口不同,也会被视为不同的源。
    • 例如,从 example.com:80 加载资源 example.com:8080

跨域问题的影响

  1. 安全限制:

    • 同源策略是为了保护用户的隐私和数据安全。
    • 例如,它防止恶意脚本从一个网站窃取另一个网站的敏感数据。
  2. 资源加载限制:

    • 浏览器会阻止来自不同源的脚本加载或访问其他源的资源,除非服务器明确允许跨域访问。

解决方案

  1. CORS (Cross-Origin Resource Sharing):

    • CORS 是一种机制,允许服务器显式地允许来自不同源的请求。
    • 服务器可以通过 HTTP 响应头 Access-Control-Allow-Origin 来指定允许哪些源进行跨域访问。
  2. JSONP (JSON with Padding):

    • JSONP 是一种跨域数据传输的技术,适用于 GET 请求。
    • 它依赖于 
      父组件 (ParentComponent.vue)
      1
      7
      8

      解释

      1. 子组件:

        • 在子组件中,当用户点击按钮时,会触发 sendDataToParent 方法。
        • sendDataToParent 方法使用 $emit 触发一个名为 send-data 的事件,并传递一个字符串参数 "Hello from child component!"
      2. 父组件:

        • 在父组件中,使用 v-on 或 @ 监听子组件触发的 send-data 事件。
        • 当事件被触发时,父组件中的 handleDataFromChild 方法会被调用,并接收子组件传递过来的数据。
        • 接收到的数据被保存在 receivedData 数据属性中,并显示在页面上。

      总结

      • 事件触发:

        • 子组件使用 $emit 触发事件,并传递数据。
      • 事件监听:

        • 父组件使用 v-on 或 @ 监听事件,并定义处理函数来处理数据。

      九、前端存储数据方法

      在前端开发中,有多种方法可以在客户端存储数据。这些方法各有特点,适用于不同的场景。以下是常用的几种前端存储技术:

      1. Cookie

      • 用途:

        • 存储小量数据(通常不超过 4KB)。
        • 常用于会话管理、用户偏好设置等。
      • 特点:

        • 每次 HTTP 请求都会携带 Cookie 发送到服务器端。
        • 支持设置过期时间、路径、域名等属性。
        • 不支持跨域访问。
      • 示例:

        1document.cookie = "username=John Doe; expires=Fri, 31 Dec 2023 23:59:59 GMT";

      2. Local Storage

      • 用途:

        • 存储大量数据(通常浏览器限制为 5MB 至 10MB)。
        • 数据持久化存储,除非用户手动清除或者程序删除。
      • 特点:

        • 数据永远不会过期,除非被显式删除。
        • 支持字符串形式的数据存储。
        • 不支持跨域访问。
      • 示例:

        1localStorage.setItem('username', 'John Doe');
        2const username = localStorage.getItem('username');

      3. Session Storage

      • 用途:

        • 存储会话级别的数据。
        • 数据在浏览器关闭时自动删除。
      • 特点:

        • 数据仅在当前会话期间有效。
        • 支持字符串形式的数据存储。
        • 不支持跨域访问。
      • 示例:

        1sessionStorage.setItem('sessionUsername', 'John Doe');
        2const sessionUsername = sessionStorage.getItem('sessionUsername');

      4. IndexedDB

      • 用途:

        • 存储大量数据(理论上无限大,但受制于浏览器限制)。
        • 支持结构化数据存储,例如对象和数组。
      • 特点:

        • 支持事务处理。
        • 支持索引,可以进行高效的查询。
        • 不支持跨域访问。
      • 示例:

         
        1const request = window.indexedDB.open('myDatabase', 1);
        2request.onsuccess = function(event) {
        3  db = event.target.result;
        4};
        5
        6request.onupgradeneeded = function(event) {
        7  const db = event.target.result;
        8  const objectStore = db.createObjectStore('users', { keyPath: 'id' });
        9};
        10
        11// 添加数据
        12const transaction = db.transaction(['users'], 'readwrite');
        13const objectStore = transaction.objectStore('users');
        14objectStore.add({ id: 1, name: 'John Doe' });
        15
        16// 查询数据
        17const request = objectStore.get(1);
        18request.onsuccess = function(event) {
        19  console.log(event.target.result);
        20};

      5. Web SQL (已废弃)

      • 用途:

        • 存储数据,类似于 SQLite 数据库。
        • 支持事务处理和结构化数据存储。
      • 特点:

        • 不再被推荐使用。
        • 主要由 Safari 支持,其他浏览器已停止支持。
      • 示例:

        1const db = openDatabase('myDatabase', '1.0', 'My Database', 2 * 1024 * 1024);
        2
        3db.transaction(function(tx) {
        4  tx.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
        5});
        6
        7db.transaction(function(tx) {
        8  tx.executeSql('INSERT INTO users (name) VALUES (?)', ['John Doe']);
        9});
        10
        11db.transaction(function(tx) {
        12  tx.executeSql('SELECT * FROM users', [], function(tx, results) {
        13    console.log(results.rows.item(0).name);
        14  });
        15});

      6. HTML5 localStorage 和 sessionStorage 的区别

      • localStorage:

        • 数据永久存储,除非用户手动清除或者程序删除。
        • 数据在浏览器重启后仍然存在。
      • sessionStorage:

        • 数据仅在当前会话期间有效。
        • 数据在浏览器关闭后被清除。

      选择合适的存储方式

      • Cookie:

        • 适用于会话管理和用户认证。
        • 数据量较小。
      • Local Storage:

        • 适用于持久化存储大量数据。
        • 数据不会随浏览器关闭而丢失。
      • Session Storage:

        • 适用于临时存储数据。
        • 数据在浏览器关闭后自动删除。
      • IndexedDB:

        • 适用于需要高性能存储和查询的场景。
        • 支持事务处理和结构化数据。
      • Web SQL:

        • 已废弃,不建议使用。

      选择哪种存储方式取决于具体的应用需求。例如,如果你需要存储大量数据并且要求数据持久化,那么 localStorageIndexedDB 可能是更好的选择。如果你需要存储临时数据,那么 sessionStorage 更合适。而对于需要频繁交互的数据,IndexedDB 提供了更多的灵活性和性能优势。

      十、v-modle原理

      v-model 是 Vue.js 中用于实现表单输入和其他控件的双向数据绑定的一种简化的语法糖。在 Vue 2 中,v-model 的实现原理基于 v-bindv-on,并在内部处理了一些细节,使得开发者可以更方便地处理表单数据。下面是 v-model 的原理及其实现细节:

      基本原理

      1. 数据绑定:

        • v-model 通过 v-bind 将数据属性绑定到表单元素的 value 属性上。
        • 当数据属性的值发生变化时,表单元素的值也会随之更新。
      2. 事件监听:

        • v-model 通过 v-on 为表单元素添加事件监听器,通常是 input 事件。
        • 当用户交互导致表单元素的值发生变化时,事件监听器会更新数据属性的值。

      不同类型的表单元素

      v-model 在不同类型的表单元素上的表现有所不同:

      1. 对于