Javascript前端面试基础(九)

浏览器缓存

浏览器缓存分为强缓存和协商缓存。当客户端请求某个资源时,获取缓存的流程如下

  • 先根据这个资源的一些http header判断它是否命中强缓存,如果命中则直接从本地获取缓存资源,不会发请求到服务器;
  • 当强缓存没有命中时,客户端会发送请求到服务器,服务器通过另一些request header验证这个资源是否命中协商缓存,称为http再验证,如果命中,服务器将请求返回,但不返回资源,而是告诉客户端直接从缓存中获取,客户端收到返回后就会从缓存中获取资源;
  • 强缓存和协商缓存共同之处在于,如果命中缓存,服务器都不会返回资源;区别是,强缓存不对发送请求到服务器,但协商缓存会。
  • 当协商缓存也没命中时,服务器就会将资源发送回客户端。
  • 当ctr1+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;当f5刷新网页时,跳过强缓存,但是会检查协商缓存;

强缓存

  • Expires(该字段是http1.0时的规范,值为一个绝对时间的GMT 格式的时间字符串,代表缓存资源的过期时间)
  • Cache-Contro7: max-age(该字段是http1.1的规范,强缓存利用其max-age 值来判断缓存资源的最大生命周期,它的值单位为秒)

协商缓存

  • Last-Modified(值为资源最后更新时间,随服务器response返回)
  • If-Modified-Since(通过比较两个时间来判断资源在两次请求期间是否有过修改,如果没有修改,则命中协商缓存)
  • ETag(表示资源内容的唯一标识,随服务器response 返回)
  • If-None-Match(服务器通过比较请求头部的If-None-Match与当前资源的ETag是否一致来判断资源是否在两次请求之间有过修改,如果没有修改,则命中协商缓存)

浏览器缓存(Browser Caching)是一种减少网页加载时间的技术,它通过存储之前请求过的资源(如HTML文档、CSS文件、JavaScript文件、图片等)的副本,在再次需要这些资源时直接从本地存储中加载,而不是重新从服务器下载。这可以显著提高网页的加载速度和效率,并减少网络带宽的使用。

浏览器缓存的工作原理

  1. 首次请求资源
    • 浏览器向服务器发送请求以获取资源。
    • 服务器返回资源,并在响应头中包含缓存相关的信息,如ExpiresCache-ControlETagLast-Modified等。
  2. 再次请求资源
    • 浏览器在再次需要该资源时,首先检查本地缓存中是否存在该资源的副本。
    • 如果存在,浏览器会检查资源的缓存是否仍然有效(基于缓存策略,如Cache-Control中的max-age)。
    • 如果缓存有效,则直接从缓存中加载资源,无需向服务器发送请求。
    • 如果缓存无效,浏览器会向服务器发送条件性请求(使用If-None-MatchIf-Modified-Since请求头),询问自上次请求以来资源是否已更改。
    • 如果资源未更改(服务器返回304 Not Modified状态码),浏览器继续使用缓存中的资源。
    • 如果资源已更改,则服务器返回新资源,并更新缓存。

缓存控制策略

  • Cache-Control :这是HTTP/1.1中引入的最重要的缓存控制头部,用于定义缓存策略,如no-cacheno-storepublicprivatemax-age等。
  • Expires :指定资源缓存的过期时间。不过,由于HTTP/1.1中Cache-Control的优先级更高,Expires可能不被所有浏览器支持。
  • ETag:资源的特定版本标识符。浏览器在发送条件性请求时会包含此头部,以检查资源自上次请求以来是否已更改。
  • Last-Modified:资源最后一次修改的时间戳。虽然不直接控制缓存,但可用于条件性请求中。

缓存的好处

  • 减少加载时间:快速加载页面可以提高用户体验。
  • 减少带宽消耗:减少了对服务器的请求,降低了带宽使用。
  • 缓解服务器压力:由于减少了直接对服务器的请求,服务器可以处理更多的并发请求。

注意事项

  • 缓存可能导致用户看到旧的内容,特别是在内容频繁更新的网站上。
  • 需要正确配置缓存策略,以确保缓存的有效性和时效性。
  • 对于敏感数据或需要实时更新的内容,应谨慎使用缓存。

WebSocket

什么是websocket?_websocket是什么-CSDN博客

