前端面试题(综合篇)

1.js中的深浅拷贝的区别,如何实现(浅拷贝是拷贝地址还是对象本身)

        在JavaScript中,浅拷贝和深拷贝的区别主要涉及如何复制对象的属性,尤其是当对象的属性本身也是对象时。

        浅拷贝 (Shallow Copy)

           浅拷贝只复制对象的第一层属性,如果属性值本身是一个对象,浅拷贝只复制这个对象的引用(即内存地址),而不是对象本身。这意味着,如果原始对象的某个属性值是对象,并且被修改,那么拷贝的对象的相应属性也会受到影响。

           如何实现浅拷贝:

          (1)使用Object.assign方法:

const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);
console.log(copy); // { a: 1, b: { c: 2 } }
copy.b.c = 3;
console.log(original.b.c); // 3

           (2)使用扩展运算符(spread operator):

const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
console.log(copy); // { a: 1, b: { c: 2 } }
copy.b.c = 3;
console.log(original.b.c); // 3

         深拷贝 (Deep Copy)

             深拷贝不仅复制对象的第一层属性,还会递归复制所有层级的属性,包括属性值为对象的情况。这意味着,深拷贝的结果是一个完全独立的新对象,原始对象的任何更改都不会影响到拷贝的对象。

             如何实现深拷贝:

            (1)使用JSON.parseJSON.stringify

const original = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(original));
console.log(copy); // { a: 1, b: { c: 2 } }
copy.b.c = 3;
console.log(original.b.c); // 2

              上述方法不能复制函数、undefined和循环引用。

            (2)使用库(如lodash的_.cloneDeep方法):

const _ = require('lodash');
const original = { a: 1, b: { c: 2 } };
const copy = _.cloneDeep(original);
console.log(copy); // { a: 1, b: { c: 2 } }
copy.b.c = 3;
console.log(original.b.c); // 2

 2.==和===的区别,使用场景有哪些?

        在JavaScript中,===== 是两种用于比较两个值的操作符,但它们在比较时的行为和规则有显著差异:

  ==(等于)

  • 类型转换== 是等值比较,它在比较前会进行类型转换(类型强制),如果比较的两个值不是同一类型,JavaScript会尝试将它们转换成同一类型,然后进行比较。
  • 示例
    • 0 == '0' → true(数字0会被转换成字符串'0')
    • 2 == '2' → true
    • 1 == true → true
    • null == undefined → true

  ===(恒等于)

  • 无类型转换=== 是严格等值比较,它不进行类型转换。如果两个值的类型不同,直接返回false;只有当两个值的类型相同且值相等时,才返回true。
  • 示例
    • 0 === '0' → false
    • 2 === '2' → false
    • 1 === true → false
    • null === undefined → false

       使用场景

  • === 的使用场景:在大多数编程实践中,推荐使用===,因为它不会进行隐式类型转换,这使得比较结果更加可预测和明确。这是尤其重要的,在处理复杂的数据结构和需要精确值比较的逻辑时,===可以避免很多由于隐式类型转换导致的错误。
  • == 的使用场景== 可以在你确信进行类型转换是安全的或者必要的情况下使用。例如,在一些特定的情况下,你可能需要将nullundefined视为等价值,这时使用==可以简化代码(null == undefined)。但是,由于==可能会导致难以追踪的逻辑错误,使用时需要格外小心。

3.讲讲闭包

        闭包的定义

        闭包是指那些能够访问自由变量的函数。所谓“自由变量”,是指在函数中使用的,但既不是函数参数也不是函数局部变量的变量。

        一个函数在创建时会生成闭包,闭包包含该函数的函数体以及该函数可以访问的所有变量。

        闭包的创建

         闭包通常是在一个函数内部创建另一个函数时形成的:

function outer() {
    var outerVar = "I am outside!";

    function inner() {
        console.log(outerVar);  // 访问外部函数的变量
    }

    return inner;
}

