splice
方法是 JavaScript 中数组操作的一个强大工具,用于对数组的内容进行增删改查。它可以从数组中删除元素、添加新元素,或者同时进行删除和添加操作。
语法:
javascriptarray.splice(start, deleteCount, item1, item2, ...);
参数:
start
:表示要修改的起始位置(从 0 开始计数)。如果 start
大于数组长度,则从数组末尾开始添加元素。如果是负数,则表示从数组末尾向前计算的位置。
deleteCount
(可选):表示要删除的元素个数。如果为 0,则不删除任何元素。如果未指定,则删除从 start
开始的所有元素。
item1, item2, ...
(可选):表示要添加到数组中的新元素。如果不指定,则只删除元素,不添加新元素。
返回值:
返回一个包含被删除元素的新数组。如果没有删除元素,则返回一个空数组。
例子:
javascriptlet arr = [1, 2, 3, 4, 5];
let removed = arr.splice(2, 2); // 从索引 2 开始删除 2 个元素
console.log(arr); // 输出 [1, 2, 5]
console.log(removed); // 输出 [3, 4]
javascriptlet arr = [1, 2, 3, 4, 5];
arr.splice(2, 0, 'a', 'b'); // 在索引 2 处不删除任何元素,添加 'a' 和 'b'
console.log(arr); // 输出 [1, 2, 'a', 'b', 3, 4, 5]
javascriptlet arr = [1, 2, 3, 4, 5];
arr.splice(2, 2, 'a', 'b'); // 从索引 2 开始删除 2 个元素,并添加 'a' 和 'b'
console.log(arr); // 输出 [1, 2, 'a', 'b', 5]
通过 splice
方法,你可以灵活地对数组进行操作,包括删除、添加或替换元素。
filter
方法是 JavaScript 中用于筛选数组元素的一个常用方法。它会创建一个新数组,其中包含通过提供的测试函数的所有元素。
语法:
javascriptarray.filter(callback(element, index, array), thisArg);
参数:
callback
:一个用来测试每个元素的函数。返回 true
的元素会被包含在返回的新数组中,返回 false
的则会被过滤掉。这个函数接收以下三个参数:
element
:正在处理的当前元素。index
(可选):正在处理的当前元素的索引。array
(可选):调用 filter
的数组本身。thisArg
(可选):可选的参数。当执行回调函数时,用作 this
的值。
返回值:
返回一个新的数组,包含所有通过测试的元素。如果没有元素通过测试,则返回一个空数组。
例子:
javascriptlet arr = [1, 2, 3, 4, 5, 6];
let evenNumbers = arr.filter(function(element) {
return element % 2 === 0;
});
console.log(evenNumbers); // 输出 [2, 4, 6]
javascriptlet words = ['apple', 'pear', 'banana', 'kiwi'];
let longWords = words.filter(function(word) {
return word.length > 3;
});
console.log(longWords); // 输出 ['apple', 'pear', 'banana']
javascriptlet arr = [10, 20, 30, 40];
let filtered = arr.filter(num => num > 25);
console.log(filtered); // 输出 [30, 40]
使用场景:
null
、undefined
或空字符串)。filter
方法不会修改原数组,而是返回一个新的数组,因此它非常适合用于保持数据的不可变性(immutability)。
在 JavaScript 中,Math.round
、Math.floor
和 Math.ceil
是用于对数字进行不同方式的取整操作的三个常用方法。它们各自的功能如下:
Math.round()
Math.round()
方法用于将数字四舍五入到最接近的整数。
语法:
javascriptMath.round(number);
参数:
number
:要四舍五入的数字。返回值:
返回最接近的整数。如果小数部分等于或大于 0.5,则向上舍入;如果小数部分小于 0.5,则向下舍入。
例子:
javascriptMath.round(4.7); // 返回 5
Math.round(4.4); // 返回 4
Math.round(4.5); // 返回 5
Math.floor()
Math.floor()
方法用于向下取整,即返回小于或等于给定数字的最大整数。
语法:
javascriptMath.floor(number);
参数:
number
:要向下取整的数字。返回值:
返回小于或等于给定数字的最大整数。
例子:
javascriptMath.floor(4.7); // 返回 4
Math.floor(4.4); // 返回 4
Math.floor(-4.7); // 返回 -5
Math.ceil()
Math.ceil()
方法用于向上取整,即返回大于或等于给定数字的最小整数。
语法:
javascriptMath.ceil(number);
参数:
number
:要向上取整的数字。返回值:
返回大于或等于给定数字的最小整数。
例子:
javascriptMath.ceil(4.1); // 返回 5
Math.ceil(4.9); // 返回 5
Math.ceil(-4.1); // 返回 -4
Math.round()
:四舍五入,返回最接近的整数。Math.floor()
:向下取整,返回小于或等于给定数字的最大整数。Math.ceil()
:向上取整,返回大于或等于给定数字的最小整数。在 JavaScript 中,可以通过多种方式从 URL 中提取参数。以下是几种常用的方法:
URLSearchParams
URLSearchParams
是一个用于处理 URL 查询参数的内置对象,非常方便提取和操作 URL 参数。
示例:
假设有以下 URL:
javascriptlet url = "https://example.com?page=2&sort=asc&filter=active";
要提取 URL 中的参数,可以这样做:
javascriptlet params = new URLSearchParams(new URL(url).search);
// 获取特定参数值
let page = params.get('page'); // "2"
let sort = params.get('sort'); // "asc"
let filter = params.get('filter'); // "active"
// 遍历所有参数
for (let [key, value] of params) {
console.log(`${key}: ${value}`);
}
如果不想使用 URLSearchParams
,可以手动解析 URL 查询字符串:
示例:
javascriptlet url = "https://example.com?page=2&sort=asc&filter=active";
function getQueryParams(url) {
let queryString = url.split('?')[1];
if (!queryString) {
return {};
}
return queryString.split('&').reduce((acc, param) => {
let [key, value] = param.split('=');
acc[decodeURIComponent(key)] = decodeURIComponent(value);
return acc;
}, {});
}
let params = getQueryParams(url);
// 获取特定参数值
console.log(params.page); // "2"
console.log(params.sort); // "asc"
console.log(params.filter); // "active"
你还可以使用正则表达式来提取特定的 URL 参数。这种方法比较灵活,但可能需要更复杂的正则表达式来处理不同的情况。
示例:
javascriptlet url = "https://example.com?page=2&sort=asc&filter=active";
function getParamByName(name, url) {
name = name.replace(/[\[\]]/g, "\\$&");
let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
let page = getParamByName('page', url); // "2"
let sort = getParamByName('sort', url); // "asc"
let filter = getParamByName('filter', url); // "active"
URLSearchParams
,因为它是现代浏览器中处理 URL 参数的标准方法,简单且强大。一般有两种数据类型:
浅拷贝:只复制对象或数组的第一层属性,嵌套对象的引用不变。常用方法:Object.assign()
和展开运算符(...
)。引用类型只会拷贝它的内存地址。
深拷贝:递归复制所有层级的属性,生成一个完全独立的新对象。常用方法:JSON.parse(JSON.stringify())
或使用 lodash
的 _.cloneDeep()
。拷贝后的对象与原始对象互不影响。
浅拷贝适用于简单结构,深拷贝适用于需要完全独立副本的复杂结构。
虚拟DOM(Virtual DOM)是JavaScript对象的一个抽象表示,类似于实际的DOM结构。它是UI的轻量级副本,存在于内存中。虚拟DOM的主要目的是优化实际DOM的操作,从而提高性能。
提高性能:
跨平台兼容性:
简化编程模型:
PWA(Progressive Web App,渐进式Web应用)是一种结合了网页和原生应用优点的Web应用程序。PWA旨在通过现代Web技术为用户提供与原生应用类似的体验,同时保留网页的灵活性和可访问性。
渐进式:
响应式:
离线可用:
类似原生应用的体验:
安全性:
推送通知:
自动更新:
可发现性:
是异步编程解决方案,避免回调地狱。是一个构造函数,接受一个函数作为参数,返回promise实例。构建promise时,内部代码立刻执行。
它是个对象,也是个容器,保存着未来才会结束的事件。对象状态不受外界影响,只有异步操作的结果可以决定当前是哪种状态。
三种状态:
状态都是不可逆的。
JSfunction getData(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(`Error: ${xhr.status}`);
}
};
xhr.onerror = function () {
reject('Network Error');
};
xhr.send();
});
}
// 使用示例
getData('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error(error));
JSfunction postData(url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(`Error: ${xhr.status}`);
}
};
xhr.onerror = function () {
reject('Network Error');
};
xhr.send(JSON.stringify(data));
});
}
// 使用示例
postData('https://api.example.com/data', { key: 'value' })
.then(response => console.log(response))
.catch(error => console.error(error));
Promise 值穿透(Promise Value Throttling)是指在 Promise
链中,即使某个 then
或 catch
回调函数中没有显式地返回值,Promise
也会将前一个 then
中的结果值“穿透”到下一个 then
或 catch
回调函数中。
javascriptconst promise = new Promise((resolve, reject) => {
resolve('Initial Value');
});
promise
.then(value => {
console.log(value); // 输出: 'Initial Value'
// 没有返回值,相当于返回 undefined
})
.then(value => {
console.log(value); // 输出: 'Initial Value'
});
第一个 then
:
resolve('Initial Value')
使得 Promise
进入 fulfilled
状态,并将 'Initial Value'
作为参数传递给第一个 then
。then
中虽然没有显式地返回值,但默认返回了 undefined
。值穿透:
then
没有显式返回值(即没有返回 Promise
或其他值),Promise
会将前一个 then
的值(即 'Initial Value'
)继续传递给下一个 then
,形成值的“穿透”。第二个 then
:
then
收到了第一个 then
中的值 'Initial Value'
,并继续处理这个值。值穿透的行为是 JavaScript Promise
的一种默认机制,目的是简化链式调用。它确保即使中间某些 then
或 catch
没有返回值或未对值进行处理,整个链中的后续操作仍然可以接收到上一个有效的返回值。
值穿透在一些特定的应用场景中很有用,例如,你希望在中间处理某些副作用(如日志记录),但不改变或干扰 Promise
链中传递的值。
javascriptpromise
.then(value => {
console.log('Logging:', value); // 记录日志
// 不改变值的传递
})
.then(value => {
console.log('Received:', value); // 依然能接收到原始值
});
在这个例子中,虽然第一个 then
中没有返回值,第二个 then
依然能够接收到最初的 'Initial Value'
,这就是值穿透的作用。
延迟加载:等页面加载完成后再加载JS,有助于提高页面加载速度。
defer
属性defer
属性用于将脚本的执行延迟到整个页面解析完成之后。具有 defer
属性的脚本会按照它们在文档中出现的顺序加载并执行。它适用于不会依赖于 DOM 结构的脚本。
用法:
html<script src="script.js" defer></script>
特点:
async
属性async
属性用于异步加载脚本。脚本会在加载完成后立即执行,而不必等待 HTML 的解析完成。这种方式适合不依赖其他脚本或 DOM 结构的独立脚本。
用法:
html<script src="script.js" async></script>
特点:
通过 JavaScript 动态创建 <script>
元素,并将其添加到 DOM 中,可以实现延迟加载脚本。这种方法通常用于条件加载脚本或在用户触发某个事件后再加载脚本。
用法:
javascriptfunction loadScript(url) {
const script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
}
// 在某个事件发生时加载脚本
loadScript('script.js');
特点:
将 <script>
标签放在 HTML 文档的底部(即 </body>
之前),可以确保页面的主要内容(HTML 和 CSS)在 JavaScript 加载和执行之前先行加载和显示。
用法:
html<!-- HTML 内容 -->
<script src="script.js"></script>
</body>
特点:
懒加载是指只有在需要时才加载脚本,比如当用户滚动到页面的某个部分时或在某个特定事件触发时加载。
用法:
懒加载可以通过动态创建 <script>
标签或使用第三方库(如 IntersectionObserver
)来实现。
javascriptdocument.addEventListener('scroll', function() {
if (document.documentElement.scrollTop > 200) {
loadScript('lazyload.js');
}
});
特点:
在 JavaScript 中,异步编程是处理长时间运行任务(如网络请求、文件读取、计时器等)而不阻塞主线程的关键技术。异步编程可以通过多种方式实现,以下是常见的几种异步编程实现方式:
回调函数是最基础的异步编程方式。一个函数在执行异步操作时,将另一个函数(回调函数)作为参数传递进去,待异步操作完成后,回调函数会被调用。
示例:
javascriptfunction fetchData(callback) {
setTimeout(() => {
const data = "Some data";
callback(data);
}, 1000);
}
fetchData(function(result) {
console.log(result); // 输出: Some data
});
优点:
缺点:
Promise
是 ES6 引入的一种更现代的异步编程方式。Promise
对象代表一个异步操作的最终完成或失败,并返回一个值。
示例:
javascriptfunction fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = "Some data";
resolve(data);
}, 1000);
});
}
fetchData()
.then(result => {
console.log(result); // 输出: Some data
})
.catch(error => {
console.error(error);
});
优点:
then
、catch
和 finally
方法,方便处理异步操作的不同状态。缺点:
async/await
是基于 Promise
的语法糖,提供了更简洁和同步风格的异步代码编写方式,使代码更易读。
示例:
javascriptasync function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Some data");
}, 1000);
});
}
async function main() {
try {
const result = await fetchData();
console.log(result); // 输出: Some data
} catch (error) {
console.error(error);
}
}
main();
优点:
try/catch
。缺点:
await
的位置,可能导致等待非必要的异步操作完成。生成器是另一种控制异步流程的方式,通过 function*
定义生成器函数,并使用 yield
暂停和恢复执行。与异步函数结合使用时,可以通过外部库(如 co
)来实现异步操作。
示例:
javascriptfunction* fetchData() {
const data = yield new Promise((resolve) => {
setTimeout(() => resolve("Some data"), 1000);
});
console.log(data); // 输出: Some data
}
const generator = fetchData();
const promise = generator.next().value;
promise.then(result => {
generator.next(result);
});
优点:
yield
可以让你在异步代码中间暂停执行,写出看起来像同步的代码。缺点:
co
)实现异步操作,且不如 async/await
直观。跟npm一样是一个包管理器,但是具有如下优势:
函数式编程(Functional Programming, FP)是一种编程范式,它强调使用纯函数和不可变数据来构建程序,避免使用共享状态和副作用。函数式编程通常被认为是一种更加声明式的编程方式,重点在于“做什么”而不是“怎么做”。
纯函数(Pure Functions):
示例:
javascriptfunction add(a, b) {
return a + b;
}
add
函数是一个纯函数,它总是返回相同的结果,不依赖或改变外部状态。
不可变性(Immutability):
示例:
javascriptconst arr = [1, 2, 3];
const newArr = arr.concat(4); // 不修改原数组,返回新数组 [1, 2, 3, 4]
函数作为一等公民(First-class Functions):
示例:
javascriptfunction double(x) {
return x * 2;
}
function applyFunction(fn, value) {
return fn(value);
}
console.log(applyFunction(double, 5)); // 输出: 10
函数组合(Function Composition):
示例:
javascriptconst add = x => x + 1;
const multiply = x => x * 2;
const composedFunction = x => multiply(add(x));
console.log(composedFunction(5)); // 输出: 12
声明式编程:
示例:
javascriptconst numbers = [1, 2, 3, 4];
const squared = numbers.map(x => x * 2);
console.log(squared); // 输出: [2, 4, 6, 8]
代码简洁和可读性高:
容易测试和调试:
并发和并行计算的优势:
避免共享状态和副作用:
学习曲线:
性能开销:
复杂度:
CSR(Client-Side Rendering,客户端渲染)和 SSR(Server-Side Rendering,服务器端渲染)是两种常见的网页渲染技术,分别在客户端和服务器端处理 HTML 的生成。它们在处理网页的渲染方式、性能和用户体验上有不同的特点和适用场景。
在客户端渲染中,初始 HTML 文件是由服务器发送到客户端的一个基础模板,通常包含很少的内容(可能只是一个空的 div
容器和脚本标签)。然后,JavaScript 框架(如 React、Vue 或 Angular)在客户端(浏览器)上加载和执行,将数据填充到模板中,生成最终的页面内容。
在服务器端渲染中,HTML 是在服务器上生成的,然后完整的 HTML 页面发送到客户端。客户端收到完整的 HTML 文件后,直接渲染内容。这种方式通常用于传统的多页应用(MPA),以及通过 React、Vue 等框架进行 SSR 的现代应用。
在实际开发中,许多现代应用会结合 CSR 和 SSR 的优点,采用混合渲染策略。例如,使用 SSR 提供快速的初次加载和良好的 SEO,之后在客户端使用 CSR 处理动态交互和页面更新。
闭包(Closure)是 JavaScript 中一个非常重要的概念,也是理解 JavaScript 作用域和函数行为的关键。闭包使得函数可以访问它外部的变量,即使这个函数在它的外部环境中被调用。
闭包是指一个函数可以访问它在声明时所在的词法作用域,即使这个函数是在其词法作用域之外执行。换句话说,闭包是指函数与其词法环境的组合。
闭包形成的条件是:
以下是一个简单的闭包示例:
javascriptfunction outerFunction() {
let outerVariable = 'I am from outer scope';
function innerFunction() {
console.log(outerVariable); // 访问外部函数的变量
}
return innerFunction;
}
const closure = outerFunction(); // 执行外部函数,返回内部函数
closure(); // 调用内部函数,输出:I am from outer scope
在这个示例中:
innerFunction
是 outerFunction
内部定义的一个函数,因此它可以访问 outerFunction
的变量 outerVariable
。outerFunction
已经执行完毕并退出了,但通过闭包,innerFunction
依然保持对 outerVariable
的访问权。数据私有化:
示例:
javascriptfunction counter() {
let count = 0;
return function() {
count += 1;
return count;
};
}
const increment = counter();
console.log(increment()); // 输出: 1
console.log(increment()); // 输出: 2
在这个例子中,count
是一个私有变量,只有通过闭包函数 increment
才能访问和修改它。
回调函数和事件处理:
示例:
javascriptfunction setupEventHandlers() {
let message = 'Hello, World!';
document.getElementById('myButton').addEventListener('click', function() {
alert(message);
});
}
setupEventHandlers();
在这个例子中,事件处理函数访问了外部函数中的变量 message
。
函数工厂:
示例:
javascriptfunction createMultiplier(multiplier) {
return function(value) {
return value * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 输出: 10
console.log(triple(5)); // 输出: 15
在这个例子中,createMultiplier
函数返回一个闭包,每个闭包都有自己的 multiplier
参数,允许创建不同的乘数函数。
闭包是 JavaScript 中的一个强大特性,它允许函数“记住”并访问其词法作用域,即使函数在该作用域之外执行。理解闭包对于编写更健壮、模块化的 JavaScript 代码至关重要,同时也需要注意它可能带来的内存管理和调试问题。
本文作者:Jeff Wu
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!