WebSocket是HTML5引入的一项技术,它实现了浏览器与服务器之间的全双工通信,为实时Web应用程序提供了更高效和实时的通信方式。以下是对WebSocket的详细解析:

一、WebSocket的基本概念

WebSocket本质上是一个基于TCP的协议,它不属于HTTP无状态协议,协议名为"ws"(加密时为"wss")。WebSocket通过握手机制,在客户端和服务器之间建立一个持久的连接,从而允许双方进行双向通信。这种通信方式不仅减少了网络流量和延迟,还提高了数据交换的效率。

二、WebSocket的工作原理

WebSocket的工作原理可以分为三个阶段:握手、数据传输和断开连接。

  1. 握手阶段
    • 客户端通过向服务器发送一个特殊的HTTP请求头来发起WebSocket连接。
    • 服务器检查请求头中的特定字段,确认支持WebSocket协议后,发送特殊的HTTP响应头进行握手确认。
    • 握手成功后,双方建立WebSocket连接。
  2. 数据传输阶段
    • 一旦建立了WebSocket连接,客户端和服务器就可以通过该连接进行双向的实时数据传输。
    • 双方可以发送和接收消息,消息以帧的形式进行传输。WebSocket协议定义了不同类型的帧,如文本帧和二进制帧,用于传输不同类型的数据。
  3. 断开连接阶段
    • 当连接不再需要时,客户端或服务器可以发起关闭连接的请求。
    • 双方会交换特殊的关闭帧,以协商关闭连接,并确保双方都接收到了关闭请求。

三、WebSocket的优点

  1. 实时性:WebSocket能够实现实时的双向通信,服务器可以主动推送数据到客户端,而不需要客户端发送请求。
  2. 减少网络流量:WebSocket连接只需要进行一次握手,之后就可以保持长连接,减少了网络流量和延迟。
  3. 较少的开销:WebSocket使用较少的开销来维持连接,因为在连接建立后,客户端和服务器之间的通信只需要少量的头信息。
  4. 跨平台支持:WebSocket协议可以在多种平台上使用,包括桌面应用、移动应用和Web应用。

四、WebSocket的缺点

  1. 兼容性问题:WebSocket协议在一些旧版本的浏览器上不被支持,需要通过polyfill或者其他技术手段来解决兼容性问题。
  2. 服务器资源占用:由于WebSocket的长连接特性,服务器需要维护大量的连接,这可能会占用较多的服务器资源。
  3. 安全性问题:WebSocket连接需要特殊的安全设置,以防止恶意攻击和数据泄漏。

五、WebSocket的应用场景

WebSocket适用于需要实时通信、实时推送数据、实时同步编辑等场景,如实时聊天应用、实时数据展示、多人协同编辑、实时监控系统、游戏开发等。通过WebSocket,可以构建实时、高效和响应迅速的Web应用程序。

总的来说,WebSocket作为HTML5引入的一项技术,为Web应用程序的实时通信提供了强有力的支持。然而,在使用WebSocket时,也需要注意其兼容性问题、服务器资源占用和安全性问题等方面的挑战。

Electron

Electron是一个基于Node.js和Chromium的开源框架,它允许开发者使用JavaScript、HTML和CSS等Web技术来构建跨平台的桌面应用程序。以下是关于Electron的详细解析:

一、Electron简介

  • 定义:Electron是一个使用Web技术(JavaScript、HTML、CSS)来开发跨平台桌面应用程序的框架。它集成了Chromium和Node.js,使得开发者能够利用熟悉的Web技术来创建桌面应用。
  • 开发背景:Electron最早由GitHub团队开发,自2013年发布以来,已经得到了广泛的应用和持续的更新。
  • 技术组成 :Electron主要由Chromium、Node.js和Native API 三部分组成。Chromium提供了Web渲染能力,Node.js用于处理底层操作系统和硬件的交互,Native API则允许开发者调用操作系统的原生接口