var myInner = outer();
myInner();  // 输出: I am outside!

        在这个例子中,inner函数是在outer函数内部定义的,并且inner可以访问outer函数作用域中的outerVar变量。即使在outer函数执行完毕后,inner函数依旧能够访问outerVar变量,这就是闭包的作用。

        闭包的用途

         闭包的一些常见用途包括:

  1. 数据封装:闭包可以帮助创建私有变量,提供对象数据的封装和保护。
  2. 模块化代码:闭包允许创建模块,这些模块可以包含私有函数和私有状态,只暴露公共的API。
  3. 实现回调和高阶函数:在异步编程中,闭包常用于存储状态,直到异4步操作完成。

 4.讲讲异步(async,promise,原生的异步是什么)

        在JavaScript中,异步编程是一种处理耗时操作(如文件读取、网络请求等)的方式,它允许程序继续执行其他任务,而不是停下来等待耗时操作完成。JavaScript的异步模型基于事件循环,支持回调函数、Promises、以及async/await语法,这些都是用来处理异步操作的。

        原生的异步

         JavaScript的异步操作通常是通过以下几种技术实现的:

  1. 回调函数:这是实现异步操作的最早和最基本的方式。一个函数作为参数传递给另一个函数,当异步操作完成后,这个回调函数被调用。
  2. 事件监听:在DOM编程中常用,比如监听网络请求的load事件。

        Promise

         Promise是一个代表了异步操作最终完成或失败的对象。它有三种状态:

  • Pending(等待中):异步操作还没有完成。
  • Fulfilled(已成功):异步操作已经完成并且成功返回。
  • Rejected(已失败):异步操作已经完成但失败了。

        Promise的使用提供了比原生回调更好的错误处理和链式调用方法。例如,使用Promise处理一个HTTP请求:

function fetchData(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = () => {
            if (xhr.status === 200) {
                resolve(xhr.responseText);
            } else {
                reject(new Error("Request failed: " + xhr.statusText));
            }
        };
        xhr.onerror = () => reject(new Error("Network error"));
        xhr.send();
    });
}

fetchData('https://api.example.com/data')
    .then(data => console.log(data))
    .catch(error => console.error(error));

        async/await

   asyncawait是基于Promises的语法糖,使异步代码看起来更像同步代码,从而简化了Promise代码的写法。一个async函数自动将其返回值转换为一个Promise。await关键字可以暂停async函数的执行,等待Promise的解决,然后继续执行async函数并返回结果。

async function fetchData(url) {
    try {
        const response = await fetch(url);
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error:', error);
    }
}

fetchData('https://api.example.com/data');

      使用场景

  • 回调函数:适用于简单的异步处理,如单一事件响应或简单的异步转换。
  • Promises:适合需要链式处理多个异步操作的场景。
  • async/await:适用于需要处理多个异步操作,特别是在逻辑复杂或条件多变的情况下。

5.讲讲AJAX

        AJAX(Asynchronous JavaScript and XML)是一种在客户端和服务器之间异步交换数据和更新网页的技术,而无需重新加载整个网页。这使得应用程序可以快速响应用户的操作,提供更流畅的用户体验。AJAX 不是一种新的编程语言,而是一种使用现有标准的技术组合。

      核心技术

       AJAX 主要依赖以下几种技术:

  • HTML/XHTML 和 CSS:用于标准的网页内容和样式表现。
  • DOM (Document Object Model):用于动态显示和交互。
  • XML:最初用作数据交换的格式,但现在通常被JSON所取代。
  • XMLHttpRequest 对象:允许网页与服务器进行数据交换和更新。
  • JavaScript:用来绑定以上技术。

     XMLHttpRequest 对象

  XMLHttpRequest(XHR)对象是实现AJAX的核心。它为客户端提供了在发送请求到接收响应的过程中处理数据的能力。使用XHR对象,可以与服务器交换数据,从而更新网页的某一部分,而无需重载整个页面。

     使用XHR请求示例:

