前端面试题详解整理86|基本数据理性,let,const区别,js作用域,异步问题,promis的三种状态,响应式变量,commputed,watch,ref,

61 阅读10分钟

3.5袋鼠云前端开发工程师面经

1.自我介绍

2.js的基本数据类型

JavaScript中的基本数据类型包括以下几种:

  1. 数字(Number):表示数值,可以是整数或浮点数,如 423.14

  2. 字符串(String):表示文本数据,用单引号 ' 或双引号 " 括起来的一串字符,如 'hello'"world"

  3. 布尔值(Boolean):表示逻辑值,只有两个取值,truefalse,用于逻辑判断和控制流程。

  4. 空值(null):表示空值或者不存在的对象,表示一个空对象指针。

  5. 未定义(undefined):表示变量声明但未初始化时的值,或者对象属性不存在时的值。

  6. 符号(Symbol):表示唯一的标识符,用于对象属性的键名。

JavaScript中的基本数据类型是不可变的,即它们的值一旦创建就不能被修改。这些基本数据类型存储在栈内存中,它们的值直接存储在变量访问的位置。

除了基本数据类型之外,JavaScript还有一个特殊的数据类型:对象(Object)。对象是一种复合数据类型,它可以包含多个键值对,每个键值对中的键是字符串或符号,值可以是任意类型的数据,包括基本数据类型和其他对象。对象存储在堆内存中,变量存储的是对象的引用,而不是对象本身的值。

3.let和const之间的区别

letconst 是 ES6 新增的两个用于声明变量的关键字,它们之间的主要区别在于变量的可变性和作用域范围。

  1. 可变性(Mutability)

    • 使用 let 声明的变量是可变的(mutable),即可以重新赋值。
    • 使用 const 声明的变量是不可变的(immutable),一旦赋值就不能再修改。
  2. 作用域范围(Scope)

    • 使用 let 声明的变量具有块级作用域(block-scoped),只在声明它的块(如 {})内部有效,不会被提升到外部作用域。
    • 使用 const 声明的变量也具有块级作用域,但与 let 不同的是,它声明的变量必须进行初始化赋值,并且一旦赋值后就不能再被修改。

综上所述,let 主要用于声明可变的变量,而 const 主要用于声明不可变的常量。在实际开发中,建议尽量使用 const 来声明变量,以避免不必要的修改和提高代码的可读性和稳定性。只有在需要重新赋值的情况下才使用 let

4.js作用域

JavaScript中的作用域(Scope)定义了变量和函数的可访问性和生存周期。作用域可以分为全局作用域和局部作用域(也称为块级作用域)两种。

  1. 全局作用域(Global Scope)

    • 全局作用域中的变量和函数在代码的任何地方都可以被访问,它们的生命周期与整个程序的运行周期一致。
    • 在浏览器环境中,全局作用域是指在全局作用域中声明的变量和函数,而在Node.js环境中,全局作用域是指在全局对象 global 上声明的变量和函数。
  2. 局部作用域(Local Scope)

    • 局部作用域中的变量和函数只在其声明的代码块(例如函数内部、for 循环、if 语句块等)内部可访问。
    • JavaScript中的局部作用域可以由函数作用域和块级作用域两种方式实现。
      • 函数作用域:在函数内部声明的变量只在该函数内部可访问。
      • 块级作用域:使用 letconst 关键字声明的变量只在其所在的块(例如 {})内部有效。
  3. 作用域链(Scope Chain)

    • 在JavaScript中,每个作用域都有一个与之关联的作用域链,作用域链是一个由当前作用域和其上层作用域中的变量对象组成的链式结构。
    • 当访问变量时,JavaScript引擎会沿着作用域链从内向外查找变量,直到找到第一个匹配的变量为止,如果没有找到则会报错。
    • 函数的作用域链在函数定义时确定,而不是在函数调用时确定,这意味着函数可以访问其定义时所处的作用域以及上层作用域中的变量。

JavaScript的作用域规则和作用域链是理解JavaScript程序行为的关键之一,合理使用作用域可以提高代码的可维护性和可读性。

5.异步问题,await和async,promise

awaitasync和promise之间的区别 async/awaitPromise 都是用于处理 JavaScript 中的异步编程的特性,它们之间有一些区别,但也是相互配合使用的。

  1. 语法上的区别

    • Promise 是一种异步操作的处理方式,使用 then() 方法链式调用,或者使用 catch() 方法捕获错误。
    • async/await 是基于 Promise 的语法糖,它提供了一种更加直观和简洁的方式来编写异步代码,使用 async 关键字声明异步函数,然后在函数内部使用 await 关键字等待异步操作的完成。
  2. 错误处理方式

    • Promise 中,通常使用 catch() 方法或者在 then() 方法链的最后添加一个 catch 处理未捕获的错误。
    • async/await 中,可以使用 try/catch 语法来捕获异步操作中的错误,使得错误处理更加直观和方便。
  3. 代码可读性

    • async/await 相对于 Promise 更加易读易写,使得异步代码更接近同步代码的写法,逻辑更加清晰,减少了回调地狱(Callback Hell)的问题。
  4. 执行顺序

    • Promise 中,异步操作的执行顺序由 then() 方法链的顺序决定,或者使用 Promise.all() 等方法控制多个异步操作的执行顺序。
    • async/await 中,异步操作的执行顺序由 await 关键字决定,可以使用 await 在必要时等待前一个异步操作的完成后再执行下一个异步操作。
  5. 返回值

    • Promise 是一个对象,可以通过 then() 方法获取异步操作的返回值。
    • async/await 是一种语法结构,异步函数的返回值是一个 Promise 对象,可以直接获取异步操作的返回值。

综上所述,async/await 是一种更加直观和方便的异步编程方式,它基于 Promise 提供了更加简洁和易读的语法,使得异步代码的编写和理解更加容易。在实际开发中,可以根据具体情况选择使用 Promiseasync/await 来处理异步操作。

promise有几种状态

Promise 有三种状态:

  1. 待定(Pending):初始化 Promise 实例时的初始状态。表示异步操作尚未完成,也未被拒绝。这时可能正在进行中或者还未开始。

  2. 已完成(Fulfilled):表示异步操作已经成功完成。一旦 Promise 的状态变为已完成,就会调用 .then() 方法中的回调函数,以处理异步操作的结果。

  3. 已拒绝(Rejected):表示异步操作失败或者出错。一旦 Promise 的状态变为已拒绝,就会调用 .catch() 方法中的回调函数,以处理异步操作的错误。

在 Promise 的生命周期中,它的状态会从待定转变为已完成或已拒绝,且不可逆。一旦状态发生变化,就会触发相应状态的回调函数执行。

apromise如何捕获异常,promise的事件监听

在 Promise 中,可以使用 .catch() 方法来捕获异常,也可以使用 .then() 方法的第二个参数来捕获异常。此外,还可以使用全局的 unhandledrejection 事件来监听未捕获的 Promise 异常。

  1. 捕获异常

    • 使用 .catch() 方法捕获异常:
      somePromiseFunction()
        .then(result => {
          // 异步操作成功的处理
        })
        .catch(error => {
          // 异步操作失败的处理
        });
      
    • 使用 .then() 方法的第二个参数捕获异常:
      somePromiseFunction()
        .then(result => {
          // 异步操作成功的处理
        }, error => {
          // 异步操作失败的处理
        });
      
  2. Promise 事件监听

    • 可以使用 unhandledrejection 事件来监听未捕获的 Promise 异常:
      window.addEventListener('unhandledrejection', event => {
        // 处理未捕获的 Promise 异常
        console.error('Unhandled Promise rejection:', event.reason);
      });
      
    • 注意:使用 unhandledrejection 事件可以全局监听未捕获的 Promise 异常,但不能阻止脚本的运行。建议在开发环境中使用,以便及时发现并处理未捕获的 Promise 异常。

以上是在 JavaScript 中捕获异常和监听 Promise 事件的一些方法,根据具体情况选择适合的方式来处理异步操作的异常。

6.react的掌握程度

7.vue的响应式变量如何声明