二、Electron的优势

  1. 跨平台支持:Electron开发的桌面应用可以在Windows、macOS和Linux等多个操作系统上无缝运行,有效减少了开发者在不同平台上开发应用程序的工作量和时间。
  2. 前端技术支持:使用HTML、CSS和JavaScript等前端技术栈进行开发,拥有大量的UI组件和模板,开发出来的桌面应用界面更加美观、交互体验更好。
  3. 本地能力支持:Electron除了支持Web API外,还允许调用很多操作系统底层API来访问计算机的硬件设备,实现更丰富的功能。
  4. 调试测试支持:由于Electron框架开发的应用程序是基于Chrome内核的,因此可以直接使用Chrome内核的开发者工具进行调试和测试,提高了开发效率。
  5. 自动更新支持:Electron应用程序在发布后可以自动更新,当有新版本可用时,用户会自动收到更新提示,无需手动下载和安装更新文件。
  6. 丰富的生态系统:Electron有着丰富的生态系统,包括许多优秀的开源组件和框架,如Electron-builder、Electron-redux等,可以帮助开发者更高效地创建桌面应用程序。

三、Electron的应用案例

Electron已经被广泛应用于多个知名项目中,包括但不限于:

  • Visual Studio Code:微软推出的跨平台轻量级代码编辑器。
  • Skype:微软推出的跨平台在线通讯工具,支持语音和视频通话。
  • Postman:跨平台的API开发和测试工具。
  • 微信开发者工具:为开发者提供的用于微信小程序开发的开发工具。
  • 钉钉:阿里巴巴推出的企业级通讯与办公工具。
  • 网易云音乐:知名的在线音乐播放平台。
  • 有道翻译:网易推出的翻译工具。
  • Typora:Markdown编辑器。

四、Electron的学习与使用

学习Electron需要掌握一定的Web前端技术、Node.js和npm包管理技术等相关编程语言和工具基础知识。在学习过程中,可以参考官方文档、教程和社区资源,通过实践来加深理解。

使用Electron构建桌面应用程序的基本步骤包括安装Node.js和Electron、创建项目、初始化项目、安装依赖、创建主进程文件和渲染进程文件、打包应用程序、运行和调试应用程序等。

五、Electron的未来发展

随着Web技术的不断发展和普及,Electron作为一个能够将Web技术应用于桌面应用开发的框架,其发展前景是广阔的。它将继续为开发者提供便捷的开发工具和丰富的生态系统支持,助力更多优秀的桌面应用程序的诞生。同时,随着Electron的不断更新和完善,其性能和稳定性也将得到进一步提升。

Javascript深浅拷贝

浅拷贝

浅拷贝只复制对象的第一层属性,如果对象的属性值是基本类型(如String、Number、Boolean、Undefined、Null、Symbol),则直接复制值;如果属性值是引用类型(如Object、Array、Function),则复制的是内存地址,即两个对象指向同一个内存地址,修改其中一个对象的属性会影响到另一个对象。

实现浅拷贝的方法
  • Object.assign()

  • 扩展运算符

  • Array.prototype.slice()

    let obj1 = { a: 1, b: { c: 2 } };
    let obj2 = Object.assign({}, obj1);
    obj2.b.c = 3;
    console.log(obj1.b.c); // 输出 3,说明obj1也被修改了

    let obj1 = { a: 1, b: { c: 2 } };
    let obj2 = { ...obj1 };
    obj2.b.c = 3;
    console.log(obj1.b.c); // 输出 3

    let arr1 = [1, 2, { c: 3 }];
    let arr2 = arr1.slice();
    arr2[2].c = 4;
    console.log(arr1[2].c); // 输出 4

深拷贝

深拷贝会递归复制对象及其所有子属性,确保两个对象在内存中完全独立,修改一个对象的属性不会影响到另一个对象。

实现深拷贝的方法
  1. JSON.parse(JSON.stringify())

这种方法虽然可以实现深拷贝,但有局限性,比如不能复制函数、undefined、Symbol等,并且会忽略对象的原型链。

let obj1 = { a: 1, b: { c: 2 } };  
let obj2 = JSON.parse(JSON.stringify(obj1));  
obj2.b.c = 3;  
console.log(obj1.b.c); // 输出 2
  1. 递归拷贝

通过递归的方式,手动实现深拷贝,可以处理函数、undefined、Symbol等特殊情况,也可以保留对象的原型链。

function deepClone(obj, hash = new WeakMap()) {  
  if (obj === null) return null; // null 的情况  
  if (obj instanceof Date) return new Date(obj); // 日期对象直接返回一个新的日期对象  
  if (obj instanceof RegExp) return new RegExp(obj); // 正则对象直接返回一个新的正则对象  
  // 如果循环引用了就用 weakMap 来解决  
  if (hash.has(obj)) return hash.get(obj);  

  let allDesc = Object.getOwnPropertyDescriptors(obj);  
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);  
  hash.set(obj, cloneObj);  

  for (let key of Reflect.ownKeys(obj)) {  
    cloneObj[key] =  
      (typeof obj[key] === 'object' && obj[key] !== null)  
        ? deepClone(obj[key], hash)  
        : obj[key];  
  }  
  return cloneObj;  
}  