function fetchData() {
    var xhr = new XMLHttpRequest();  // 创建 XMLHttpRequest 对象
    xhr.open('GET', 'https://api.example.com/data', true);  // 配置请求类型、URL 和异步处理方式

    xhr.onreadystatechange = function () {  // 每当 readyState 改变时,就会调用这个函数
        if (xhr.readyState == 4 && xhr.status == 200) {
            console.log(xhr.responseText);  // 在控制台打印响应文本
        }
    };

    xhr.send();  // 发送请求
}

fetchData();

        在这个示例中,当请求完成并且没有错误时(readyState 为 4,status 为 200),响应的内容会被打印到控制台。

       JSON 与 AJAX

        随着时间的推移,JSON(JavaScript Object Notation)因其简洁和易于解析的特性,逐渐成为替代XML作为数据交换格式的首选。它与JavaScript的兼容性非常好,使得处理由服务器返回的数据更加方便。

       使用 AJAX 的好处

  • 提高性能:只需更新页面的一部分,而不是每次都重新加载页面。
  • 改善用户体验:减少了加载时间和页面闪烁,提供更平滑的交互体验。
  • 异步操作:允许用户在数据请求处理的同时,继续其他操作,例如填写网页表单。

       现代替代方案

        虽然XMLHttpRequest一直是实现 AJAX 的传统方式,但现代开发中更倾向于使用更简洁和强大的fetch API。fetch提供了一种更简单、更灵活的方式来处理网络请求,并支持Promises,使得异步操作更加容易管理。

6.跨域问题的解决方式有哪些

后端CORS、JSONP、搭建Node代理服务器、Nginx反向代理、postMessage、Websocket;

7.讲讲websocket(项目中如果出现断连应该怎么处理,怎么重连)

        WebSocket 是一种网络通信协议,提供了一种在单个长连接上进行全双工、双向交互的通信方式。它允许服务器和客户端之间的信息即时传递,这在需要实时功能的应用程序(如在线游戏、实时交易平台、协作工具等)中非常有用。

        WebSocket 基本工作原理

  1. 握手: 首先,客户端通过 HTTP 请求发起一个 WebSocket 连接。如果服务器支持 WebSocket,它会返回一个升级(Upgrade)响应,此时连接由 HTTP 升级为 WebSocket 连接。
  2. 数据交换: 一旦握手成功,客户端和服务器就可以通过这个建立的 WebSocket 连接自由地发送数据,直到其中一方关闭连接。

        处理 WebSocket 断连

        在实际项目中,WebSocket 连接可能因为多种原因断开,如网络波动、服务器重启等。为了确保应用的连续性和用户体验,处理断连和实现自动重连是非常重要的。

        重连策略
  1. 监听连接关闭: 使用 WebSocket 的 onclose 事件监听器来检测连接何时被关闭。
  2. 执行重连: 在 onclose 事件中实现重连逻辑。一般来说,重连应当有延迟,并考虑到重连次数,避免无限重试。
  3. 增加延迟和退避策略: 在尝试重连时,建议使用指数退避策略(exponential backoff),即每次重连尝试之间的间隔时间逐渐增长,这可以减少对服务器的压力。

        示例代码

let socket;
let retryInterval = 1000; // 初始重连间隔为 1000 毫秒
const maxInterval = 30000; // 最大重连间隔

function connect() {
    socket = new WebSocket('ws://example.com/socket');

    socket.onopen = function() {
        console.log('WebSocket connection established');
        retryInterval = 1000; // 重连成功后重置重连间隔
    };

    socket.onclose = function(e) {
        console.log('WebSocket connection closed', e);
        setTimeout(connect, retryInterval);
        retryInterval = Math.min(maxInterval, retryInterval * 2); // 指数退避策略
    };

    socket.onerror = function(error) {
        console.error('WebSocket error:', error);
        socket.close(); // 确保触发 onclose 事件
    };

    socket.onmessage = function(event) {
        console.log('Received:', event.data);
    };
}

