目录
1、var、let、const之间有什么区别?
2、网页从输入url到页面加载发生了什么?
3、开发过程中存在内存泄露该怎么解决?
4、在页面渲染的过程中,导致加载速度慢的因素有哪些?
5、什么是跨域,一般怎么解决跨域?
6、ES6的新特性有哪些?
7、箭头函数和普通函数之间有什么区别?
8、JS中slice()方法和splice()方法有什么区别?
9、JS中的BOM和DOM有什么区别?
10、JS延迟加载的方式有哪些?
1、var、let、const之间有什么区别?
- var:函数作用域、变量提升(但初始化为 undefined)、允许重复声明。
- let:块作用域、存在暂时性死区、禁止重复声明。
- const:块作用域、存在暂时性死区、禁止重复声明、不可重新赋值(但对象和数组的内容可以修改)。
1. var
var 是在 ECMAScript 1 版本中引入的关键字,用于声明一个变量。
- 作用域:var 声明的变量是函数作用域或全局作用域。如果在函数内部声明,则变量在整个函数内可见;如果在函数外声明,则变量在整个全局作用域内可见。
- 变量提升:var 声明的变量会被提升到其所在作用域的顶部。这意味着你可以在变量声明之前使用它(尽管值是 undefined)。
- 重复声明:允许在同一作用域内多次声明同一个变量。
function example() {
console.log(x); // 输出 undefined(变量提升)
var x = 10;
console.log(x); // 输出 10
}
example();
2. let
let 是在 ECMAScript 6 (ES6) 中引入的关键字,用于声明一个块作用域的变量。
- 作用域:let 声明的变量是块作用域的,即变量仅在声明所在的代码块内可见。
- 变量提升:let 声明的变量也会提升,但在声明之前不能访问(存在暂时性死区,Temporal Dead Zone)。
- 重复声明:在同一作用域内不能多次声明同一个变量。
function example() {
console.log(x); // ReferenceError:
x is not defined(暂时性死区)
let x = 10; console.log(x); // 输出 10
}
example();
if (true) {
let y = 20; console.log(y); // 输出 20
}
console.log(y); // ReferenceError: y is not defined(块作用域)
3. const
const 也是在 ECMAScript 6 (ES6) 中引入的关键字,用于声明一个块作用域的常量。const 声明的变量的值在初始化后不能再被修改。
- 作用域:const 声明的变量是块作用域的,即变量仅在声明所在的代码块内可见。
- 变量提升:const 声明的变量也会提升,但在声明之前不能访问(存在暂时性死区,Temporal Dead Zone)。
- 重复声明:在同一作用域内不能多次声明同一个变量。
- 不可变性:const 声明的变量必须在声明时初始化,且之后不可再赋值。但对于对象和数组来说,其内容是可以改变的。
function example() {
const x = 10;
console.log(x); // 输出 10
x = 20; // TypeError: Assignment to constant variable.
}
example();
if (true) {
const y = 20;
console.log(y); // 输出 20
} console.log(y); // ReferenceError: y is not defined(块作用域)
const obj = { a: 1 };
obj.a = 2; // 允许修改对象的属性
console.log(obj.a); // 输出 2
const arr = [1, 2, 3];
arr.push(4); // 允许修改数组的内容
console.log(arr); // 输出 [1, 2, 3, 4]
2、网页从输入url到页面加载发生了什么?
DNS 解析
TCP 连接
发送 HTTP 请求
服务器处理请求
浏览器接收响应
解析和渲染页面
执行 JavaScript 和处理用户事件等多个阶段。这些过程涉及多个层次的技术和协议,协同工作来实现网页的快速加载和响应。
1. DNS 解析
浏览器首先需要将 URL 转换为服务器的 IP 地址。它会向 DNS(域名系统)服务器发送一个请求来获取该域名对应的 IP 地址。DNS 解析的过程包括以下步骤:
- 浏览器缓存:检查浏览器缓存是否已经有这个域名的 IP 地址。
- 操作系统缓存:如果浏览器缓存没有命中,浏览器会向操作系统查询。
- 路由器缓存:如果操作系统缓存没有命中,查询会被发送到本地网络路由器。
- ISP DNS 缓存:如果路由器缓存没有命中,请求会被发送到 ISP 的 DNS 服务器。
- 递归查询:如果 ISP 的 DNS 服务器也没有命中,会进行递归查询,直到找到权威 DNS 服务器,返回 IP 地址。
2. 建立 TCP 连接
一旦获取到 IP 地址,浏览器会与服务器建立一个 TCP 连接。这个过程包括三次握手:
- 客户端发送 SYN:客户端向服务器发送一个 SYN(同步)数据包,请求建立连接。
- 服务器回复 SYN-ACK:服务器收到请求后,回复一个 SYN-ACK(同步-确认)数据包。
- 客户端回复 ACK:客户端收到服务器的 SYN-ACK 后,再次回复一个 ACK(确认)数据包,连接建立。
3. 发送 HTTP 请求
TCP 连接建立后,浏览器会发送一个 HTTP 请求到服务器,请求页面资源。这个请求包括以下内容:
- 请求行:包括请求方法(GET、POST 等)、URL 和 HTTP 版本。
- 请求头:包括一些元信息,如 Host、User-Agent、Accept 等。
- 请求体:在 POST 请求中包含提交的数据。
4. 服务器处理请求
服务器接收到 HTTP 请求后,会进行处理:
- 服务器检查请求的资源路径。
- 如果请求资源存在,服务器会读取资源并生成一个 HTTP 响应。
- 响应头:包含状态码(如 200 OK)、内容类型(如 text/html)、内容长度等信息。
- 响应体:实际的页面内容(HTML、CSS、JavaScript、图片等)。
5. 浏览器接收响应
浏览器接收到服务器的响应后,会开始处理:
- 解析响应头:浏览器会根据响应头中的信息确定如何处理响应体。
- 解析响应体:浏览器解析 HTML 内容并构建 DOM 树。
- 请求资源:浏览器发现 HTML 中引用的其他资源(如 CSS、JavaScript、图片等),会发起新的 HTTP 请求来获取这些资源。
6. 渲染页面
浏览器依次解析和渲染页面:
- 构建 DOM 树:解析 HTML,生成 DOM(文档对象模型)树。
- 构建 CSSOM 树:解析 CSS,生成 CSSOM(CSS 对象模型)树。
- 构建渲染树:将 DOM 树和 CSSOM 树合并,生成渲染树。
- 布局(回流):计算每个元素的位置和大小。
- 绘制(重绘):将元素绘制到屏幕上。
7. 执行 JavaScript
在构建和渲染过程中,浏览器还会执行 HTML 中的 JavaScript:
- 下载和解析:浏览器下载 JavaScript 文件并进行解析。
- 执行脚本:执行脚本可能会操作 DOM、CSSOM 或者发起新的网络请求。
8. 处理事件
浏览器继续监听和处理用户交互事件,如点击、输入、滚动等。根据用户操作,可能会触发 JavaScript 事件处理程序,导致 DOM 和渲染树的变化,重新布局和重绘页面。
9. 持续加载资源
有些页面可能会在初始加载后继续加载资源(如图片、视频、数据等),浏览器会持续进行这些资源的请求和处理。
3、开发过程中存在内存泄露该怎么解决?
内存泄漏通常是由于未能正确管理引用导致的。通过适当的编码实践,如及时解除不再需要的引用、移除事件监听器、清除定时器等,可以有效地防止内存泄漏。此外,使用浏览器开发者工具进行内存分析也是一种检测和解决内存泄漏的有效方法。
1. 全局变量
问题:过多使用全局变量会导致内存泄漏,因为全局变量一直存在于内存中。
解决方法:
- 尽量减少全局变量的使用,使用局部变量和函数作用域。
- 使用 const 和 let 代替 var,因为它们具有块作用域。
// 避免
var globalVar = 'I am a global variable';
// 使用函数作用域
function doSomething() {
const localVar = 'I am a local variable';
console.log(localVar);
} doSomething();
2. 未解除的事件监听器
问题:注册的事件监听器未被移除,导致内存无法释放。
解决方法:
- 确保在不需要时移除事件监听器。
// 添加事件监听器
const button = document.getElementById('myButton');
function handleClick() {
console.log('Button clicked');
}
button.addEventListener('click', handleClick); // 移除事件监听器
button.removeEventListener('click', handleClick);
3. 闭包
问题:闭包使得内部函数引用外部函数的变量,这些变量不会被垃圾回收。
解决方法:
- 在适当的时机将不再需要的变量置为 null,解除引用。
function createClosure() {
let bigArray = new Array(1000000).fill('*');
return function() {
console.log(bigArray[0]);
}; }
let closure = createClosure(); // 解除引用
closure = null;
4. DOM 引用
问题:JavaScript 中的对象保留对 DOM 元素的引用,导致 DOM 元素不能被垃圾回收。
解决方法:
- 当删除 DOM 元素时,同时移除对该元素的引用。
let element = document.getElementById('myElement'); // 删除 DOM 元素
document.body.removeChild(element); // 解除引用 element = null;
5. 定时器和回调函数
问题:未清除的定时器和回调函数会导致内存泄漏。
解决方法:
- 确保在不需要时清除定时器和回调函数。
let intervalId = setInterval(function() {
console.log('Running...');
}, 1000); // 清除定时器
clearInterval(intervalId);
intervalId = null;
6. 闭包中的大对象
问题:在闭包中保存大型数据结构而不及时释放会导致内存泄漏。
解决方法:
- 在适当的时机将不再需要的对象置为 null。
function processData() {
let largeData = new Array(1000000).fill('*');
return
function() {
console.log(largeData[0]);
}; }
let process = processData(); // 解除引用
process = null;
7. 其他避免内存泄漏的方法
- 使用工具进行内存分析:利用浏览器开发者工具中的内存分析工具(如 Chrome DevTools 的 Memory 面板)检测内存泄漏。
- 避免过多的对象实例:尽量减少不必要的对象实例化,特别是在循环或递归中。
- 优化算法和数据结构:使用适当的数据结构和算法,避免大规模、长时间存在的临时数据。
4、在页面渲染的过程中,导致加载速度慢的因素有哪些?
页面加载速度慢的因素有很多,涉及服务器、网络、资源大小和数量、渲染和解析、缓存策略、HTTP 请求数量、第三方资源、动画和交互、字体优化、传输压缩和渲染策略等多个方面。通过针对性地优化这些因素,可以显著提高页面的加载速度和用户体验。
1. 服务器响应时间慢
原因:
- 服务器负载过高。
- 服务器配置不当。
- 数据库查询缓慢。
解决方法:
- 使用负载均衡分散请求。
- 优化服务器配置和代码。
- 缓存频繁访问的数据。
2. 网络带宽和延迟
原因:
- 用户的网络连接慢。
- 网络延迟高。
解决方法:
- 使用内容分发网络(CDN)加速静态资源的分发。
- 压缩和优化资源大小,减少传输数据量。
3. 资源大小和数量
原因:
- 页面包含大量的图片、视频、JavaScript 和 CSS 文件。
- 资源未进行压缩和优化。
解决方法:
- 压缩图片和视频,使用现代图片格式(如 WebP)。
- 使用工具压缩和合并 JavaScript 和 CSS 文件(如 UglifyJS、CSSNano)。
- 使用 HTTP/2 或更高版本的协议,提高并发加载资源的效率。
4. 页面渲染和解析
原因:
- 复杂的 HTML 结构和深层嵌套的 DOM 元素。
- 大量的 JavaScript 需要在页面加载时执行。
解决方法:
- 优化 HTML 结构,减少不必要的嵌套。
- 将关键渲染路径中的 JavaScript 延迟加载(使用 defer 或 async)。
5. 阻塞的 JavaScript 和 CSS
原因:
- 阻塞的 JavaScript 和 CSS 文件会阻止页面的渲染。
解决方法:
- 将 CSS 放在 中,确保页面渲染时 CSS 可用。
- 将非关键的 JavaScript 放在页面底部,或者使用 defer 或 async 属性加载。
- 内联关键 CSS,减少首屏加载时间。
6. 缓存策略不当
原因:
- 缓存策略不当导致每次访问都需要重新加载资源。
解决方法:
- 使用浏览器缓存、服务端缓存(如 Redis)、CDN 缓存等多级缓存策略。
- 设置适当的缓存头(如 Cache-Control、Expires)。
7. 大量的 HTTP 请求
原因:
- 页面包含大量的 HTTP 请求(如多个图片、脚本和样式文件)。
解决方法:
- 合并 CSS 和 JavaScript 文件,减少请求数。
- 使用 CSS 雪碧图(Sprite)合并小图片。
- 使用 srcset 和 picture 元素优化图片加载。
8. 第三方资源
原因:
- 第三方资源(如广告、分析工具、社交媒体嵌入)加载缓慢。
解决方法:
- 延迟加载或异步加载第三方资源。
- 仅在需要时加载第三方资源。
9. 动画和交互
原因:
- 大量的动画和交互效果可能会导致页面加载缓慢。
解决方法:
- 优化动画和交互效果的实现,尽量使用 CSS 动画而不是 JavaScript 动画。
- 避免在页面加载时执行大量动画。
10. 未优化的字体
原因:
- 自定义字体文件过大,加载时间长。
解决方法:
- 使用现代字体格式(如 WOFF2)。
- 延迟加载非关键字体。
- 使用字体显示策略(如 font-display: swap)确保文本在字体加载前可见。
11. 未启用压缩
原因:
- 未启用 Gzip 或 Brotli 压缩传输文本资源。
解决方法:
- 在服务器上启用 Gzip 或 Brotli 压缩,减少传输文件大小。
12. 复杂的客户端渲染
原因:
- 使用客户端渲染(如 React、Angular、Vue)导致首屏渲染时间长。
解决方法:
- 使用服务器端渲染(SSR)或静态站点生成(SSG)。
- 仅在需要时进行客户端渲染,减少初始 JavaScript 负载。
5、什么是跨域,一般怎么解决跨域?
跨域(Cross-Origin)问题是指当浏览器从一个源(origin)向另一个源发出请求时,由于同源策略(Same-Origin Policy)的限制,导致浏览器拒绝这些请求。同源策略是一种重要的安全机制,用来防止恶意网站读取其他网站的敏感数据。源是由协议、域名和端口号共同组成的,只有当这三者完全一致时,才被认为是同源。
跨域问题是由于同源策略引起的,可以通过多种方式解决,包括 CORS、JSONP、反向代理、服务器端中转和 WebSocket 等。每种方法都有其适用场景和限制,选择合适的解决方案可以有效地解决跨域问题,提升应用的性能和用户体验。
跨域问题的常见解决方法
- CORS(Cross-Origin Resource Sharing)
CORS 是一种机制,它使用额外的 HTTP 头来告诉浏览器允许哪些跨域请求。服务器需要配置响应头来允许跨域请求。
// 在服务器上配置 CORS 头
response.setHeader('Access-Control-Allow-Origin', '*');
// 允许所有来源
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
// 允许的方法
response.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// 允许的头
在服务器端,可以使用中间件来处理 CORS,例如在 Node.js 中使用 cors 中间件:
const express = require('express');
const cors = require('cors');
const app = express(); app.use(cors());
app.get('/data', (req, res) => { res.json({
message: 'This is a CORS-enabled response'
}); }); app.listen(3000, () => {
console.log('Server running on port 3000');
});
- JSONP(JSON with Padding)
客户端:
<script> function handleResponse(data) { console.log(data); } </script>
<script src="https://example.com/data?callback=handleResponse"></script>
服务器端(返回 JSONP 响应):
const express = require('express');
const app = express();
app.get('/data', (req, res) => {
const callback = req.query.callback;
const data = { message: 'This is a JSONP response' };
res.send(`${callback}(${JSON.stringify(data)})`);
}); app.listen(3000, () => {
console.log('Server running on port 3000');
});
- 反向代理
通过配置反向代理服务器,浏览器实际上请求的是同源的代理服务器,再由代理服务器转发请求到目标服务器。这样浏览器认为请求是同源的。
在 Nginx 配置文件中:
server {
listen 80;
server_name example.com;
location /api/ {
proxy_pass http://api.example.com/;
proxy_set_header Host $host;
} }
- 服务器端中转
类似于反向代理,通过服务器中转请求,避免跨域问题。客户端请求本地服务器,然后本地服务器再请求目标服务器,获取数据并返回给客户端。
客户端请求:
fetch('/local-api/data') .then(response => response.json()) .then(data => console.log(data));
服务器端中转:
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/local-api/data', async (req, res) => {
try { const response = await axios.get('http://api.example.com/data');
res.json(response.data);
} catch (error) {
res.status(500).send('Error');
} });
app.listen(3000, () => { console.log('Server running on port 3000'); });
- WebSocket
WebSocket 不受同源策略的限制,可以用来实现跨域通信。它适用于实时通信的场景,但不适用于所有跨域请求。
客户端:
const socket = new WebSocket('ws://example.com/socket');
socket.onmessage = function(event) { console.log(event.data); };
服务器端(Node.js 使用 ws 模块):
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });
wss.on('connection', ws => { ws.send('Welcome to WebSocket server'); });
6、ES6的新特性有哪些?
块级作用域、箭头函数、模板字符串、解构赋值、默认参数、扩展运算符和剩余参数、模块、类、Promise、Symbol 以及新的内置对象方法等。通过使用这些新特性,可以编写出更简洁、高效和可维护的代码。
1. 块级作用域(Block Scope)
- let 和 const 关键字:用于声明具有块级作用域的变量。
{ let a = 10; const b = 20; console.log(a); // 10 console.log(b); // 20 } // console.log(a); // ReferenceError // console.log(b); // ReferenceError
2. 箭头函数(Arrow Functions)
- 简化函数的写法,并且不绑定自己的 this。
const add = (x, y) => x + y; console.log(add(2, 3)); // 5 const sayHello = name => `Hello, ${name}!`; console.log(sayHello('Alice')); // Hello, Alice!
3. 模板字符串(Template Literals)
- 使用反引号 ` 定义的字符串,可以包含嵌入表达式和多行字符串。
const name = 'Alice'; const greeting = `Hello, ${name}!`; console.log(greeting); // Hello, Alice! const multiLineString = `This is a string that spans multiple lines.`; console.log(multiLineString);
4. 解构赋值(Destructuring Assignment)
- 允许从数组或对象中提取值,并将其赋给变量。
// 数组解构 const [a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2 // 对象解构 const {name, age} = {name: 'Alice', age: 25}; console.log(name); // Alice console.log(age); // 25
5. 默认参数(Default Parameters)
- 函数参数可以有默认值。
function multiply(x, y = 1) { return x * y; } console.log(multiply(5)); // 5 console.log(multiply(5, 2)); // 10
6. 扩展运算符(Spread Operator)和剩余参数(Rest Parameters)
- 扩展运算符:用于数组和对象的展开。
- 剩余参数:用于函数参数的收集。
// 扩展运算符 const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; console.log(arr2); // [1, 2, 3, 4, 5] const obj1 = {a: 1, b: 2}; const obj2 = {...obj1, c: 3}; console.log(obj2); // {a: 1, b: 2, c: 3} // 剩余参数 function sum(...numbers) { return numbers.reduce((acc, num) => acc + num, 0); } console.log(sum(1, 2, 3)); // 6
7. 模块(Modules)
- 使用 import 和 export 关键字实现模块化。
// 导出模块 // math.js export const add = (x, y) => x + y; export const subtract = (x, y) => x - y; // 导入模块 // main.js import {add, subtract} from './math'; console.log(add(5, 3)); // 8 console.log(subtract(5, 3)); // 2
8. 类(Classes)
- 引入 class 关键字,提供更加面向对象的语法。
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { return `Hello, my name is ${this.name} and I am ${this.age} years old.`; } } const alice = new Person('Alice', 25); console.log(alice.greet()); // Hello, my name is Alice and I am 25 years old.
9. Promise
- 用于处理异步操作,避免回调地狱。
const fetchData = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Data fetched'); }, 1000); }); }; fetchData().then(data => { console.log(data); // Data fetched }).catch(error => { console.error(error); });
10. Symbol
- 一种新的原始数据类型,用于创建唯一的标识符。
const sym1 = Symbol('description'); const sym2 = Symbol('description'); console.log(sym1 === sym2); // false
11. 新的内置对象方法
- 例如,Object.assign()、Array.from()、Array.find()、Array.includes() 等。
// Object.assign() const target = {a: 1}; const source = {b: 2, c: 3}; const result = Object.assign(target, source); console.log(result); // {a: 1, b: 2, c: 3} // Array.from() const arrayLike = {length: 2, 0: 'a', 1: 'b'}; const arr = Array.from(arrayLike); console.log(arr); // ['a', 'b'] // Array.find() const numbers = [1, 2, 3, 4, 5]; const found = numbers.find(num => num > 3); console.log(found); // 4 // Array.includes() const includes = numbers.includes(3); console.log(includes); // true
7、箭头函数和普通函数之间有什么区别?
箭头函数和普通函数在语法、this 绑定、argume
nts 对象、构造函数功能、作为对象方法的适用性等方面存在明显的区别。选择使用哪种函数取决于具体的使用场景和需求:
- 箭头函数:适用于需要简洁语法和继承父作用域的 this 的场景,不适合作为构造函数或对象的方法。
- 普通函数:适用于需要灵活的 this 绑定、访问 arguments 对象、作为构造函数和对象方法的场景。
1. 语法上的区别
箭头函数的语法更简洁:
// 普通函数 function add(a, b) { return a + b; } // 箭头函数 const add = (a, b) => a + b;
2. this 绑定
这是箭头函数和普通函数之间最重要的区别之一。
- 普通函数:this 关键字的值取决于函数的调用方式。如果函数作为对象的方法调用,this 指向该对象;如果作为普通函数调用,this 指向全局对象(在严格模式下为 undefined)。
const obj = { value: 42, regularFunction: function() { console.log(this.value); // 输出 42 } }; obj.regularFunction(); // 正常工作 const regularFunction = obj.regularFunction; regularFunction(); // 输出 undefined(或 42,如果在非严格模式下)
- 箭头函数:没有自己的 this,它的 this 继承自定义它时所在的作用域。箭头函数中的 this 是静态绑定的,不会因为调用方式的不同而改变。
const obj = { value: 42, arrowFunction: () => { console.log(this.value); // 输出 undefined,因为箭头函数的 this 继承自全局对象 } }; obj.arrowFunction(); // 输出 undefined const anotherObj = { value: 42, arrowFunction: function() { return () => { console.log(this.value); // 输出 42,因为箭头函数的 this 继承自包含它的普通函数 }; } }; anotherObj.arrowFunction()(); // 输出 42
3. arguments 对象
- 普通函数:可以访问 arguments 对象,表示传递给函数的所有参数。
function regularFunction() { console.log(arguments); } regularFunction(1, 2, 3); // 输出 [1, 2, 3]
- 箭头函数:没有 arguments 对象。如果需要,可以使用 rest 参数来获取传递的参数。
const arrowFunction = (...args) => { console.log(args); }; arrowFunction(1, 2, 3); // 输出 [1, 2, 3]
4. 构造函数
- 普通函数:可以作为构造函数使用,使用 new 关键字调用时,会创建一个新的对象实例。
function Person(name) { this.name = name; } const person = new Person('Alice'); console.log(person.name); // 输出 'Alice'
- 箭头函数:不能作为构造函数使用,尝试使用 new 关键字调用会导致错误。
const Person = (name) => { this.name = name; }; // const person = new Person('Alice'); // TypeError: Person is not a constructor
5. 作为方法
- 普通函数:适合用作对象的方法。
const obj = { value: 42, method: function() { console.log(this.value); } }; obj.method(); // 输出 42
- 箭头函数:由于其 this 绑定方式,不适合作为对象的方法,否则 this 不会指向对象本身。
const obj = { value: 42, method: () => { console.log(this.value); // 输出 undefined } }; obj.method();
6. 语法细节
- 普通函数:有多种定义方式,如函数声明、函数表达式、匿名函数等。
// 函数声明 function foo() {} // 函数表达式 const bar = function() {}; // 匿名函数 setTimeout(function() { console.log('Hello'); }, 1000);
- 箭头函数:语法更加简洁,只能作为表达式使用。
const foo = () => {}; const bar = (x, y) => x + y; setTimeout(() => { console.log('Hello'); }, 1000);
8、JS中slice()方法和splice()方法有什么区别?
- slice() 方法:
- 用于提取数组的部分内容,返回一个新数组。
- 不会修改原数组。
- 语法:array.slice(begin, end)
- splice()方法:
- 用于添加、删除或替换数组元素。
- 会修改原数组。
- 语法:array.splice(start, deleteCount, item1, item2, ...)
slice() 方法
slice() 方法用于提取数组的一个部分,并返回一个新的数组,不会修改原数组。
array.slice(begin, end)
参数:
- begin(可选):开始提取的索引(包含该索引)。如果省略,默认为 0。
- end(可选):结束提取的索引(不包含该索引)。如果省略,默认为数组的长度。
返回值:
- 返回一个新数组,包含从 begin 到 end(不包括 end)的元素。
const array = [1, 2, 3, 4, 5]; const slicedArray = array.slice(1, 3); console.log(slicedArray); // [2, 3] console.log(array); // [1, 2, 3, 4, 5] (原数组未改变)
splice() 方法
splice() 方法用于通过删除或替换现有元素或添加新元素来修改数组的内容。它会修改原数组并返回被删除的元素。
array.splice(start, deleteCount, item1, item2, ...)
参数:
- start:开始修改的位置(索引)。
- deleteCount(可选):表示要删除的数组元素的个数。如果省略,则从 start 到数组结尾的所有元素都会被删除。
- item1, item2, ...(可选):要添加到数组的新元素。如果没有指定,则 splice() 仅删除元素。
返回值:
- 返回一个由被删除的元素组成的数组。如果没有元素被删除,则返回空数组。
const array = [1, 2, 3, 4, 5]; // 删除元素 const removedElements = array.splice(1, 2); console.log(removedElements); // [2, 3] console.log(array); // [1, 4, 5] (原数组被修改) // 替换元素 array.splice(1, 1, 'a', 'b'); console.log(array); // [1, 'a', 'b', 5] // 添加元素 array.splice(2, 0, 'x', 'y'); console.log(array); // [1, 'a', 'x', 'y', 'b', 5]
9、JS中的BOM和DOM有什么区别?
- BOM(Browser Object Model)是关于浏览器窗口及其组件的接口,用于控制浏览器窗口和获取浏览器信息。
- DOM(Document Object Model)是关于文档内容的接口,用于访问和操作 HTML 和 XML 文档的结构和内容。
- 主要区别
- 作用范围:
- 主要对象:
- 功能:
BOM(Browser Object Model)
BOM 是指浏览器对象模型,它提供了与浏览器窗口进行交互的方法和接口。BOM 主要用于控制浏览器窗口和进行浏览器级别的操作,而不是操作网页内容。
BOM 的主要对象:
- window:代表浏览器窗口或框架,所有全局对象、函数和变量都是 window 对象的属性。
- navigator:提供关于浏览器的信息,例如浏览器名称、版本、平台等。
- screen:提供关于用户屏幕的信息,例如分辨率、颜色深度等。
- location:提供当前 URL 的信息,并允许改变 URL。
- history:提供对浏览器历史记录的访问,可以在用户访问的页面之间移动。
// 访问和修改当前 URL console.log(window.location.href); // 输出当前页面的 URL window.location.href = 'https://www.example.com'; // 跳转到新的 URL // 获取浏览器信息 console.log(navigator.userAgent); // 输出浏览器的用户代理字符串 // 获取屏幕分辨率 console.log(screen.width); // 输出屏幕的宽度 console.log(screen.height); // 输出屏幕的高度 // 浏览器历史操作 window.history.back(); // 后退到上一页 window.history.forward(); // 前进到下一页
DOM(Document Object Model)
DOM 是指文档对象模型,它提供了访问和操作 HTML 和 XML 文档内容的接口。通过 DOM,可以动态地修改文档的结构、样式和内容。
DOM 的主要对象:
- document:代表整个 HTML 或 XML 文档,是访问和操作网页内容的入口。
- Element:代表 HTML 元素,可以通过 document 对象来访问和操作这些元素。
- Node:代表文档树中的节点,包括元素节点、文本节点、属性节点等。
// 访问 HTML 元素 const element = document.getElementById('myElement'); // 通过 ID 获取元素 const elements = document.getElementsByClassName('myClass'); // 通过类名获取元素 // 修改元素内容 element.textContent = 'Hello, World!'; // 修改元素的文本内容 // 创建新元素并添加到文档中 const newElement = document.createElement('div'); // 创建新元素 newElement.textContent = 'This is a new element.'; // 设置元素内容 document.body.appendChild(newElement); // 将新元素添加到文档中 // 修改元素样式 element.style.color = 'red'; // 修改元素的文本颜色
10、JS延迟加载的方式有哪些?
JavaScript 延迟加载(Lazy Loading)是指在页面加载时推迟加载某些资源或脚本,直到它们真正需要时才进行加载。这种技术可以提高页面的初始加载速度,减少不必要的资源消耗,从而提升用户体验。
1. async 和 defer 属性
- async:异步加载脚本,加载完后立即执行,可能在 HTML 解析完成之前执行,不保证顺序。
<script src="script.js" async></script>
- defer:延迟脚本执行,直到 HTML 解析完成后执行,保证按顺序执行。
<script src="script.js" defer></script>
2. 动态创建脚本标签
function loadScript(url, callback) { const script = document.createElement('script'); script.src = url; script.onload = callback; document.head.appendChild(script); } loadScript('script.js', function() { console.log('Script loaded and executed.'); });
3. Intersection Observer API
用于懒加载图像和其他元素,当元素进入视口时触发加载。
const images = document.querySelectorAll('img.lazy-load'); const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove('lazy-load'); observer.unobserve(img); } }); }); images.forEach(img => { observer.observe(img); });
4. 请求IdleCallback
利用浏览器空闲时间执行低优先级的任务,例如延迟加载脚本。
requestIdleCallback(() => { const script = document.createElement('script'); script.src = 'script.js'; document.body.appendChild(script); });
5. 按需加载(Code Splitting)
利用模块化工具(如 Webpack)实现代码分割,按需加载特定模块。
// 使用 Webpack 的动态导入 import('./module.js').then(module => { module.doSomething(); });
6. 事件驱动的延迟加载
在特定事件发生时加载资源,例如用户点击按钮时。
document.getElementById('loadButton').addEventListener('click', () => { const script = document.createElement('script'); script.src = 'script.js'; document.body.appendChild(script); });
7. 使用第三方库
利用第三方库(如 loadjs、require.js 等)来实现延迟加载和依赖管理。
// 使用 loadjs 库 loadjs('script.js', () => { console.log('Script loaded and executed.'); });
8. 图片懒加载
通过设置 src 属性为占位符图像,实际图像地址存放在 data-src 中,使用 JavaScript 替换。
<img class="lazy" src="placeholder.jpg" data-src="actual-image.jpg" alt="Lazy Image">
const lazyImages = document.querySelectorAll('img.lazy'); const lazyLoad = () => { lazyImages.forEach(img => { if (img.getBoundingClientRect().top < window.innerHeight && img.getBoundingClientRect().bottom > 0) { img.src = img.dataset.src; img.classList.remove('lazy'); } }); }; window.addEventListener('scroll', lazyLoad); window.addEventListener('resize', lazyLoad); window.addEventListener('orientationchange', lazyLoad);