LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

10个常见的前端js手写功能,你都会了吗 ?

admin
2024年10月18日 23:32 本文热度 498

#01 js防抖

在 JavaScript 中,防抖(debounce)是一种常用的优化技术,用于限制某个函数在一定时间内的触发次数。

一、防抖的概念

当用户进行某些频繁触发的操作时,比如滚动事件、输入框的输入事件等,如果直接绑定事件处理函数,可能会导致函数被频繁调用,从而影响性能。防抖的目的就是在用户操作停止一段时间后,才真正执行相应的函数,避免不必要的频繁调用。

二、实现防抖的方法

以下是一个简单的防抖函数的实现:

function debounce(func, delay) {let timer;return function() {const context = this;const args = arguments;    clearTimeout(timer);    timer = setTimeout(() => {      func.apply(context, args);    }, delay);  };}

这个函数接受两个参数:要执行的函数 func 和延迟时间 delay。它返回一个新的函数,当这个新函数被调用时,会先清除之前设置的定时器,然后重新设置一个定时器。如果在延迟时间内没有再次调用这个新函数,定时器到期后就会执行原始函数。

三、使用防抖的示例

假设我们有一个输入框,当用户在输入框中输入内容时,我们希望在用户停止输入一段时间后才进行搜索操作。可以这样使用防抖函数:

html:

<input type="text" id="searchInput">
js:
const input = document.getElementById('searchInput');function search() {console.log('Searching...');}input.addEventListener('input', debounce(search, 500));

在这个例子中,当用户在输入框中输入内容时,debounce(search, 500) 返回的新函数会被触发。如果用户在 500 毫秒内没有再次输入,那么 search 函数就会被执行,进行搜索操作。如果用户在 500 毫秒内继续输入,那么定时器会被清除,重新开始计时。

四、防抖的应用场景

  1. 搜索框输入:如上述例子所示,避免在用户输入过程中频繁进行搜索,提高性能。

  2. 窗口大小调整:当用户调整窗口大小时,可能会触发一些需要重新布局或计算的操作。使用防抖可以在用户停止调整窗口一段时间后再执行这些操作,避免频繁计算。

  3. 按钮点击:如果一个按钮在短时间内可能被多次点击,使用防抖可以确保只有在用户停止点击一段时间后才执行相应的操作,避免重复执行。


#02 js深拷贝

在 JavaScript 中,深拷贝是指创建一个对象的完全独立的副本,包括对象的所有嵌套属性和子对象。以下是关于 JavaScript 中深拷贝的详细介绍:

一、为什么需要深拷贝

  1. 避免数据共享:当你有一个复杂的对象,并且希望对其进行修改而不影响原始对象时,深拷贝就非常有用。如果只是进行浅拷贝(例如使用赋值操作或 Object.assign()),修改副本可能会意外地修改原始对象,因为它们共享相同的嵌套对象引用。

  2. 数据独立性:在某些情况下,你可能需要将一个对象传递给不同的部分代码,并且确保这些部分代码对该对象的修改不会相互影响。深拷贝可以提供这种数据独立性。

二、实现深拷贝的方法

  1. 使用递归函数:

    • 可以编写一个递归函数来遍历对象的所有属性,并对每个属性进行深拷贝。如果属性是基本类型(如字符串、数字、布尔值等),直接复制值。如果属性是对象或数组,递归地调用深拷贝函数。

    • 以下是一个简单的深拷贝函数的实现:

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;   }
  1. 使用 JSON 序列化和反序列化:

    • 一种简单的方法是将对象转换为 JSON 字符串,然后再将其解析回对象。这种方法在大多数情况下都有效,但对于一些特殊类型的对象(如函数、正则表达式、日期对象等)可能会出现问题。

    • 示例代码:

const originalObject = { a: 1, b: { c: 2 } };const copiedObject = JSON.parse(JSON.stringify(originalObject));