let obj1 = { a: 1, b: { c: 2 } };  
let obj2 = deepClone(obj1);  
obj2.b.c = 3;  
console.log(obj1.b.c); // 输出 2

在JavaScript中,防抖(Debouncing)和节流(Throttling)是两种常用的性能优化技术,特别是在处理如窗口滚动、输入框内容变化、窗口大小调整等高频事件时。它们可以帮助我们减少函数执行的频率,从而提升应用的性能和响应能力。

防抖(Debouncing)

防抖技术的基本原理是:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。这通常用于输入框的搜索联想、窗口大小调整等场景。

function debounce(func, wait) {  
    let timeout;  
    return function() {  
        const context = this,  
              args = arguments;  
        clearTimeout(timeout);  
        timeout = setTimeout(() => {  
            func.apply(context, args);  
        }, wait);  
    };  
}  
  
// 使用示例  
const search = debounce(function(query) {  
    console.log('搜索:', query);  
}, 500);  
  
// 模拟输入框内容变化  
window.addEventListener('keyup', function(e) {  
    search(e.target.value);  
});

节流(Throttling)

节流技术的基本原理是:规定在单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次能生效。这常用于滚动条事件监听、页面重绘等场景。

function throttle(func, limit) {  
    let lastFunc;  
    let lastRan;  
    return function() {  
        const context = this;  
        const args = arguments;  
        if (!lastRan) {  
            func.apply(context, args);  
            lastRan = Date.now();  
        } else {  
            clearTimeout(lastFunc);  
            lastFunc = setTimeout(function() {  
                if ((Date.now() - lastRan) >= limit) {  
                    func.apply(context, args);  
                    lastRan = Date.now();  
                }  
            }, limit - (Date.now() - lastRan));  
        }  
    };  
}  
  
// 使用示例  
const scrollFunc = throttle(function() {  
    console.log('滚动事件触发');  
}, 1000);  
  
window.addEventListener('scroll', scrollFunc);

总结

  • 防抖(Debouncing):确保事件处理函数在最后一次事件触发一定时间后才执行,常用于输入框连续输入、窗口大小调整等场景。
  • 节流(Throttling):确保事件处理函数在特定时间间隔内只执行一次,常用于滚动事件监听、游戏循环等场景。

Jquery的Deferred对象

jQuery 的 Deferred 对象是一种强大的机制,用于处理异步操作和回调函数。它提供了一种方法来注册多个回调函数到某个异步操作完成(或失败)时执行,而不需要将这些回调函数作为参数传递给异步函数本身。这有助于编写更清晰、更易于维护的异步代码,特别是当涉及到多个异步操作需要按特定顺序执行时。

基本概念

  • Deferred 对象 :代表了一个尚未完成但预期将来会完成的操作。它有两个重要的属性:done(), fail(), 和 then() 方法,用于注册回调函数,以及一个 promise 属性,用于返回一个不带 .resolve().reject() 方法的 Deferred 对象,从而允许安全地将操作的处理逻辑暴露给外部代码,而不会破坏内部状态。
  • Promise 对象 :是 Deferred 对象的 promise 属性返回的对象。它提供了 done(), fail(), 和 then() 方法,但不包含用于改变 Deferred 对象状态的 .resolve().reject() 方法。

使用场景

  1. 处理异步操作:如 AJAX 请求,你可以使用 Deferred 对象来注册在请求成功或失败时执行的回调函数。

  2. 链式调用:Deferred 对象允许你链式地注册多个回调函数,这使得你可以轻松地处理多个异步操作,并将它们的结果传递给下一个操作。

  3. 动画队列:虽然 jQuery 的动画系统内部使用了 Deferred,但你也可以使用它来管理自己的动画队列,确保动画按特定顺序执行。

    function ajaxCall(url) {
    // 创建一个 Deferred 对象
    var deferred = $.Deferred();

     $.ajax({  
         url: url,  
         type: "GET",  
         dataType: "json",  
         success: function(data) {  
             // 当 AJAX 请求成功时,解决 Deferred 对象  
             deferred.resolve(data);  
         },  
         error: function(xhr, status, error) {  
             // 当 AJAX 请求失败时,拒绝 Deferred 对象  
             deferred.reject(status + ': ' + error);  
         }  
     });  
    
     // 返回 Promise 对象  
     return deferred.promise();  
    

    }

    // 使用
    ajaxCall('https://api.example.com/data')
    .done(function(data) {
    console.log('Data fetched successfully:', data);
    })
    .fail(function(error) {
    console.error('Failed to fetch data:', error);
    });