在 Vue 中声明响应式变量,一般有以下几种方式:

  1. data 属性

    • 在 Vue 组件中,可以通过 data 属性声明响应式变量。在 Vue 实例创建时,Vue 会将 data 属性中的所有属性转换为响应式数据,使得当数据发生变化时,相关的视图会自动更新。
    • 示例:
      Vue.component('my-component', {
        data() {
          return {
            message: 'Hello, Vue!'
          };
        },
        template: '<div>{{ message }}</div>'
      });
      
  2. computed 计算属性

    • 可以使用 computed 计算属性来声明一个基于其他响应式变量计算得出的新变量。计算属性具有缓存特性,只有依赖的数据发生改变时,才会重新计算。
    • 示例:
      Vue.component('my-component', {
        data() {
          return {
            firstName: 'John',
            lastName: 'Doe'
          };
        },
        computed: {
          fullName() {
            return this.firstName + ' ' + this.lastName;
          }
        },
        template: '<div>{{ fullName }}</div>'
      });
      
  3. watch 监听器

    • 可以使用 watch 监听器来监听一个响应式变量的变化,并在变化时执行特定的逻辑。
    • 示例:
      Vue.component('my-component', {
        data() {
          return {
            count: 0
          };
        },
        watch: {
          count(newValue, oldValue) {
            console.log('count 变化为:', newValue);
          }
        },
        template: '<div>{{ count }}</div>'
      });
      

以上是在 Vue 中声明响应式变量的几种方式,根据具体需求选择适合的方式来管理组件的数据。

8.ref和reactive之间的区别

在 Vue 3 中,refreactive 是用于创建响应式数据的两个主要函数,它们之间的区别主要在于使用场景和返回值类型:

  1. ref

    • ref 是用于创建一个包装对象(wrapper object),将基本数据类型(如数字、字符串等)转换为响应式对象。通常用于声明单个简单的响应式变量。
    • ref 返回的是一个包含 value 属性的响应式对象,通过访问 value 属性来获取和修改包装的值。
    • 示例:
      import { ref } from 'vue';
      
      const count = ref(0);
      console.log(count.value); // 0
      count.value++; // 修改 count 的值
      console.log(count.value); // 1
      
  2. reactive

    • reactive 是用于创建一个响应式代理对象,将普通对象转换为响应式对象。通常用于声明包含多个属性的响应式对象。
    • reactive 返回的是一个响应式代理对象,可以直接访问和修改对象的属性。
    • 示例:
      import { reactive } from 'vue';
      
      const user = reactive({
        name: 'John',
        age: 30
      });
      console.log(user.name); // John
      user.age++; // 修改 age 的值
      console.log(user.age); // 31
      
  3. 使用场景

    • ref 通常用于声明单个简单的响应式变量,比如计数器、输入框的值等。
    • reactive 通常用于声明包含多个属性的复杂对象,比如用户信息、表单数据等。

综上所述,refreactive 在创建响应式数据时有不同的用途和返回值类型,根据具体的需求选择适合的函数来创建响应式数据。

9.子组件去调用父组件的变量如何操作

在 Vue 中,子组件想要访问父组件的变量,通常通过以下两种方式来实现:

  1. 通过 Props 传递数据

    • 父组件可以通过 props 向子组件传递数据,子组件通过 props 接收父组件传递过来的数据。
    • 父组件:
      <template>
        <child-component :parentData="parentData"></child-component>
      </template>
      
      <script>
      import ChildComponent from './ChildComponent.vue';
      
      export default {
        components: {
          ChildComponent
        },
        data() {
          return {
            parentData: 'Hello from parent'
          };
        }
      }
      </script>
      
    • 子组件:
      <template>
        <div>{{ parentData }}</div>
      </template>
      
      <script>
      export default {
        props: ['parentData']
      }
      </script>
      
  2. 通过 $emit 触发事件

    • 子组件可以通过 $emit 触发一个自定义事件,父组件监听这个事件并处理。
    • 子组件:
      <template>
        <button @click="sendDataToParent">Send Data to Parent</button>
      </template>
      
      <script>
      export default {
        methods: {
          sendDataToParent() {
            this.$emit('childEvent', 'Data from child');
          }
        }
      }
      </script>
      
    • 父组件:
      <template>
        <child-component @childEvent="handleChildEvent"></child-component>
      </template>
      
      <script>
      import ChildComponent from './ChildComponent.vue';
      
      export default {
        components: {
          ChildComponent
        },
        methods: {
          handleChildEvent(data) {
            console.log('Received data from child:', data);
          }
        }
      }
      </script>
      

以上是两种常见的子组件调用父组件的变量的方法。根据具体情况选择适合的方法来实现组件之间的数据通信。