三、应用场景

  1. 对象克隆:当你需要创建一个与现有对象完全相同但独立的副本时,可以使用深拷贝。例如,在某些数据处理场景中,你可能需要对一个对象进行多次操作,但又不想影响原始对象。

  2. 参数传递:在函数调用中,如果你希望传递一个对象的副本而不是原始对象,以避免函数内部的修改影响到外部的对象,可以使用深拷贝来传递参数。

  3. 数据持久化:当你需要将对象保存到本地存储或数据库时,通常需要进行深拷贝以确保保存的是对象的独立副本,而不是对原始对象的引用。

总之,深拷贝在 JavaScript 中是一个重要的技术,用于创建对象的独立副本,避免数据共享和意外的修改。在实际应用中,根据具体情况选择合适的深拷贝方法可以提高代码的可靠性和可维护性。

#03 js节流

在 JavaScript 中,节流(throttle)是一种用于控制函数执行频率的技术,确保函数在特定时间间隔内最多执行一次。

一、节流的作用

  1. 性能优化:在一些频繁触发的事件中,如滚动事件、鼠标移动事件、窗口大小调整事件等,如果直接绑定事件处理函数,可能会导致函数被频繁调用,从而占用大量的计算资源,影响性能。节流可以限制函数的执行频率,减少不必要的计算,提高性能。

  2. 防止过度触发:在某些情况下,我们希望函数在一定时间内只执行一次,即使事件被频繁触发。例如,在发送网络请求时,我们可能希望在用户输入完成后再发送请求,而不是每次输入都发送请求。节流可以帮助我们实现这种需求,防止函数被过度触发。

二、节流的实现方法

  1. 使用时间戳和定时器:

    • 一种常见的实现节流的方法是使用时间戳和定时器。基本思路是记录上一次执行函数的时间戳,当事件触发时,判断当前时间与上一次执行时间的差值是否大于指定的时间间隔。如果是,则执行函数,并更新上一次执行时间戳;如果不是,则不执行函数。如果在时间间隔内事件再次被触发,设置一个定时器,在时间间隔结束后执行函数。

    • 以下是一个使用时间戳和定时器实现节流的示例代码:

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);       }     };   }
  1. 使用定时器和标志位:

    • 另一种实现节流的方法是使用定时器和标志位。基本思路是设置一个标志位,表示函数是否正在执行。当事件触发时,如果标志位为 false,则执行函数,并设置标志位为 true。在函数执行完成后,设置标志位为 false。如果在函数执行过程中事件再次被触发,则不执行函数。为了确保函数在一定时间间隔内至少执行一次,可以使用定时器在时间间隔结束后执行函数,并重置标志位。

    • 以下是一个使用定时器和标志位实现节流的示例代码:

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);       }     };   }

三、节流的使用场景

  1. 滚动事件处理:在网页滚动时,可能需要执行一些计算或更新操作。如果直接绑定滚动事件处理函数,可能会导致函数被频繁调用,影响性能。使用节流可以限制函数在滚动事件中的执行频率,提高性能。

    • 示例代码:

window.addEventListener('scroll', throttle(function() {// 执行滚动事件处理函数   }, 200));
  1. 鼠标移动事件处理:在某些应用中,可能需要根据鼠标移动的位置进行实时计算或更新。使用节流可以限制函数在鼠标移动事件中的执行频率,减少不必要的计算,提高性能。

    • 示例代码:

document.addEventListener('mousemove', throttle(function(event) {// 执行鼠标移动事件处理函数   }, 50));
  1. 输入框实时搜索:在输入框中输入内容时,可能需要实时搜索并显示搜索结果。使用节流可以限制搜索函数的执行频率,避免在用户输入过程中频繁发送网络请求,提高性能。

    • 示例代码:

const input = document.getElementById('searchInput');   input.addEventListener('input', throttle(function() {const query = input.value;// 执行搜索函数   }, 300));

总之,节流是一种非常有用的技术,可以在 JavaScript 中控制函数的执行频率,提高性能,防止过度触发。在实际应用中,根据具体情况选择合适的节流方法和时间间隔,可以有效地优化代码的性能。

#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);});

这个实现虽然简单,但涵盖了 Promise 的基本功能,包括异步执行、状态转换和链式调用。在实际应用中,可能需要进一步扩展和优化这个实现以满足更复杂的需求。

#05 js 异步控制并发数

在 JavaScript 中,可以通过多种方式来控制异步操作的并发数。以下是一种常见的实现方法:

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);  });}

使用方法如下:

// 模拟一些异步函数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 函数接受一个异步函数数组和一个并发数限制作为参数。它通过控制同时执行的异步函数数量来确保不会超出并发限制。当一个异步函数完成时,会自动启动下一个异步函数,直到所有的异步函数都完成。最后,它返回一个 Promise,当所有异步函数都完成时,这个 Promise 会被 resolve,结果数组包含了所有异步函数的执行结果。

#06 js继承

在 JavaScript 中,实现继承有多种方式。以下是几种常见的方法:

一、原型链继承

  1. 基本原理:

    • JavaScript 对象有一个指向其原型对象的内部链接。通过让一个对象的原型指向另一个对象,可以实现继承。

    • 子类型的原型对象是父类型的一个实例,这样子类型就可以访问父类型的属性和方法。

  2. 示例代码:

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.'
  1. 优缺点:

    • 优点:实现简单,可以继承父类的原型属性和方法。

    • 缺点:多个实例对引用类型的原型属性的修改会相互影响;无法向父类构造函数传参。

二、构造函数继承

  1. 基本原理:

    • 在子类型的构造函数中调用父类型的构造函数,并使用 call 或 apply 方法改变 this 的指向,将父类型的属性和方法复制到子类型的实例上。

  2. 示例代码:

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'
  1. 优缺点:

    • 优点:可以向父类构造函数传参;避免了多个实例对引用类型的原型属性的修改相互影响。

    • 缺点:只能继承父类的实例属性和方法,不能继承父类的原型属性和方法。

三、组合继承

  1. 基本原理:

    • 结合原型链继承和构造函数继承的优点,通过在子类型的构造函数中调用父类型的构造函数来继承实例属性,同时让子类型的原型对象指向父类型的实例来继承原型属性和方法。

  2. 示例代码:

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.
  1. 优缺点:

    • 优点:可以继承父类的实例属性和方法,也可以继承父类的原型属性和方法;可以向父类构造函数传参;避免了多个实例对引用类型的原型属性的修改相互影响。

    • 缺点:调用了两次父类构造函数,可能会影响性能。

四、寄生组合式继承

  1. 基本原理:

    • 避免组合继承中调用两次父类构造函数的问题,通过创建一个中间函数来继承父类的原型属性和方法,然后将这个中间函数的实例赋值给子类型的原型对象。

  2. 示例代码:

 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.'
  1. 优缺点:

    • 优点:只调用了一次父类构造函数,性能较好;可以继承父类的实例属性和方法,也可以继承父类的原型属性和方法;可以向父类构造函数传参;避免了多个实例对引用类型的原型属性的修改相互影响。

总之,JavaScript 中的继承方式各有优缺点,开发者可以根据实际情况选择合适的继承方式。

#07 js数组排序

在 JavaScript 中,可以使用数组的 sort 方法对数组进行排序。sort 方法会对数组进行原地排序,即直接修改原数组。

一、基本排序

  1. 升序排序:

    • 如果不提供比较函数,sort 方法会将数组元素转换为字符串,然后按照字典序进行升序排序。对于数字数组,这可能不是期望的结果。

    • 示例代码:

 const numbers = [4, 2, 5, 1, 3];   const sortedNumbers = numbers.sort();   console.log(sortedNumbers); // [1, 2, 3, 4, 5]
  1. 降序排序:

    • 可以通过提供一个比较函数来实现降序排序。比较函数接收两个参数,如果第一个参数应该排在第二个参数之前,则返回一个负数;如果两个参数相等,则返回 0;如果第一个参数应该排在第二个参数之后,则返回一个正数。

    • 示例代码:

 const numbers = [4, 2, 5, 1, 3];   const sortedNumbers = numbers.sort((a, b) => b - a);   console.log(sortedNumbers); // [5, 4, 3, 2, 1]

二、对象数组排序

  1. 根据对象属性排序:

    • 对于包含对象的数组,可以根据对象的某个属性进行排序。

    • 示例代码:

 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 }   // ]
  1. 多个属性排序:

    • 如果需要根据多个属性进行排序,可以在比较函数中依次比较这些属性。

    • 示例代码:

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 }   // ]

三、稳定性

  1. 稳定性的重要性:

    • 排序算法的稳定性是指在排序过程中,如果两个元素相等,它们在排序后的相对位置与排序前保持一致。在某些情况下,稳定性是很重要的,例如当需要对已经按照某个属性排序的数组再按照另一个属性排序时。

  2. JavaScript 的 sort 方法的稳定性:

    • JavaScript 的 sort 方法在默认情况下是稳定的,即当比较函数返回 0 时,两个元素的相对位置不会改变。

四、自定义比较函数

  1. 灵活的比较逻辑:

    • 可以根据具体的需求编写自定义的比较函数,实现各种复杂的排序逻辑。

    • 示例代码:

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']
  1. 处理不同类型的数据:

    • 比较函数可以处理不同类型的数据,例如数字、字符串、对象等。在比较不同类型的数据时,需要根据具体情况进行适当的类型转换和比较。

JavaScript 的数组排序功能非常强大,可以通过简单的方式实现各种排序需求。在使用 sort 方法时,需要注意比较函数的返回值规则,以确保得到正确的排序结果。同时,根据具体情况选择合适的排序算法和比较函数,可以提高排序的效率和稳定性。

#08 js 数组去重

在 JavaScript 中,可以通过以下几种方法实现数组去重:

一、使用 ES6 的 Set 和扩展运算符

  1. 基本原理:

    • Set 是 ES6 新增的数据结构,它类似于数组,但成员的值都是唯一的。

    • 可以将数组转换为 Set,然后再转换回数组,利用 Set 的唯一性实现去重。

  2. 示例代码:

 const arr = [1, 2, 2, 3, 4, 4, 5];   const uniqueArr = [...new Set(arr)];   console.log(uniqueArr); // [1, 2, 3, 4, 5]

二、使用 for 循环和 indexOf

  1. 基本原理:

    • 遍历数组元素,对于每个元素,检查它在新数组中是否已经存在。如果不存在,则将其添加到新数组中。

  2. 示例代码:

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

  1. 基本原理:

    • 与使用 for 循环和 indexOf 的方法类似,但是使用 forEach 方法遍历数组,使用 includes 方法检查元素是否在新数组中。

  2. 示例代码:

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

  1. 基本原理:

    • reduce 是数组的一种高阶函数,它可以对数组中的每个元素进行累积操作。

    • 可以使用 reduce 方法遍历数组,将不重复的元素添加到累积器中。

  2. 示例代码:

 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]

五、对象属性法

  1. 基本原理:

    • 创建一个空对象,将数组元素作为对象的属性名,如果属性不存在,则将元素添加到新数组中。

  2. 示例代码:

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]

这些方法各有优缺点,可以根据具体情况选择合适的方法。例如,使用 Set 和扩展运算符的方法简洁高效,但不支持在低版本的浏览器中使用;使用 for 循环和 indexOf 的方法兼容性好,但代码相对较长。

#09 js 获取URL参数

在 JavaScript 中,可以通过以下几种方式获取 URL 中的参数:

一、使用正则表达式

  1. 基本原理:

    • 通过正则表达式匹配 URL 中的参数部分,然后解析出各个参数的值。

  2. 示例代码:

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

  1. 基本原理:

    • URLSearchParams是一个内置的 JavaScript API,它提供了一种方便的方式来处理 URL 查询参数。

  2. 示例代码:

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

  1. 基本原理:

    • 首先获取 URL 的查询部分,然后通过分割字符串的方式解析出各个参数的值。

  2. 示例代码:

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"}

这些方法可以根据具体的需求和环境选择使用。如果浏览器支持URLSearchParams API,那么使用它是一种比较简洁和现代的方式。如果需要兼容旧版本的浏览器,可以使用正则表达式或手动解析 URL 的方法。

#10 js发布订阅模式

在 JavaScript 中,发布订阅模式是一种常用的设计模式,它允许对象之间进行松耦合的通信。在发布订阅模式中,有三个主要的角色:发布者、订阅者和事件中心。

一、发布订阅模式的概念

  1. 发布者(Publisher):发布者是产生事件的对象。当发布者发生某些事件时,它会通知事件中心。

  2. 订阅者(Subscriber):订阅者是对特定事件感兴趣的对象。订阅者向事件中心注册自己,以便在特定事件发生时得到通知。

  3. 事件中心(Event Center):事件中心是发布者和订阅者之间的中介。它负责管理事件的订阅和发布,当发布者发布事件时,事件中心会通知所有订阅了该事件的订阅者。

二、发布订阅模式的实现

  1. 使用对象实现:

    • 可以使用一个普通的对象来实现事件中心。这个对象有一些方法,用于订阅事件、发布事件和取消订阅事件。

    • 以下是一个使用对象实现发布订阅模式的示例代码:

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);       }     }   };
  1. 使用类实现:

    • 也可以使用一个类来实现事件中心。这个类有一些方法,用于订阅事件、发布事件和取消订阅事件。

    • 以下是一个使用类实现发布订阅模式的示例代码:

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);       }     }   }