Hybrid

hybrid_前端hybrid是什么意思-CSDN博客

前端模块化

前端组件化是一种将复杂的用户界面拆分成一系列独立的、可复用的组件的开发方式。这种方式在现代前端开发中占据了重要地位,带来了诸多优势,但同时也需要注意其潜在的缺点。以下是对前端组件化的详细解析:

一、前端组件化的定义

前端组件化开发,即将页面的某一部分独立出来,将这一部分的数据层(M)、视图层(V)和控制层(C)用黑盒的形式全部封装到一个组件内,暴露出一些开箱即用的函数和属性供外部调用。无论这个组件放到哪里去使用,它都具有一样的功能和样式,从而实现复用(只写一处,处处复用)。这种整体化的思想就是组件化。

二、前端组件化的优势

  1. 代码复用性高:通过定义和使用组件,开发者可以避免重复编写相同的代码,提高开发效率。
  2. 开发效率高:由于组件的独立性,开发者可以并行开发多个组件,从而提高开发并行度。同时,由于组件的可复用性,可以减少大量重复性的工作。
  3. 维护成本低:当某个组件出现问题或需要优化时,只需要修改这个组件的代码,而不需要在整个应用中进行搜索和替换。这大大降低了维护成本。
  4. 组件易于测试:由于组件的独立性,每个组件都可以单独进行单元测试,这有助于确保组件的质量和稳定性。
  5. 灵活性高:组件化开发允许开发者根据需要灵活组合和扩展组件,以满足各种复杂的业务场景。

三、前端组件化的实现方式

在前端组件化开发中,通常会使用一些框架或库来支持组件的开发和使用,例如React、Vue和Angular等。这些框架提供了丰富的组件库和工具,使得开发者可以更加高效地进行组件化开发。以下是一些常见的实现方式:

  1. 基于类的组件化开发方法:通过创建类来定义组件的行为和状态。
  2. 基于函数的组件化开发方法:将组件封装为一个函数,函数接受参数作为组件的属性,然后返回一个表示组件的虚拟DOM。
  3. 基于模板的组件化开发方法:使用模板语言来定义组件的HTML结构。
  4. 基于组合的组件化开发方法:将多个小型组件组合成一个大型组件,通过组件嵌套来创建复杂的页面布局和交互效果。
  5. 基于状态管理的组件化开发方法:通过将组件的状态统一管理来实现组件之间的通信和数据共享。

四、前端组件化的潜在缺点

  1. 学习成本高:对于初学者来说,组件化开发需要掌握一定的概念和技能,包括组件的定义、使用、通信等。
  2. 组件设计复杂:设计一个高质量的组件需要考虑很多因素,如组件的接口设计、状态管理、性能优化等。
  3. 组件间通信开销:虽然组件化开发可以提高代码的复用性,但组件之间的通信也可能带来一定的开销。如果组件之间的通信过于复杂或频繁,可能会影响应用的性能。
  4. 过度组件化:有时候,开发者可能会过度使用组件化开发,将过小的功能或UI元素也拆分成组件,导致代码结构过于复杂和碎片化,反而不利于维护和扩展。

五、总结

前端组件化开发是一种将复杂问题简单化的开发方式,它通过将用户界面拆分成一系列独立的组件,使得开发者可以更加专注于每个组件的实现和优化,从而提高开发效率和质量。然而,在实际应用中,也需要注意其潜在的缺点,并采取相应的措施来避免或减轻这些问题的影响。

相关推荐
寻找沙漠的人20 分钟前
前端知识补充—CSS
前端·css
GISer_Jing32 分钟前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_7482455233 分钟前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v1 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing1 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600951 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600951 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL1 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
2402_857583491 小时前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js
web150850966412 小时前
在uniapp Vue3版本中如何解决webH5网页浏览器跨域的问题
前端·uni-app