闭包让你可以在一个内层函数中访问到其外层函数的作用域
前面所有触发都被取消,最后一次执行,在规定时间之后才会触发,也就是说如果连续快速的触发,用户操作频繁,但只会执行一次 。
常用场景:输入框输入
1、lodash的debounce函数
2、 当用户点击按钮时,debounce
包装的 getBtnValue
函数会延迟 3000 毫秒执行。如果在这 3000 毫秒内用户再次点击按钮,那么之前的定时器会被清除,重新开始计时。因此,getBtnValue
函数只会在用户停止点击 3000 毫秒后才执行
function debounce(fn,apply){
let timer;// 初始状态下,timer是undefined
return function(){
// 如果timer有值,清除之前的定时器
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(()=>{
fn.apply(this,args)
},delay)
}
}
function getValue(e){
console.log('1111')
}
const btn = document.createElement('button')
btn.innerHTML = 'btn'
document.body.appendChild(btn)
btn.onclick = debounce(getValue, 3000)
浏览器环境:timer 会被赋值为一个整数,例如 1、2、3 等;Node.js 环境:timer 会被赋值为一个 Timeout 对象。
有规律执行,减少时间执行次数,拖放,滚屏;
只会在第一个点击时执行一次,后续点击将被忽略,直到 delay时间过去后才能再次执行
function throttle(func, delay) {
let timer; // 用于保存定时器标识符
return function() {
if (timer) return; // 如果 timer 已经存在,说明在 delay 时间内已经触发过,直接返回,跳过本次调用
const args = arguments; // 保存当前的参数
const context = this; // 保存当前的执行上下文
// 设置一个定时器,在 delay 毫秒后执行 func
timer = setTimeout(() => {
func.apply(context, args); // 执行原始函数,传递当前上下文和参数
timer = null; // 重置 timer,表示可以再次触发 func
}, delay);
};
}
const btn = document.createElement('button')
btn.innerHTML = 'btn'
document.body.appendChild(btn)
function handleClick() {
console.log('Button clicked!');
}
btn.onclick = throttle(handleClick, 3000);
timer
,设置 delay
毫秒后执行 func
。delay
期间再次点击:由于 timer
存在,函数直接返回,不会再次执行 func
。delay
时间到达:func
被执行,timer
被重置为 null
。timer
并触发 func。
因此,尽管多次点击,只有第一次点击时创建的定时器会生效!!
将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
function curry(fn){
let args = []
return function res(...rest){
if(arguments.length==0){
//当没有参数传递给 res 时于是调用 fn(...args)
return fn(...args)
}else{
args.push(...rest)
return res
}
}
}
var sumFn = function(...arr){
return arr.reduce((prev,cur)=>{
return prev+cur
})
}
let res = curry(sumFn)(1)(2)(3,4)() //15
new
操作符的主要作用是生成一个新对象,并将这个对象与构造函数的原型连接起来,同时构造函数中的代码会在新对象的上下文中执行,给新对象赋予属性和方法。
主要流程是:const person1 = new Person('Alice', 25);
const person1 = {};
person1.__proto__ = Person.prototype;//隐式原型指向构造函数的显示原型
this
:执行 Person
构造函数时,this
被绑定到 person1
。Person
函数中,this.name = name;
将 name
赋值给 person1
。new
操作符会返回 person1
。function Person(a){
// 检查 this 是否是 Person 的实例
//if (!(this instanceof Person)) {
//throw new Error("Person 只能通过 new 关键字调用");
//}
this.a=a
}
function myNew(fn,...args){
const obj={};
obj._proto_=fn.prototype
fn.apply(obj,args)
return obj
}
const obj = myNew(Person,123)
顾名思义:console.log(uniqueArray([1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5, 6]
const uniqueArray = (arr) => {
return [...new Set(arr)]
}
顾名思义:console.log(formatThousands(123456789)); // 输出: 123,456,789
function format(num){
//将数字转为字符串并分割整数和小数部分
let arr = String(num).split(.)
let char = arr[0].split('').reverse()
return char.reduce((pre,cur,curIndex)=>{
if(curIndex+1) %3 ===0 && curIndex !==char.length -1){
return ','+cur+pre
}
return cur+pre
},"")
}
描述:通过使用call/apply方法,可以将一个对象的方法应用到另一个对象上,区别是传入参数一个是参数列表,一个是数组;都是立即执行
Function.prototype.myCall = function(context,...args){
if (typeof this!=="function"){
throw new TypeError("被调用的必须是函数")
}
context||globalThis
//用Symbol来创建唯一的fn,防止名字冲突
const fn = Symbol('key')
// this是调用myCall的函数test,将函数绑定到上下文对象的新属性上
context[fn] = this
const res = context[fn](...args)//hello,world
delete context[fn]
return res
}
const test = {
name:"xxx",
hello: function(){
console.log(`hello,${this.name}!`);
}
}
const obj = {name:"world"};
test.hello.myCall(obj);//hello,world
与call和apply方法不同,bind方法并不会立即执行函数,而是返回一个新函数,可以稍后调用;
且参数可以分多次传入
Function.prototype.myBind = function(thisArg,...args1){
const fn = this;
return function(...args2){
if(typeof fn!=="function"){
throw new TypeError("被调用的是函数")
}
thisArg = thisArg||globalThis;
let uniqueFn = Symbol("fn")
thisArg[uniqueFn] = fn;
//合并参数并调用函数
const res = thisArg[uniqueFn](...args1,...args2)
delete thisArg[uniqueFn]
return res
}
}
function greet(param1,param2) {
console.log(`${param1},${this.name}${param2}`)
}
const boundGreet = greet.myBind(obj,"Hey");
boundGreet("!!")// "Hey, Alice!!"
将多维转为一维
遍历,检查是数组,递归,不是数组扔进去
let arr = [1, [2, 3], [4, [5, 6, [7, 8]]]];
function flatten(arr){
let res = []
let len = arr.length
for (let i=0;i
function flattenObj(){
let res = {}
for (let key in obj){
if(typeof obj[key] ==='object'&& obj[key] !==null){
flatten(res,obj[key],`${key}`)
}else{
res[key]=obj[key]
}
}
function flatten(res,obj,keyname){
for(let key in obj){
if(typeof obj[key] ==='object'&& obj[key] !==null){
flatten(res,obj[key],`${keyname}.${key}`)
}else{
res[`${keyname}.${key}`]=obj[key]
}
}
}
return res
}
const obj = {
a: 1,
b: [1, 2, { c: true }],
c: { e: 2, f: 3 },
g: null,
};
let res = flattenObj(obj)
结果:{
a: 1,
'b.0': 1,
'b.1': 2,
'b.2.c': true,
'c.e': 2,
'c.f': 3,
g: null
}
入参是个由Promise
实例组成的数组,返回值是个promise
,因为可以使用.then,
如果全部成功,状态变为resolved
, 并且返回值组成一个数组传给回调,但凡有一个失败,状态变为rejected
, 并将error
返回给回调
// 添加一个自定义的静态方法
Promise.MyAll = function(promises){
let arr = []
count = 0
return new Promise((resolve,reject)=>{
promises.forEach((item,i)=>{
//将 item 转换为一个 Promise
Promise.resolve(item).then(res=>{
arr[i]=res
count+=1
if(count === promises.length) resolve(arr)
}).catch(reject)
})
})
}
测试
const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 500)
})
Promise.MyAll([p1, p2])
.then(res => console.log(res))//['p1','p2']
.catch(err => console.log(err))
返回状态以最快的那个为准
Promise.MyRace(promises=>{
return new Promise((resolve,reject)=>{
for(const x of promises){
Promise.resolve(x).then(resolve,reject)
}
})
})
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新
例如公司(被观察者)发中秋福利给员工(观察者)
//被观察者
class Subject{
constructor(){
this.observerList = []
}
addObserver(observer){
this.observerList.push(observer)
}
removeObserver(observer){
const index = this.observerList.findIndex(o => o.name===observer.name)
this.observerList.splice(index,1)
}
notifyObservers(message){
const observers = this.observerList;
observers.forEach(observer=>observer.notified(message))
}
}
//观察者
class Observer{
constructor(name,subject){
this.name = name;
if(subject){
subject.addObserver(this)
}
}
notified(message){
console.log(this.name,message)
}
}
测试
const subject = new Subject();
const observerA = new Observer('observerA', subject);
subject.notifyObservers('Hello from subject');//observerA Hello from subject
数据绑定机制通常采用观察者模式。当数据模型变化时,所有绑定了该数据的组件(观察者)都会自动更新。例如,一个购物车系统,当用户添加商品到购物车时,购物车总价会自动更新。这个过程可以通过观察者模式来实现
发布-订阅是一种消息范式,消息的发送者(发布者)不会将消息直接发送给特定的接收者(订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在
例如:公司发快递给消费者
class Publisher {
constructor(name, context) {
this.name = name; // 设置发布者的名称
this.context = context; // 设置发布者所属的 PubSub 实例
}
publish(type, content) {
this.context.publish(type, content); // 调用 PubSub 实例的 publish 方法,发布消息
}
}
class Subscriber {
constructor(name, context) {
this.name = name; // 设置订阅者的名称
this.context = context; // 设置订阅者所属的 PubSub 实例
}
subscribe(type, cb) {
this.context.subscribe(type, cb); // 调用 PubSub 实例的 subscribe 方法,订阅某个消息类型
}
}
class PubSub {
constructor() {
this.messages = {}; // 存储已发布的消息,按消息类型存储
this.listeners = {}; // 存储订阅者回调函数,按消息类型存储
}
publish(type, content) {
const existContent = this.messages[type]; // 检查是否已有该类型的消息
if (!existContent) {
this.messages[type] = []; // 如果没有,初始化一个数组来存储该类型的消息
}
this.messages[type].push(content); // 将消息内容添加到对应类型的消息数组中
}
subscribe(type, cb) {
const existListener = this.listeners[type]; // 检查是否已有订阅者监听该类型的消息
if (!existListener) {
this.listeners[type] = []; // 如果没有,初始化一个数组来存储该类型的订阅者回调函数
}
this.listeners[type].push(cb); // 将订阅者的回调函数添加到对应类型的监听数组中
}
notify(type) {
const messages = this.messages[type]; // 获取该类型的所有已发布的消息
const subscribers = this.listeners[type] || []; // 获取该类型的所有订阅者回调函数
subscribers.forEach((cb, index) => cb(messages[index])); // 将对应的消息传递给订阅者的回调函数
}
}
测试
const TYPE_A = 'music'; // 定义一个消息类型
const pubsub = new PubSub(); // 创建一个 PubSub 实例
const publisherA = new Publisher('publisherA', pubsub); // 创建一个发布者,并与 PubSub 实例关联
publisherA.publish(TYPE_A, 'we are young'); // 发布者发布一条类型为 'music' 的消息,内容是 'we are young'
const subscriberA = new Subscriber('subscriberA', pubsub); // 创建一个订阅者,并与 PubSub 实例关联
subscriberA.subscribe(TYPE_A, res => {
console.log('subscriberA received', res); // 订阅者订阅 'music' 类型的消息,并定义回调函数处理接收到的消息
});
pubsub.notify(TYPE_A); // 通知所有订阅了 'music' 类型的订阅者,调用他们的回调函数并传递消息
事件总线:不同组件之间不直接通信,而是通过事件总线来发布和订阅事件。这使得组件之间高度解耦,组件可以独立发展
Vuex 是 Vue.js 的状态管理模式,主要解决组件之间共享状态时的问题