三、发布订阅模式的使用

  1. 订阅事件:

    • 订阅者可以使用事件中心的 subscribe 方法来订阅特定的事件。当事件发生时,事件中心会调用订阅者提供的回调函数。

    • 以下是一个订阅事件的示例代码:

ventCenter.subscribe('eventName', (data) => {     console.log(`Received event "${eventName}" with data: ${data}`);   });
  • 发布事件:

    • 发布者可以使用事件中心的 publish 方法来发布事件。事件中心会通知所有订阅了该事件的订阅者。

    • 以下是一个发布事件的示例代码:

   eventCenter.publish('eventName', 'event data');
  1. 取消订阅事件:

    • 订阅者可以使用事件中心的 unsubscribe 方法来取消订阅特定的事件。

    • 以下是一个取消订阅事件的示例代码:

const callback = (data) => {     console.log(`Received event "${eventName}" with data: ${data}`);   };   eventCenter.subscribe('eventName', callback);   eventCenter.unsubscribe('eventName', callback);

四、发布订阅模式的优点

  1. 松耦合:发布者和订阅者之间没有直接的依赖关系,它们通过事件中心进行通信。这使得代码更加灵活和可维护。

  2. 可扩展性:可以轻松地添加新的发布者和订阅者,而不会影响现有的代码。

  3. 事件驱动:发布订阅模式是一种事件驱动的编程方式,它可以更好地处理异步操作和并发事件。

五、发布订阅模式的缺点

  1. 内存管理:如果订阅者没有正确地取消订阅事件,可能会导致内存泄漏。

  2. 调试困难:由于发布者和订阅者之间没有直接的调用关系,调试起来可能会比较困难。


发布订阅模式是一种非常有用的设计模式,它可以帮助你实现松耦合的通信,提高代码的可维护性和可扩展性。在使用发布订阅模式时,需要注意内存管理和调试问题,以确保代码的正确性和性能。


该文章在 2024/10/19 12:40:37 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved