跳至主要內容

08-07 面经

黄曦大约 4 分钟面经面经

JavaScript 数据类型

原始数据类型

  • string
  • number
  • bigint
  • boolean
  • undefined
  • symbol
  • null

复杂数据类型

  • object
  • array
  • function
  • 复杂数据类型均为 object 的子类

数据类型转换

  • tostring() 转换为字符串
  • Number() 转换为数字
  • Boolean() 转换为布尔值
  • parseInt() 转换为整数
  • String() 转换为字符串

typeof 结果

  • 其他值返回正常结果,只有以下三条特殊:
    • typeof null 返回 object
    • typeof Function 返回 function
    • typeof Array 返回 object

复杂内容的判断需要 instanceof 来判断:[] instanceof Array

原型链

每个构造函数(例如 Array)都有自己的原型对象 Prototype。当 Array 实例化时,通过 __proto__ 找到原型对象的属性。例如:

  • array.__proto__ === Array.prototype
  • Array.prototype.__proto__ === Object.prototype
  • Object.prototype.__proto__ === null

这种通过 __proto__ 查找原型对象属性的链式结构称为原型链。

作用域和作用域链

  • 作用域:代码的执行环境。全局执行环境是全局作用域。
  • 作用域链:一系列作用域,当需要查找某个变量或函数时,会按作用域链的顺序查找,直到找到为止。

浏览器全局对象是 window,Node.js 中是 global。全局属性 globalThis 在浏览器和 Node.js 环境下都指向全局对象。

深拷贝和浅拷贝的方法

深拷贝

  • 推荐方法:JSON.parse(JSON.stringify(obj))
  • 复杂数据结构:建议使用 lodash 的深拷贝方法 _.cloneDeep(obj)
  • 结构化克隆方法:structuredClone(obj) 性能更佳
  • 手动实现深拷贝:
    function deepClone(obj) {
        if (obj === null || typeof obj !== "object") {
            return obj; // 返回非对象的值(包括 null)
        }
    
        const objClone = Array.isArray(obj) ? [] : {};
    
        for (const key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (obj[key] && typeof obj[key] === "object") {
                    objClone[key] = deepClone(obj[key]); // 递归深拷贝
                } else {
                    objClone[key] = obj[key];
                }
            }
        }
    
        return objClone;
    }
    

浅拷贝

  1. Object.assign({}, obj)
  2. {...obj}
  3. 循环取出键值,赋值给新对象

闭包

闭包使一个函数能够记住并访问其定义时的作用域,即使函数在其作用域外部被调用。闭包用于私有数据的封装和数据的缓存。例如:

function createCounter() {
    let count = 0;
    return function() {
        return ++count;
    }
}
let counter = createCounter();
counter(); // 1
counter(); // 2
counter = null; // 释放闭包

callapplybind 的区别

  • callapply 都用于改变函数执行的上下文,区别是接收参数的方式不同:
    • apply(this, [args]):第一个参数是 this,第二个参数是数组
    • call(this, arg1, arg2, ...):第一个参数是 this,其余参数按顺序传入
  • bind 也用于改变函数执行的上下文,但返回一个新的函数,该函数可以接收参数,并在调用时绑定给定的 this 值。

DOM 事件

  • onmouseover:鼠标指针移入时执行事件
  • onmouseout:鼠标指针移出时执行事件
  • onmousedown:鼠标按下时执行事件
  • onmouseup:鼠标松开时执行事件

宏任务与微任务

  • 宏任务setTimeoutsetInterval、I/O、UI 渲染、requestAnimationFrame
  • 微任务PromiseMutationObserver

宏任务在当前任务执行完之前执行,如 setTimeoutsetInterval、I/O、UI 渲染。微任务在当前任务执行完之后执行,如 PromiseMutationObserver

0.5 毫米的线

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div></div>

    <style>
      div {
        left: 0;
        top: 0;
        margin-top: 20px;
        width: 100%;
        height: 1px;
        background-color: red;
        transform: scaleY(0.5);
        transform-origin: center center;
      }
    </style>
  </body>
</html>

在 Vue 中,自定义指令允许你在 DOM 元素上应用特殊行为。Vue 2 和 Vue 3 的自定义指令有些细微差别。以下是两者的基本用法:

Vue 2

在 Vue 2 中,自定义指令使用 Vue.directive 方法来注册。指令对象包含多个钩子函数,如 bindinsertedupdateunbind

// 全局注册
Vue.directive('focus', {
  // 当绑定元素插入到 DOM 中。
  inserted: function (el) {
    // 聚焦元素
    el.focus();
  }
});

// 局部注册
new Vue({
  el: '#app',
  directives: {
    focus: {
      inserted: function (el) {
        el.focus();
      }
    }
  }
});

使用自定义指令:

<input v-focus>

Vue 3

在 Vue 3 中,自定义指令的注册方式有所变化。我们使用 app.directive 方法来注册指令。

import { createApp } from 'vue';

const app = createApp({});

// 全局注册
app.directive('focus', {
  mounted(el) {
    el.focus();
  }
});

// 局部注册
const MyComponent = {
  template: '<input v-focus>',
  directives: {
    focus: {
      mounted(el) {
        el.focus();
      }
    }
  }
};

app.mount('#app');

使用自定义指令:

<input v-focus>

钩子函数

在 Vue 2 和 Vue 3 中,自定义指令可以定义以下钩子函数:

  • bind (Vue 2) / created (Vue 3): 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted (Vue 2) / mounted (Vue 3): 被绑定元素插入父节点时调用。
  • update (Vue 2) / updated (Vue 3): 所在组件的 VNode 更新时调用。
  • unbind (Vue 2) / beforeUnmount (Vue 3): 只调用一次,指令与元素解绑时调用。
  • componentUpdated (Vue 2) / unmounted (Vue 3): 指令所在组件的 VNode 及其子 VNode 全部更新后调用。

例子:

// Vue 2
Vue.directive('example', {
  bind(el, binding, vnode) {
    // 初始化
  },
  inserted(el, binding, vnode) {
    // 元素插入 DOM
  },
  update(el, binding, vnode, oldVnode) {
    // VNode 更新
  },
  unbind(el, binding, vnode) {
    // 元素解绑
  }
});

// Vue 3
app.directive('example', {
  created(el, binding, vnode, prevVnode) {
    // 初始化
  },
  mounted(el, binding, vnode, prevVnode) {
    // 元素插入 DOM
  },
  updated(el, binding, vnode, prevVnode) {
    // VNode 更新
  },
  beforeUnmount(el, binding, vnode, prevVnode) {
    // 元素解绑
  },
  unmounted(el, binding, vnode, prevVnode) {
    // 组件卸载后
  }
});