#01 js防抖
在 JavaScript 中,防抖(debounce)是一種常用的優(yōu)化技術(shù),用于限制某個函數(shù)在一定時間內(nèi)的觸發(fā)次數(shù)。
一、防抖的概念
當(dāng)用戶進行某些頻繁觸發(fā)的操作時,比如滾動事件、輸入框的輸入事件等,如果直接綁定事件處理函數(shù),可能會導(dǎo)致函數(shù)被頻繁調(diào)用,從而影響性能。防抖的目的就是在用戶操作停止一段時間后,才真正執(zhí)行相應(yīng)的函數(shù),避免不必要的頻繁調(diào)用。
二、實現(xiàn)防抖的方法
以下是一個簡單的防抖函數(shù)的實現(xiàn):
function debounce(func, delay) {
let timer;
return function() {
const context = this;
const args = arguments;
? ?clearTimeout(timer);
? ?timer = setTimeout(() => {
? ? ?func.apply(context, args);
? ?}, delay);
?};
}
這個函數(shù)接受兩個參數(shù):要執(zhí)行的函數(shù)?func
?和延遲時間?delay
。它返回一個新的函數(shù),當(dāng)這個新函數(shù)被調(diào)用時,會先清除之前設(shè)置的定時器,然后重新設(shè)置一個定時器。如果在延遲時間內(nèi)沒有再次調(diào)用這個新函數(shù),定時器到期后就會執(zhí)行原始函數(shù)。
三、使用防抖的示例
假設(shè)我們有一個輸入框,當(dāng)用戶在輸入框中輸入內(nèi)容時,我們希望在用戶停止輸入一段時間后才進行搜索操作。可以這樣使用防抖函數(shù):
html:
<input type="text" id="searchInput">
js:
const input = document.getElementById('searchInput');
function search() {
console.log('Searching...');
}
input.addEventListener('input', debounce(search, 500));
在這個例子中,當(dāng)用戶在輸入框中輸入內(nèi)容時,debounce(search, 500)
?返回的新函數(shù)會被觸發(fā)。如果用戶在 500 毫秒內(nèi)沒有再次輸入,那么?search
?函數(shù)就會被執(zhí)行,進行搜索操作。如果用戶在 500 毫秒內(nèi)繼續(xù)輸入,那么定時器會被清除,重新開始計時。
四、防抖的應(yīng)用場景
搜索框輸入:如上述例子所示,避免在用戶輸入過程中頻繁進行搜索,提高性能。
窗口大小調(diào)整:當(dāng)用戶調(diào)整窗口大小時,可能會觸發(fā)一些需要重新布局或計算的操作。使用防抖可以在用戶停止調(diào)整窗口一段時間后再執(zhí)行這些操作,避免頻繁計算。
按鈕點擊:如果一個按鈕在短時間內(nèi)可能被多次點擊,使用防抖可以確保只有在用戶停止點擊一段時間后才執(zhí)行相應(yīng)的操作,避免重復(fù)執(zhí)行。
#02?js深拷貝
在 JavaScript 中,深拷貝是指創(chuàng)建一個對象的完全獨立的副本,包括對象的所有嵌套屬性和子對象。以下是關(guān)于 JavaScript 中深拷貝的詳細介紹:
一、為什么需要深拷貝
避免數(shù)據(jù)共享:當(dāng)你有一個復(fù)雜的對象,并且希望對其進行修改而不影響原始對象時,深拷貝就非常有用。如果只是進行淺拷貝(例如使用賦值操作或?Object.assign()
),修改副本可能會意外地修改原始對象,因為它們共享相同的嵌套對象引用。
數(shù)據(jù)獨立性:在某些情況下,你可能需要將一個對象傳遞給不同的部分代碼,并且確保這些部分代碼對該對象的修改不會相互影響。深拷貝可以提供這種數(shù)據(jù)獨立性。
二、實現(xiàn)深拷貝的方法
使用遞歸函數(shù):
function deepCopy(obj) {
if (obj === null || typeof obj!== 'object') {
return obj;
? ? }
let copy;
if (Array.isArray(obj)) {
? ? ? copy = [];
for (let i = 0; i < obj.length; i++) {
? ? ? ? copy[i] = deepCopy(obj[i]);
? ? ? }
? ? } else {
? ? ? copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
? ? ? ? ? copy[key] = deepCopy(obj[key]);
? ? ? ? }
? ? ? }
? ? }
return copy;
? }
使用 JSON 序列化和反序列化:
const originalObject = { a: 1, b: { c: 2 } };
const copiedObject = JSON.parse(JSON.stringify(originalObject));
三、應(yīng)用場景
對象克隆:當(dāng)你需要創(chuàng)建一個與現(xiàn)有對象完全相同但獨立的副本時,可以使用深拷貝。例如,在某些數(shù)據(jù)處理場景中,你可能需要對一個對象進行多次操作,但又不想影響原始對象。
參數(shù)傳遞:在函數(shù)調(diào)用中,如果你希望傳遞一個對象的副本而不是原始對象,以避免函數(shù)內(nèi)部的修改影響到外部的對象,可以使用深拷貝來傳遞參數(shù)。
數(shù)據(jù)持久化:當(dāng)你需要將對象保存到本地存儲或數(shù)據(jù)庫時,通常需要進行深拷貝以確保保存的是對象的獨立副本,而不是對原始對象的引用。
總之,深拷貝在 JavaScript 中是一個重要的技術(shù),用于創(chuàng)建對象的獨立副本,避免數(shù)據(jù)共享和意外的修改。在實際應(yīng)用中,根據(jù)具體情況選擇合適的深拷貝方法可以提高代碼的可靠性和可維護性。
#03 js節(jié)流
在 JavaScript 中,節(jié)流(throttle)是一種用于控制函數(shù)執(zhí)行頻率的技術(shù),確保函數(shù)在特定時間間隔內(nèi)最多執(zhí)行一次。
一、節(jié)流的作用
性能優(yōu)化:在一些頻繁觸發(fā)的事件中,如滾動事件、鼠標移動事件、窗口大小調(diào)整事件等,如果直接綁定事件處理函數(shù),可能會導(dǎo)致函數(shù)被頻繁調(diào)用,從而占用大量的計算資源,影響性能。節(jié)流可以限制函數(shù)的執(zhí)行頻率,減少不必要的計算,提高性能。
防止過度觸發(fā):在某些情況下,我們希望函數(shù)在一定時間內(nèi)只執(zhí)行一次,即使事件被頻繁觸發(fā)。例如,在發(fā)送網(wǎng)絡(luò)請求時,我們可能希望在用戶輸入完成后再發(fā)送請求,而不是每次輸入都發(fā)送請求。節(jié)流可以幫助我們實現(xiàn)這種需求,防止函數(shù)被過度觸發(fā)。
二、節(jié)流的實現(xiàn)方法
使用時間戳和定時器:
一種常見的實現(xiàn)節(jié)流的方法是使用時間戳和定時器?;舅悸肥怯涗浬弦淮螆?zhí)行函數(shù)的時間戳,當(dāng)事件觸發(fā)時,判斷當(dāng)前時間與上一次執(zhí)行時間的差值是否大于指定的時間間隔。如果是,則執(zhí)行函數(shù),并更新上一次執(zhí)行時間戳;如果不是,則不執(zhí)行函數(shù)。如果在時間間隔內(nèi)事件再次被觸發(fā),設(shè)置一個定時器,在時間間隔結(jié)束后執(zhí)行函數(shù)。
以下是一個使用時間戳和定時器實現(xiàn)節(jié)流的示例代碼:
function throttle(func, delay) {
let lastTime = 0;
let timer = null;
return function() {
const now = Date.now();
const context = this;
const args = arguments;
if (now - lastTime > delay) {
? ? ? ? func.apply(context, args);
? ? ? ? lastTime = now;
? ? ? } else if (!timer) {
? ? ? ? timer = setTimeout(() => {
? ? ? ? ? func.apply(context, args);
? ? ? ? ? timer = null;
? ? ? ? ? lastTime = Date.now();
? ? ? ? }, delay);
? ? ? }
? ? };
? }
使用定時器和標志位:
另一種實現(xiàn)節(jié)流的方法是使用定時器和標志位?;舅悸肥窃O(shè)置一個標志位,表示函數(shù)是否正在執(zhí)行。當(dāng)事件觸發(fā)時,如果標志位為 false,則執(zhí)行函數(shù),并設(shè)置標志位為 true。在函數(shù)執(zhí)行完成后,設(shè)置標志位為 false。如果在函數(shù)執(zhí)行過程中事件再次被觸發(fā),則不執(zhí)行函數(shù)。為了確保函數(shù)在一定時間間隔內(nèi)至少執(zhí)行一次,可以使用定時器在時間間隔結(jié)束后執(zhí)行函數(shù),并重置標志位。
以下是一個使用定時器和標志位實現(xiàn)節(jié)流的示例代碼:
function throttle(func, delay) {
let isThrottled = false;
let timer = null;
return function() {
const context = this;
const args = arguments;
if (!isThrottled) {
? ? ? ? func.apply(context, args);
? ? ? ? isThrottled = true;
? ? ? ? setTimeout(() => {
? ? ? ? ? isThrottled = false;
? ? ? ? }, delay);
? ? ? } else if (!timer) {
? ? ? ? timer = setTimeout(() => {
? ? ? ? ? func.apply(context, args);
? ? ? ? ? timer = null;
? ? ? ? }, delay);
? ? ? }
? ? };
? }
三、節(jié)流的使用場景
滾動事件處理:在網(wǎng)頁滾動時,可能需要執(zhí)行一些計算或更新操作。如果直接綁定滾動事件處理函數(shù),可能會導(dǎo)致函數(shù)被頻繁調(diào)用,影響性能。使用節(jié)流可以限制函數(shù)在滾動事件中的執(zhí)行頻率,提高性能。
window.addEventListener('scroll', throttle(function() {
// 執(zhí)行滾動事件處理函數(shù)
? }, 200));
鼠標移動事件處理:在某些應(yīng)用中,可能需要根據(jù)鼠標移動的位置進行實時計算或更新。使用節(jié)流可以限制函數(shù)在鼠標移動事件中的執(zhí)行頻率,減少不必要的計算,提高性能。
document.addEventListener('mousemove', throttle(function(event) {
// 執(zhí)行鼠標移動事件處理函數(shù)
? }, 50));
輸入框?qū)崟r搜索:在輸入框中輸入內(nèi)容時,可能需要實時搜索并顯示搜索結(jié)果。使用節(jié)流可以限制搜索函數(shù)的執(zhí)行頻率,避免在用戶輸入過程中頻繁發(fā)送網(wǎng)絡(luò)請求,提高性能。
const input = document.getElementById('searchInput');
? input.addEventListener('input', throttle(function() {
const query = input.value;
// 執(zhí)行搜索函數(shù)
? }, 300));
總之,節(jié)流是一種非常有用的技術(shù),可以在 JavaScript 中控制函數(shù)的執(zhí)行頻率,提高性能,防止過度觸發(fā)。在實際應(yīng)用中,根據(jù)具體情況選擇合適的節(jié)流方法和時間間隔,可以有效地優(yōu)化代碼的性能。
#04 js手寫Promise
以下是用 JavaScript 手寫一個簡單的 Promise:
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach((callback) => callback(this.value));
? ? ?}
? ?};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach((callback) => callback(this.reason));
? ? ?}
? ?};
try {
? ? ?executor(resolve, reject);
? ?} catch (error) {
? ? ?reject(error);
? ?}
?}
?then(onFulfilled, onRejected) {
? ?onFulfilled = typeof onFulfilled === 'function'? onFulfilled : (value) => value;
? ?onRejected = typeof onRejected === 'function'? onRejected : (reason) => { throw reason; };
let promise2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
? ? ? ?setTimeout(() => {
try {
let x = onFulfilled(this.value);
? ? ? ? ? ?resolvePromise(promise2, x, resolve, reject);
? ? ? ? ?} catch (error) {
? ? ? ? ? ?reject(error);
? ? ? ? ?}
? ? ? ?}, 0);
? ? ?} else if (this.state === 'rejected') {
? ? ? ?setTimeout(() => {
try {
let x = onRejected(this.reason);
? ? ? ? ? ?resolvePromise(promise2, x, resolve, reject);
? ? ? ? ?} catch (error) {
? ? ? ? ? ?reject(error);
? ? ? ? ?}
? ? ? ?}, 0);
? ? ?} else {
this.onFulfilledCallbacks.push((value) => {
? ? ? ? ?setTimeout(() => {
try {
let x = onFulfilled(value);
? ? ? ? ? ? ?resolvePromise(promise2, x, resolve, reject);
? ? ? ? ? ?} catch (error) {
? ? ? ? ? ? ?reject(error);
? ? ? ? ? ?}
? ? ? ? ?}, 0);
? ? ? ?});
this.onRejectedCallbacks.push((reason) => {
? ? ? ? ?setTimeout(() => {
try {
let x = onRejected(reason);
? ? ? ? ? ? ?resolvePromise(promise2, x, resolve, reject);
? ? ? ? ? ?} catch (error) {
? ? ? ? ? ? ?reject(error);
? ? ? ? ? ?}
? ? ? ? ?}, 0);
? ? ? ?});
? ? ?}
? ?});
return promise2;
?}
catch(onRejected) {
return this.then(null, onRejected);
?}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
?}
let called = false;
if (x instanceof MyPromise) {
? ?x.then((y) => {
? ? ?resolvePromise(promise2, y, resolve, reject);
? ?}, reject);
?} else if (x!== null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
? ? ? ?then.call(x, (y) => {
if (called) return;
? ? ? ? ?called = true;
? ? ? ? ?resolvePromise(promise2, y, resolve, reject);
? ? ? ?}, (r) => {
if (called) return;
? ? ? ? ?called = true;
? ? ? ? ?reject(r);
? ? ? ?});
? ? ?} else {
? ? ? ?resolve(x);
? ? ?}
? ?} catch (e) {
if (called) return;
? ? ?called = true;
? ? ?reject(e);
? ?}
?} else {
? ?resolve(x);
?}
}
你可以使用這個自定義的 Promise 如下:
let myPromise = new MyPromise((resolve, reject) => {
?setTimeout(() => {
? ?resolve('Success!');
?}, 1000);
});
myPromise.then((value) => {
console.log(value);
return 'Another value';
}).then((value) => {
console.log(value);
});
這個實現(xiàn)雖然簡單,但涵蓋了 Promise 的基本功能,包括異步執(zhí)行、狀態(tài)轉(zhuǎn)換和鏈式調(diào)用。在實際應(yīng)用中,可能需要進一步擴展和優(yōu)化這個實現(xiàn)以滿足更復(fù)雜的需求。
#05 js 異步控制并發(fā)數(shù)
在 JavaScript 中,可以通過多種方式來控制異步操作的并發(fā)數(shù)。以下是一種常見的實現(xiàn)方法:
function asyncFunctionWithConcurrencyLimit(asyncFunctions, concurrencyLimit) {
let inFlightCount = 0;
let results = [];
let index = 0;
function executeNext() {
if (index < asyncFunctions.length && inFlightCount < concurrencyLimit) {
? ? ?inFlightCount++;
const currentIndex = index;
? ? ?index++;
const asyncFunc = asyncFunctions[currentIndex];
? ? ?asyncFunc().then((result) => {
? ? ? ?results[currentIndex] = result;
? ? ? ?inFlightCount--;
? ? ? ?executeNext();
? ? ?}).catch((error) => {
? ? ? ?results[currentIndex] = error;
? ? ? ?inFlightCount--;
? ? ? ?executeNext();
? ? ?});
? ?}
?}
for (let i = 0; i < concurrencyLimit && i < asyncFunctions.length; i++) {
? ?executeNext();
?}
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (results.length === asyncFunctions.length) {
? ? ? ?clearInterval(interval);
? ? ? ?resolve(results);
? ? ?}
? ?}, 100);
?});
}
使用方法如下:
// 模擬一些異步函數(shù)
function asyncTask(index) {
return new Promise((resolve, reject) => {
? ?setTimeout(() => {
? ? ?resolve(`Task ${index} completed`);
? ?}, Math.random() * 2000);
?});
}
});
const tasks = Array.from({ length: 10 }, (_, index) => asyncTask(index));
const concurrencyLimit = 3;
asyncFunctionWithConcurrencyLimit(tasks, concurrencyLimit).then((results) => {
console.log(results);
在這個例子中,asyncFunctionWithConcurrencyLimit
?函數(shù)接受一個異步函數(shù)數(shù)組和一個并發(fā)數(shù)限制作為參數(shù)。它通過控制同時執(zhí)行的異步函數(shù)數(shù)量來確保不會超出并發(fā)限制。當(dāng)一個異步函數(shù)完成時,會自動啟動下一個異步函數(shù),直到所有的異步函數(shù)都完成。最后,它返回一個 Promise,當(dāng)所有異步函數(shù)都完成時,這個 Promise 會被 resolve,結(jié)果數(shù)組包含了所有異步函數(shù)的執(zhí)行結(jié)果。
#06 js繼承
在 JavaScript 中,實現(xiàn)繼承有多種方式。以下是幾種常見的方法:
一、原型鏈繼承
基本原理:
示例代碼:
function Parent() {
? ? this.parentProperty = 'parent value';
? }
? Parent.prototype.parentMethod = function() {
? ? console.log('This is a parent method.');
? };
? function Child() {}
? Child.prototype = new Parent();
? const child = new Child();
? console.log(child.parentProperty); // 'parent value'
? child.parentMethod(); // 'This is a parent method.'
優(yōu)缺點:
二、構(gòu)造函數(shù)繼承
基本原理:
示例代碼:
function Parent(name) {
? ? this.parentProperty = 'parent value';
? ? this.name = name;
? }
? function Child(name) {
? ? Parent.call(this, name);
? ? this.childProperty = 'child value';
? }
? const child = new Child('child name');
? console.log(child.parentProperty); // 'parent value'
? console.log(child.name); // 'child name'
優(yōu)缺點:
三、組合繼承
基本原理:
示例代碼:
function Parent(name) {
? ? this.parentProperty = 'parent value';
? ? this.name = name;
? }
? Parent.prototype.parentMethod = function() {
? ? console.log('This is a parent method.');
? };
? function Child(name) {
? ? Parent.call(this, name);
? ? this.childProperty = 'child value';
? }
? Child.prototype = new Parent();
? Child.prototype.constructor = Child;
? const child = new Child('child name');
? console.log(child.parentProperty); // 'parent value'
? console.log(child.name); // 'child name'
???child.parentMethod();?//?'This?is?a?parent?method.
優(yōu)缺點:
四、寄生組合式繼承
基本原理:
示例代碼:
function inheritPrototype(child, parent) {
? ? const prototype = Object.create(parent.prototype);
? ? prototype.constructor = child;
? ? child.prototype = prototype;
? }
? function Parent(name) {
? ? this.parentProperty = 'parent value';
? ? this.name = name;
? }
? Parent.prototype.parentMethod = function() {
? ? console.log('This is a parent method.');
? };
? function Child(name) {
? ? Parent.call(this, name);
? ? this.childProperty = 'child value';
? }
? inheritPrototype(Child, Parent);
? const child = new Child('child name');
? console.log(child.parentProperty); // 'parent value'
? console.log(child.name); // 'child name'
? child.parentMethod(); // 'This is a parent method.'
優(yōu)缺點:
總之,JavaScript 中的繼承方式各有優(yōu)缺點,開發(fā)者可以根據(jù)實際情況選擇合適的繼承方式。
#07 js數(shù)組排序
在 JavaScript 中,可以使用數(shù)組的?sort
?方法對數(shù)組進行排序。sort
?方法會對數(shù)組進行原地排序,即直接修改原數(shù)組。
一、基本排序
升序排序:
const numbers = [4, 2, 5, 1, 3];
? const sortedNumbers = numbers.sort();
? console.log(sortedNumbers); // [1, 2, 3, 4, 5]
降序排序:
const numbers = [4, 2, 5, 1, 3];
? const sortedNumbers = numbers.sort((a, b) => b - a);
? console.log(sortedNumbers); // [5, 4, 3, 2, 1]
二、對象數(shù)組排序
根據(jù)對象屬性排序:
const people = [
? ? { name: 'Alice', age: 30 },
? ? { name: 'Bob', age: 25 },
? ? { name: 'Charlie', age: 35 }
? ];
? const sortedPeople = people.sort((a, b) => a.age - b.age);
? console.log(sortedPeople);
? // [
? // ? { name: 'Bob', age: 25 },
? // ? { name: 'Alice', age: 30 },
? // ? { name: 'Charlie', age: 35 }
? // ]
多個屬性排序:
const people = [
? ? { name: 'Alice', age: 30 },
? ? { name: 'Bob', age: 25 },
? ? { name: 'Charlie', age: 30 },
? ? { name: 'David', age: 25 }
? ];
? const sortedPeople = people.sort((a, b) => {
? ? if (a.age!== b.age) {
? ? ? return a.age - b.age;
? ? } else {
? ? ? return a.name.localeCompare(b.name);
? ? }
? });
? console.log(sortedPeople);
? // [
? // ? { name: 'Bob', age: 25 },
? // ? { name: 'David', age: 25 },
? // ? { name: 'Alice', age: 30 },
? // ? { name: 'Charlie', age: 30 }
? // ]
三、穩(wěn)定性
穩(wěn)定性的重要性:
JavaScript 的?sort
?方法的穩(wěn)定性:
四、自定義比較函數(shù)
靈活的比較邏輯:
const fruits = ['apple', 'banana', 'cherry', 'date'];
? const sortedFruits = fruits.sort((a, b) => {
? ? if (a.length!== b.length) {
? ? ? return a.length - b.length;
? ? } else {
? ? ? return a.localeCompare(b);
? ? }
? });
? console.log(sortedFruits);
? // ['date', 'apple', 'cherry', 'banana']
處理不同類型的數(shù)據(jù):
JavaScript 的數(shù)組排序功能非常強大,可以通過簡單的方式實現(xiàn)各種排序需求。在使用?sort
?方法時,需要注意比較函數(shù)的返回值規(guī)則,以確保得到正確的排序結(jié)果。同時,根據(jù)具體情況選擇合適的排序算法和比較函數(shù),可以提高排序的效率和穩(wěn)定性。
#08?js 數(shù)組去重
在 JavaScript 中,可以通過以下幾種方法實現(xiàn)數(shù)組去重:
一、使用 ES6 的 Set 和擴展運算符
基本原理:
示例代碼:
const arr = [1, 2, 2, 3, 4, 4, 5];
? const uniqueArr = [...new Set(arr)];
???console.log(uniqueArr);?//?[1,?2,?3,?4,?5]
二、使用 for 循環(huán)和 indexOf
基本原理:
示例代碼:
const arr = [1, 2, 2, 3, 4, 4, 5];
? const uniqueArr = [];
? for (let i = 0; i < arr.length; i++) {
? ? if (uniqueArr.indexOf(arr[i]) === -1) {
? ? ? uniqueArr.push(arr[i]);
? ? }
? }
? console.log(uniqueArr); // [1, 2, 3, 4, 5]
三、使用 forEach 和 includes
基本原理:
示例代碼:
const arr = [1, 2, 2, 3, 4, 4, 5];
? const uniqueArr = [];
? arr.forEach(item => {
? ? if (!uniqueArr.includes(item)) {
? ? ? uniqueArr.push(item);
? ? }
? });
? console.log(uniqueArr); // [1, 2, 3, 4, 5]
四、使用 reduce
基本原理:
示例代碼:
const arr = [1, 2, 2, 3, 4, 4, 5];
? const uniqueArr = arr.reduce((acc, item) => {
? ? if (!acc.includes(item)) {
? ? ? acc.push(item);
? ? }
? ? return acc;
? }, []);
? console.log(uniqueArr); // [1, 2, 3, 4, 5]
五、對象屬性法
基本原理:
示例代碼:
const arr = [1, 2, 2, 3, 4, 4, 5];
? const uniqueArr = [];
? const temp = {};
? arr.forEach(item => {
? ? if (!temp[item]) {
? ? ? temp[item] = true;
? ? ? uniqueArr.push(item);
? ? }
? });
? console.log(uniqueArr); // [1, 2, 3, 4, 5]
這些方法各有優(yōu)缺點,可以根據(jù)具體情況選擇合適的方法。例如,使用 Set 和擴展運算符的方法簡潔高效,但不支持在低版本的瀏覽器中使用;使用 for 循環(huán)和 indexOf 的方法兼容性好,但代碼相對較長。
#09?js 獲取URL參數(shù)
在 JavaScript 中,可以通過以下幾種方式獲取 URL 中的參數(shù):
一、使用正則表達式
基本原理:
示例代碼:
function getUrlParams(url) {
? ? const params = {};
? ? const regex = /[?&]([^=#]+)=([^]*)/g;
? ? let match;
? ? while ((match = regex.exec(url))!== null) {
? ? ? params[match[1]] = decodeURIComponent(match[2]);
? ? }
? ? return params;
? }
? const url = 'https://example.com/page?param1=value1¶m2=value2';
? const params = getUrlParams(url);
? console.log(params); // {param1: "value1", param2: "value2"}
二、使用 URLSearchParams API
基本原理:
示例代碼:
const url = 'https://example.com/page?param1=value1¶m2=value2';
? const urlParams = new URLSearchParams(url.split('?')[1]);
? const params = {};
? for (const [key, value] of urlParams.entries()) {
? ? params[key] = value;
? }
? console.log(params); // {param1: "value1", param2: "value2"}
三、手動解析 URL
基本原理:
示例代碼:
function getUrlParams(url) {
? ? const params = {};
? ? const queryString = url.split('?')[1];
? ? if (!queryString) return params;
? ? const pairs = queryString.split('&');
? ? for (let i = 0; i < pairs.length; i++) {
? ? ? const pair = pairs[i].split('=');
? ? ? const key = decodeURIComponent(pair[0]);
? ? ? const value = decodeURIComponent(pair[1]);
? ? ? params[key] = value;
? ? }
? ? return params;
? }
? const url = 'https://example.com/page?param1=value1¶m2=value2';
? const params = getUrlParams(url);
? console.log(params); // {param1: "value1", param2: "value2"}
這些方法可以根據(jù)具體的需求和環(huán)境選擇使用。如果瀏覽器支持URLSearchParams
?API,那么使用它是一種比較簡潔和現(xiàn)代的方式。如果需要兼容舊版本的瀏覽器,可以使用正則表達式或手動解析 URL 的方法。
#10 js發(fā)布訂閱模式
在 JavaScript 中,發(fā)布訂閱模式是一種常用的設(shè)計模式,它允許對象之間進行松耦合的通信。在發(fā)布訂閱模式中,有三個主要的角色:發(fā)布者、訂閱者和事件中心。
一、發(fā)布訂閱模式的概念
發(fā)布者(Publisher):發(fā)布者是產(chǎn)生事件的對象。當(dāng)發(fā)布者發(fā)生某些事件時,它會通知事件中心。
訂閱者(Subscriber):訂閱者是對特定事件感興趣的對象。訂閱者向事件中心注冊自己,以便在特定事件發(fā)生時得到通知。
事件中心(Event Center):事件中心是發(fā)布者和訂閱者之間的中介。它負責(zé)管理事件的訂閱和發(fā)布,當(dāng)發(fā)布者發(fā)布事件時,事件中心會通知所有訂閱了該事件的訂閱者。
二、發(fā)布訂閱模式的實現(xiàn)
使用對象實現(xiàn):
const eventCenter = {
? ? events: {},
? ? subscribe(eventName, callback) {
? ? ? if (!this.events[eventName]) {
? ? ? ? this.events[eventName] = [];
? ? ? }
? ? ? this.events[eventName].push(callback);
? ? },
? ? publish(eventName, data) {
? ? ? if (this.events[eventName]) {
? ? ? ? this.events[eventName].forEach(callback => callback(data));
? ? ? }
? ? },
? ? unsubscribe(eventName, callback) {
? ? ? if (this.events[eventName]) {
? ? ? ? this.events[eventName] = this.events[eventName].filter(cb => cb!== callback);
? ? ? }
? ? }
? };
使用類實現(xiàn):
class EventEmitter {
? ? constructor() {
? ? ? this.events = {};
? ? }
? ? subscribe(eventName, callback) {
? ? ? if (!this.events[eventName]) {
? ? ? ? this.events[eventName] = [];
? ? ? }
? ? ? this.events[eventName].push(callback);
? ? }
? ? publish(eventName, data) {
? ? ? if (this.events[eventName]) {
? ? ? ? this.events[eventName].forEach(callback => callback(data));
? ? ? }
? ? }
? ? unsubscribe(eventName, callback) {
? ? ? if (this.events[eventName]) {
? ? ? ? this.events[eventName] = this.events[eventName].filter(cb => cb!== callback);
? ? ? }
? ? }
? }
三、發(fā)布訂閱模式的使用
訂閱事件:
ventCenter.subscribe('eventName', (data) => {
? ? console.log(`Received event "${eventName}" with data: ${data}`);
? });
? eventCenter.publish('eventName', 'event data');
取消訂閱事件:
const callback = (data) => {
? ? console.log(`Received event "${eventName}" with data: ${data}`);
? };
? eventCenter.subscribe('eventName', callback);
? eventCenter.unsubscribe('eventName', callback);
四、發(fā)布訂閱模式的優(yōu)點
松耦合:發(fā)布者和訂閱者之間沒有直接的依賴關(guān)系,它們通過事件中心進行通信。這使得代碼更加靈活和可維護。
可擴展性:可以輕松地添加新的發(fā)布者和訂閱者,而不會影響現(xiàn)有的代碼。
事件驅(qū)動:發(fā)布訂閱模式是一種事件驅(qū)動的編程方式,它可以更好地處理異步操作和并發(fā)事件。
五、發(fā)布訂閱模式的缺點
內(nèi)存管理:如果訂閱者沒有正確地取消訂閱事件,可能會導(dǎo)致內(nèi)存泄漏。
調(diào)試困難:由于發(fā)布者和訂閱者之間沒有直接的調(diào)用關(guān)系,調(diào)試起來可能會比較困難。
發(fā)布訂閱模式是一種非常有用的設(shè)計模式,它可以幫助你實現(xiàn)松耦合的通信,提高代碼的可維護性和可擴展性。在使用發(fā)布訂閱模式時,需要注意內(nèi)存管理和調(diào)試問題,以確保代碼的正確性和性能。
該文章在 2024/10/19 12:40:37 編輯過