connect(); // 初始连接

 8.vue中的生命周期

        在 Vue.js 中,每个 Vue 实例在被创建之前都要经过一系列的初始化过程。在这个过程中,Vue 会运行一系列的配置,如设置数据监听、编译模板、挂载实例到DOM,并在数据变化时更新DOM。这些步骤自然形成了Vue的生命周期,它提供了多个事件钩子,允许用户在特定时刻添加自己的代码。

        Vue 2 的生命周期钩子

         Vue 2 中的主要生命周期钩子包括:

  1. beforeCreate

    • 这个钩子在实例初始化之后,数据观测(data observer)和event/watcher 事件配置之前被调用。
  2. created

    • 在实例创建完成后被立即调用。在这一步,实例已完成数据观测、属性和方法的运算,$watch/$event 事件回调已被配置。
  3. beforeMount

    • 在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
  4. mounted

    • el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果根实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
  5. beforeUpdate

    • 数据更新时调用,发生在虚拟DOM打补丁之前。这里适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器。
  6. updated

    • 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以现在可以执行依赖于DOM的操作。
  7. beforeDestroy

    • 实例销毁之前调用。在这一步,实例仍然完全可用,这是解绑事件监听器、销毁子实例以及解除引用的一个好时机。
  8. destroyed

    • Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁。

        Vue 3 生命周期钩子的变化

         Vue 3 保留了 Vue 2 的大部分生命周期钩子,并引入了一些以 on 开头的新的生命周期函数,这些新的函数可以在 setup() 函数中使用,适用于Composition API:

  • onBeforeMount 替代 beforeMount
  • onMounted 替代 mounted
  • onBeforeUpdate 替代 beforeUpdate
  • onUpdated 替代 updated
  • onBeforeUnmount 替代 beforeDestroy
  • onUnmounted 替代 destroyed

        这些钩子提供了更多的灵活性,尤其是在使用Composition API构建组件时。它们也更容易与其他组合逻辑集成,提高了代码的可重用性和整洁度。

9.vue的双向数据绑定原理是什么?

        Vue.js 的双向数据绑定是其核心特性之一,使得开发者能够简化大量涉及到数据交互的复杂逻辑。Vue 实现双向绑定主要依赖于 JavaScript 的响应式系统,其中最关键的部分是利用了 ES5 的 Object.defineProperty() 方法来实现对对象属性的监听。

        双向数据绑定的原理

         1. 响应式系统

         当你在 Vue 组件中定义 data 属性时,Vue 会遍历这些属性,并使用 Object.defineProperty() 将它们转换为 getter/setter。Vue 内部用这种方法来实现对数据的响应式(reactive)监听。

  • Getter: 用于依赖收集,在属性被访问时收集当前属性依赖的组件。
  • Setter: 在属性值修改时被调用,负责通知变化,触发视图的重新渲染。
        2. 依赖收集

        每个组件实例都有一个对应的 watcher 实例。当组件渲染时,会访问到组件依赖的数据,这时数据的 getter 函数就会被触发。Getter 函数负责将当前的 watcher 添加到该数据属性的依赖列表中。这样,当数据改变触发 setter 时,所有依赖于这个数据的 watcher 都会收到通知。

        3. 派发更新

        当数据发生变化时,setter 会被调用,setter 中会通知所有订阅者(watcher),告诉它们依赖的数据已经发生变化。每个 watcher 收到通知后,会重新执行,从而导致组件重新渲染。

  v-model 的作用

        在表单输入和应用状态之间创建双向绑定最常用的指令是 v-modelv-model 在背后为不同的输入元素绑定不同的事件和属性。

  • 对于文本框 (<input>, <textarea>) 和单选按钮 (<input type="radio">), v-model 绑定的是 value 属性和 input 事件。
  • 对于复选框 (<input type="checkbox">), v-model 绑定的是 checked 属性和 change 事件。
  • 对于下拉列表 (<select>), v-model 绑定的是 value 属性和 change 事件。

        通过修改这些 DOM 元素的值,事件监听器会捕捉到变化并更新相应的 Vue 组件的数据。同时,当 Vue 组件的数据变化时,视图也会自动更新,从而反映出数据的最新状态。

  • 57
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爪洼守门员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值