- bind call apply
bind
Function.prototype.myBind = function(context,..args){
const fn = this
return function(...innerArgs){
return fn.apply(context,[...args,...innerArgs])
}
}
call
Function.prototype.myCall = function(context , ...args){
context = context || window
context.fn = this
const res = context.fn(...args)
delete context.fn;
return res
}
apply
Function.prototype.myApply = function(context , argsArray){
context = context || window
context.fn = this;
const res = context.fn(...argsArray)
delete context.fn;
return res
}
- instanceof
function myInstanceof(obj,func){
if(typeof obj !== 'object' || obj === null){
return false
}
let proto = Object.getPrototypeOf(obj)
while(proto !== null){
if(proto === func.prototype){
return true
}
proto = Object.getPrototypeOf(proto)
}
return false
}
- new
function myNew (fn){
let obj = {};
obj._proto_ = fn.prototype;
let result = fn.call(this, ...args)
return typeof result === 'Object' ? result : obj;
}
- 深拷贝
**递归实现深拷贝**
function deepCopy(obj,cache = new WeakMap()){
if(typeof obj !=='object' || obj === null){
return obj
}
//检查缓存,避免循环引用导致无限递归
if(cache.has(obj)){
return cache.get(obj)
}
let copy
if(obj instanceof Date){
copy = new Date(obj)
}else if(obj instanceof RegExp){
copy = new RegExp(obj.source,obj.flags);
}else if(typeof obj === 'function'){
copy = function () {
return obj.apply(this,arguments)
}
}else if(Array.isArray(obj)){
copy = []
cache.set(obj,copy)
for(let i = 0; i < obj.length;i++){
copy[i] = deepCopy(obj[i],cache)
}
}else{
copy = Object.create(Object.getPrototypeOf(obj))
cache.set(obj,copy);
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
copy[key] = deepCopy(obj[key],cache)
}
}
return copy
}
这个深拷贝函数基于递归实现,在对象或数组中进行深度复制。它使用了一个缓存 `cache`(使用 `WeakMap`)来存储已经复制过的对象,避免处理循环引用时的无限递归问题。
在处理特殊类型时:
- 对于日期类型,通过创建一个新的 `Date` 对象来复制。
- 对于正则表达式类型,复制其源字符串和标志。
- 对于函数类型,创建一个新的函数,保留原函数的作用域和参数列表。
- 对于数组类型,创建一个新的空数组,并递归地复制每个元素。
- 对于对象类型,创建一个新对象,并递归地复制每个属性。
使用这个深拷贝函数可以处理包括日期、函数、正则表达式和循环引用的复制:
**非递归实现深拷贝**
可以使用迭代的方式来遍历对象和数组,将每个属性或元素复制到新的对象或数组中。下面是一个非递归实现深拷贝的基本思路:
1. 创建一个栈(或队列),将要拷贝的源对象(或数组)放入栈中。
1. 循环遍历栈,每次弹出一个对象(或数组),并创建一个对应的新对象(或数组)。
1. 遍历源对象(或数组)的属性或元素,将它们复制到新对象(或数组)中,如果属性或元素仍然是对象(或数组),将其入栈。
1. 重复步骤 2 和 3,直到栈为空。
function deepCopy2 (source) {
const stack = [{ source, target: undefined }];
const copy = new Map(); // 用于存放已拷贝过的对象,避免循环引用问题
while (stack.length > 0) {
const { source, target } = stack.pop();
let newTarget = target;
if (!newTarget) {
if (Array.isArray(source)) {
newTarget = [];
} else if (typeof source === 'object' && source !== null) {
newTarget = {};
} else {
newTarget = source;
}
}
if (typeof source === 'object' && source !== null) {
copy.set(source, newTarget); // 记录已拷贝的对象
for (const key in source) {
if (Object.hasOwnProperty.call(source, key)) {
if (copy.has(source[key])) {
newTarget[key] = copy.get(source[key]); // 处理循环引用
} else {
stack.push({ source: source[key], target: newTarget[key] });
}
}
}
}
if (Array.isArray(source)) {
newTarget.length = source.length;
}
}
return copy.get(source);
}
// 示例用法
const obj = {
a: 1,
b: [2, 3, { c: 4 }],
d: { e: 5 }
};
const clonedObj = deepCopy(obj);
console.log(clonedObj);
- 浅拷贝
function shallowCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
const originalObject = { name: "John", age: 30 };
const shallowCopyObject = shallowCopy(originalObject);
- promise promise.all promise.race promise.allSettled
**Promise:**
function MyPromise (executor) {
//初始状态为PENDING
this.status = 'PENDING';
//Promise的结果值
this.value = undefined;
//存储成功回调函数的数组
this.onResolvedCallbacks = []
//存储失败回调函数的数组
this.onRejectedCallbacks = []
//定义resolve函数,用于将promise状态改变为fulfilled
const resolve = (value) => {
//状态只能从PENDING 变为fulfilled
if (this.status === "PENDING") {
this.status = "fulfilled"
this.value = value;
//执行所有已注册的成功回调函数
this.onResolvedCallbacks.forEach((callback) => callback(this.value))
}
};
//定义reject函数,用于将promise状态改变为rejected
const reject = (reason) => {
if (this.status === 'PENDING') {
this.status = 'rejected'
this.value = reason
this.onRejectedCallbacks.forEach((callback) => callback(this.value))
}
};
//执行exeuctor函数,并传入resolve和reject函数作为参数
try {
executor(resolve, reject)
} catch (err) {
// 如果在执行executor函数时捕获到异常,将Promise状态改为rejected
reject(error);
}
}
// then方法用于注册回调函数,并返回一个新的promise对象
MyPromise.prototype.then = function (onResolved, onRejected) {
//创建一个新的promise对象
const newPromise = new MyPromise((resolve, reject) => {
const resolvedHandler = typeof onResolved === "function" ? onResolved : (value) => value;
const rejectedHandler = typeof onRejected === "function" ? onRejected : (reason) => { throw reason; }
//如果promise状态已经是fulfilled,则立即执行成功回调函数
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
const result = resolveHandler(this.value);
resolve(result)
} catch (err) {
reject(err)
}
}, 0)
}
//如果promise状态已经是rejectd,则立即执行失败回调
if (this.status === "rejected") {
setTimeout(() => {
try {
const result = rejectedHandler(this.value)
resolve(result)
} catch (error) {
reject(error)
}
}, 0)
}
//如果promise的状态是pending,则将成功回调和失败回调函数存储起来
if (this.status === "PENDING") {
this.onResolvedCallbacks.push((value) => {
setTimeout(() => {
try {
const result = resolvedHandler(value)
// consoke, log(result)
resolve(result)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
const result = rejectedHandler(reason)
resolve(result)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return newPromise;
}
//实例用法
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("Hello, Promise!");
}, 1000);
});
promise
.then((value) => {
console.log(value);
return "New Value";
})
.then((newValue) => {
console.log(newValue);
});
**Promise.allSettled**
function allSettled(promises){
return new Promise((resolve) => {
const results = []
let completedCount = 0
function checkCompletion(){
if(completedCount === promises.length){
resolve(results)
}
}
promises.forEach((promise,index) => {
Promises.resolve(promise).then((value) =>{
results[index] = {status: 'fulfilled' , value};
completedCount++
checkCompletion()
}).catch((reason)=>{
results[index] = {status: "rejected",reason};
completedCount++
checkCompletion()
})
})
})
}
上述代码中,`allSettled` 函数接受一个由 Promise 对象组成的数组 `promises`,并返回一个新的 Promise 对象。
在内部实现中,我们创建了一个空数组 `results` 来存储每个传入 Promise 的状态和结果。使用 `completedCount` 变量来记录已完成的 Promise 数量。
然后,我们使用 `forEach` 方法遍历 `promises` 数组中的每个 Promise。对于每个 Promise,我们使用 `Promise.resolve` 包装它,以确保它始终是一个 Promise 对象。
对于每个包装后的 Promise,我们使用 `then` 方法添加一个成功处理程序,将结果存储在 `results` 数组中,并递增 `completedCount`。然后,我们检查是否所有 Promise 都已完成,如果是,则通过 `resolve` 将结果传递给返回的 Promise。
如果 Promise 失败,我们使用 `catch` 方法添加一个拒绝处理程序,将原因存储在 `results` 数组中,并递增 `completedCount`。然后,同样检查是否所有 Promise 都已完成,如果是,则通过 `resolve` 将结果传递给返回的 Promise。
这样,我们就实现了一个简单的 `Promise.allSettled` 函数,它返回一个 Promise 对象,该对象在所有输入的 Promise 对象都已解决(无论成功还是失败)后才会被解决,返回的结果是一个包含每个 Promise 结果的数组,每个结果对象包含 `status` 表示状态("fulfilled" 表示成功,"rejected" 表示失败)以及对应的 `value` 或 `reason`。
**实现Promise.retry**
三个参数版本:
function promiseRetry(promiseFn,maxAttempts,delay = 0){
return new Promise((resolve ,reject) => {
let attempts = 0;
function attempt(){
attempts++
if(attempts > maxAttempts){
reject(new Error('已经达到最大尝试次数'))
return;
}
promiseFn().then(resolve).catch(() => {
if(delay> 0){
setTimeout(attempt,delay)
}else{
attempt()
}
})
}
attempt()
})
}
//调用示例
// 模拟一个可能失败的异步操作
function someAsyncOperation () {
return new Promise((resolve, reject) => {
// 随机成功或失败
if (Math.random() < 0.5) {
resolve('Success');
} else {
reject(new Error('Failed'));
}
});
}
// 使用 promise.retry 进行重试
promiseRetry(someAsyncOperation, 3, 1000)
.then((result) => {
console.log('Operation succeeded:', result);
})
.catch((error) => {
console.log('Operation failed after maximum retry attempts:', error.message);
});
以上代码定义了一个 `promiseRetry` 函数,它接受三个参数:`promiseFn`(要执行的函数,返回一个 Promise)、`maxAttempts`(最大尝试次数)和可选的 `delay`(每次尝试之间的延迟时间,默认为 0)。
在内部实现中,我们使用了递归调用的方式来重试执行 `promiseFn`,直到达到最大尝试次数或成功执行为止。
如果 `promiseFn` 在执行过程中抛出错误,则将根据延迟时间 `delay` 决定是否延迟一段时间后进行下一次尝试。如果 `delay` 大于 0,则使用 `setTimeout` 设置延迟;否则立即进行下一次尝试。
如果达到最大尝试次数 `maxAttempts` 仍然没有成功执行,将会返回一个被拒绝的 Promise,并带有一个包含 "Maximum retry attempts reached" 错误信息的错误对象。
只接受两个参数
function promiseRetry(promiseFn,maxAttempts){
return new Promise((resolve,reject)=>{
let attempts = 0
function attempt(){
attempts++
if(attemps > maxAttempts){
reject('已达到最大次数')
return;
}
promiseFn().then(resolve).catch(attempt)
}
attempt()
})
}
//调用示例
function someAsyncOperation () {
return new Promise((resolve, reject) => {
// 随机成功或失败
if (Math.random() < 0.5) {
resolve('Success');
} else {
reject(new Error('Failed'));
}
});
}
// 使用 promise.retry 进行重试
promiseRetry(someAsyncOperation, 3)
.then((result) => {
console.log('Operation succeeded:', result);
})
.catch((error) => {
console.log('Operation failed after maximum retry attempts:', error.message);
});
上述代码中,`promiseRetry` 函数仅接收两个参数:`promiseFn`(要执行的函数,返回一个 Promise)和 `maxAttempts`(最大尝试次数)。
在内部实现中,我们仍然使用递归调用的方式来重试执行 `promiseFn`,直到达到最大尝试次数或成功执行为止。
如果 `promiseFn` 在执行过程中抛出错误,则会立即进行下一次尝试,而无需延迟。
如果达到最大尝试次数 `maxAttempts` 仍然没有成功执行,将会返回一个被拒绝的 Promise,并带有一个包含 "Maximum retry attempts reached" 错误信息的错误对象。
promise.all()
接受一个包含多个 Promise 的可迭代对象(如数组),并返回一个新的 Promise,该 Promise 在所有输入 Promise 都解决(resolve)时才解决,如果有一个 Promise 被拒绝(reject),则整个 Promise.all 也会被拒绝。
function myPromiseAll(promises){
return new Promise((resolve,reject) => {
const results = [];
let promisesLen = promises.length
if(promisesLen === 0){
resolve(results)
return;
}
promises.forEach((promise,index) => {
promise.then((res) =>{
results[index] = res
promisesLen--
if(promisesLen === 0){
resolve(results)
}
}).catch((err) => {
reject(err)
})
})
})
}
Promise.race
接受一个包含多个 Promise 的可迭代对象,返回一个新的 Promise,该 Promise 会在第一个输入 Promise 解决或拒绝时解决或拒绝。
function myPromiseRace(promises){
return new Promise((resolve,reject) => {
promises.forEach((promise) =>{
promise.then((result) =>{
resolve(result)
}).catch((err) =>{
reject(err)
})
})
})
}
Promise.any
接受一个包含多个 Promise 的可迭代对象,返回一个新的 Promise,该 Promise 会在第一个被解决的输入 Promise 解决时解决,如果所有输入 Promise 都被拒绝,则 Promise.any 也会被拒绝
function myPromiseAny(promises){
return new Promise((resolve,reject) =>{
let errors = []
promises.forEach((promise)=>{
promise.then((res)=> {
resolve(res)
}).catch((err) => {
errors.push(err)
if(errors.length === promises.length){
reject(new Error(errors , '所有promises都被拒绝'))
}
})
})
})
}
promise.allSettled
接受一个包含多个 Promise 的可迭代对象,返回一个新的 Promise,该 Promise 在所有输入 Promise 都解决或拒绝后解决,返回一个包含每个 Promise 结果的对象数组。
function myPromiseAllSettled(promises){
return new Promise((resolve) => {
const results = []
let promisesLen = promsies.length
if(promisesLen === 0){
resolve(results)
return;
}
promises.forEach((promise,index ) => {
promise.then((res) => {
results[index] = {status: "fulfilled",value: result}
}).catch((err) =>{
results[index] = {status:"rejected",reason: err}
}).finally(() =>{
promisesLen--
if(promisesLen === 0){
resolve(results)
}
})
})
})
}
- promise并发池
Promise并发池的作用是当有大量Promise需要执行时,可以确保同时执行的Promise数量不超过设置的最大并发数,并且可以在某个Promise状态改变后自动执行新的Promise,即维持并发数始终为最大值(除去开始和结束阶段)。
两种实现思路:
Promise递归解法思路:
1. 每层递归都往 run 里加 请求
1. 递归过程中,如果 run 达到临界,使用 Promise.race 来触发 递归。
1. 请求的 回调为从 run 中删除 自身
1. 递归终点是 所有的 request 都已经加完,返回 resolve。此时,就像盗梦空间一样,这个resolve 会不断的被返回。(此刻,请求并没有结束)。最外层会接受这个resolve,使用Promise.all 等待所有请求结束,执行回调函数。
function limitPromise(requesets = [],max = 1,callback = () =>{}){
let run = []
let next = 0
let doit = () =>{
if(next === requests.length){ //递归终止条件
return Promise.resolve()
}
let thisRequest = requests[next++]
run.push(thisRequest.then(requestRes =>{//将请求推入到run数组
console.log(requestRes)
//删除自己
run.splice(run.indexOf(thisRequest),1)
}))
let res = null
if(run.length === max){
res = Promise.race(run)//需要等待一个请求到达
}else{
res = Promise.resolve()//不需要等待
}
return res.then(() => doit())
}
doit().then(()=> {
Promise.all(run).then(() => callback())
})
}
let request = (delay, id) => { return new Promise((resolve) => { setTimeout(resolve, delay, id) }) }
let test_requests = [ request(6000, 1), request(3000, 2), request(4000, 3), request(6000, 4), request(1000, 5), request(2000, 6), ]
limitPromise(test_requests, 3, () => console.log("requeset end"))
升级版 使用async 、await
递归算法都有迭代写法。实际上这个题目,使用迭代更好理解。 这里使用await语法来实现 run 满的等待。 await promise 会暂停函数执行,直到 promise 成功。 这里的逻辑非常简单,就是便利请求数组,把它们加到run 中。但是,由于max的存在,需要在 run.length === max 时,使用await等待,race 到达。同样,添加完后,需要等待 run 里所有请求结束,然后执行回调函数。
async function limitAsync(requests = [],max = 1,callback =() => {}){
let run = [],i = 0
for(const requests of requests){
run.push(request.then((requesetRes) =>{ //依次推入到run
console.log(requestRes)
run.splice(fun.indexOf(request),1)
})
if(run.length === max){
console.log('wait)
await Promise.race(run)
}
}
Promise.all(run).then(()=> callback())
}
limitPromise(test_requests, 3, () => console.log("requeset end"))
- 圣杯布局
- JS阻塞方式实现异步任务队列
有个需求,需要实现一个异步任务队列,并依次处理队列中的所有任务,具体如下:
1. 随机时间增加异步任务到队列中
1. 队列中的任务按照先进先出的规则依次执行
1. 任务为异步请求,等一个执行完了再执行下一个
<body>
<button onclick="clickNe()">点击</button>
</body>
//异步请求队列
const queue = []
//用于模拟不同的返回值
let index = 0
//标志是否正在处理队列中的请求
let running = false
//使用setTimeout模拟异步请求
function request(index){
return new Promsie(function(resolve) {
setTimeout(()=> {
resolve(index)
}, 1000)
})
}
//连续点击触发异步请求,加入任务队列
function clickMe(){
addQueue(() => request(index++))
}
//当队列中任务数大于0,开始处理队列中的人物
function addQueue(item){
queue.push(item)
if(queue.length > 0 && !running){
running = true
process()
}
}
function process(){
const item = queue.shift()
if(item){
item().then(res =>{
console.log('已经处理事件' + res)
process()
})
}else{
running = false
}
}
- 轮播图
- 发布订阅
class EventEmitter{
constructor(){
this.event = {}
}
on(type,cb){
if(!this.event[type]){
this.event[type] = [cb]
}else{
this.event[type].push(cb)
}
}
once(type,cb){
let fn = () => {
cb()
this.off(type,fn)
}
this.on(type,fn)
}
emit(type,...args){
if(!this.event[type]){
return
}else{
this.event[type].forEach(cb =>{
cb(...args)
})
}
}
off(type,cb){
if(!this.event[type]){
return
}else{
this.event[type] = this.event[type].filter(item => item !== cb)
}
}
}
- 超时重传
function fun(){
let n = Math.random()
return new Promise((resolve,reject) => {
settimeout(() =>{
if(n > 0.7){
resolve(n)
}else{
reject(n)
}
},500)
})
}
function retry(fun,times){
new Promsie(async (resolve,reject) => {
while(times--){
try{
const res = await fun()
resolve(res)
console.log('成功',res)
break
} catch(err){
console.log('失败一次',err)
if(!times){
reject(err)
}
}
}
}).catch(() => {
console.log(失败了)
})
}
- promise红绿灯
function green(){
console.log('green')
}
function yellow(){
console.log('yellow')
}
function red(){
console.log('red')
}
const light = function(timer , cb){
return new Promise((resolve,reject) =>{
settimeout(() => {
cb()
resolve()
} , timer)
})
}
const step = function(){
Promise.resolve().then(()=>{
return light(300,yellow)
}).then(() =>{
return light(300,green)
}).then(() => {
return light(300,red)
}).then(() =>{
return step
})
}
11.拖拽元素 12.实现reduce,flat
reduce
Array.prototype.myReduce = function(callback,initValue){
if(this.length === 0 && initValue === undefined){
throw new Error("error")
}
let acc = initValue !== undefined ? initValue : this[0]
for(let i = initValue !== undefiend ? 0 : 1;i <this.length;i++){
acc = callback(acc . this[i] , i , this)
}
return acc
}
flat
不指定层级
Array.prototype.myFlat1 = function() {
const result = []
this.forEach(item => {
if(Array.isArray(item)){
result.push(...item.myFlat())
}else{
result.push(item)
}
})
return result
}
指定层级
Array.prototype.myFlat2 = function(depth = 1){
if(depth === 0){
return this.slice()
}
const result = []
this.forEach(item =>{
if(Array.isArray(item)){
result.push(...item.myFlat2(depth - 1))
}else{
result.push(item)
}
})
return result
}
13.排序算法
冒泡排序
function bubbleSort(arr){
const n = arr.length;
for(let i = 0; i < n - 1;i++){
for(let j = 0; j <n - i - 1;j++){
if(arr[j] > arr[j+1){
[arr[j] , arr[j+1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
// 时间复杂度:最好情况 O(n),平均情况 O(n^2),最坏情况 O(n^2) // 空间复杂度:O(1)
插入排序
function insertSort(arr){
const n = arr.length
for(let i = 1;i < n;i++){
let current = arr[i]
let j = i - 1
while(j >= 0 && arr[j] < current){
arr[j+ 1]= arr[j]
j--
}
arr[j + 1] = current
}
}
时间复杂度:最好情况 O(n),平均情况 O(n^2),最坏情况 O(n^2) // 空间复杂度:O(1)
快速排序
function quickSort(arr){
if(arr.length <= 1){
return arr
}
const first = arr[0]
const left = [] , right = []
for(let i = 1; i < arr.length;i++){
if(arr[i] < first){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
return [...quickSort(left),first,...quickSort(right)]
}
/ 时间复杂度:最好情况 O(n log n),平均情况 O(n log n),最坏情况 O(n^2) // 空间复杂度:O(log n)(递归调用栈的空间)
归并排序
function sort(left,right){
let result = []
let leftIndex = 0, rightIndex = 0;
while(leftIndex < left.length && rightIndex < right.length){
if(left[leftIndex] < right[rightIndex]){
res.push(left[leftIndex])
leftIndex++
}else{
result.push(right[rightIndex])
rightIndex++
}
}
return result.concat(left.slice(leftIndex),right.slice(rightIndex))
}
function mereSort(arr){
if(arr.length <= 1){
return arr;
}
const mid = Math.floor(arr.length/ 2)
const left = arr.slice(0 , middle)
const right = arr.slice(middle);
return merge(mergeSort(left) , mergeSort(right))
}
14.实现eventBus
class EventBus{
constructor(){
this.event = Object.create(null)
}
on(name,fn){
//一个事件可能有多个监听者
if(!this.event[name]){
this.event[name] = []
}
this.event[name].push(fn)
}
//触发事件
emit(name,...args){
//给回调函数传参
this.event[name] && this.event[name].forEach(fn=> {
fn(...args)
})
};
//只被触发一次的事件
once(name,fn){
//在这里同时完成了对该事件的注册,对该事件的触发,并在最后取消该事件
const cb = (...args) => {
//触发
fn(...args)
//取消
this.off(name,fn)
}
//监听
this.on(name,cb)
}
//取消事件
off(name,offcb){
if(this.event[name]){
let index = this.event[name].findIndex((fn) => {
return offcb === fn
})
this.event[name].splice(index,1)
if(!this.event[name].length){
delete this.event[name];
}
}
}
}
15.实现vue-router
function Router(options){
//定义routes对象,用于存储路由配置
this.routes = options.routes || [];
//初始化currentRoute变量,用于存储当前路由信息
this.currentRoute = {};
//监听浏览器历史记录的变化,以更新currentRoute
window.addEventListener('popstate',() => {
this.currentRoute ={
path: window.location.pathname,
params:{}
};
this.matchRoute()
})
//定义matchRoute方法,用于匹配当前路径与路由配置,并执行相应的回调函数
Router.prototype.matchRoute = function() {
const route = this.routes.find(route =>route.path === this.currentRoute.path)
if(route){
route.callback(this.currentRoute)
}else{
console.log('目标路由不存在')
}
}
//定义push方法,用于切换路由并更新currentRoute
Router.prototype.push = function(path){
this.currentRoute = {
path:path,
params:{}
};
window.history.pushState({},"",path);
this.matchRoute()
}
}
16.手撕vuex
function Store(options){
//定义state对象,用于存储状态
this.state = options.state || {}
//定义getters对象,用于定义获取计算属性的函数
this.getters = {}
const computed = options.computed || {}
for(const key in computed){
if(computed.hasOwnProperty(key)){
this.getters[key] = computed[key].bind(null,this.state);
}
}
//定义mutations对象,用于定义修改状态的方法
this.mutations = options.mutations || {}
//定义actions对象,用于定义执行异步操作的方法
this.actions = options.actions || {}
//定义commit方法,用于触发mutations中的方法来修改状态
Store.prototype.commit = function(type,payload){
if(this.mutations[type]){
this.mutations[type](this.state,payload)
}else{
console.log('不存在')
}
}
//定义dispatch方法,用于触发actions重点方法来执行异步操作
Store.prototype.dispatch = function(type,payload){
if(this.actions[type]){
return this.actions[type](this,payload)
}else{
console.log('报错')
return Promise.reject(new Error('err'))
}
}
}
// 示例用法: const store = new Store({ state: { count: 0, }, computed: { doubleCount: function (state) { return state.count * 2; }, }, mutations: { increment: function (state, payload) { state.count += payload; }, }, actions: { asyncIncrement: function (store, payload) { return new Promise((resolve, reject) => { setTimeout(() => { store.commit('increment', payload); resolve(); }, 1000); }); }, }, }); console.log(store.state.count); // 输出: 0 console.log(store.getters.doubleCount); // 输出: 0 store.commit('increment', 2); console.log(store.state.count); // 输出: 2 store.dispatch('asyncIncrement', 3).then(() => { console.log(store.state.count); // 输出: 5 });
17.settimeout实现setInterval
function newSetInterval(callback,interval){
const wrapper = () => {
callback()
setTimeout(wrapper,interval)
};
setTimeout(wrapper,interval)
}
let count = 0
const intervalTime = 1000
newSetInterval(() => {
count++
console.log(`Count: ${count}`)
if(count >= 5){
console.log("newSetInterval finished")
}
},intervalTime)
在这个示例中,`customSetInterval` 函数接受一个回调函数和一个时间间隔作为参数。它通过递归调用自身和 `setTimeout` 来实现定时循环。在每次回调函数执行后,它会再次设置一个 `setTimeout` 来触发下一次回调函数执行。
需要注意的是,与 `setInterval` 不同,`customSetInterval` 的延迟时间是在每次回调函数执行完成后才会开始计算。这可能导致一些微小的时间偏移,但在大多数情况下,它是可以接受的。
18.实现分片上传
//假设有一个上传函数uploadChunk
async function uploadLargrFile(file){
const chunkSize = 1 * 1024 * 1024;//每个分片1mb
const totalChunks = MAth.ceil(file.size / chunkSize)
for(let i = 0;i < totalChunks;i++){
const start = i * chunkSize
consy end = Math.min(start + chunkSize,file.size)
const chunk = file.slice(start,end)
await uploadChunk(chunk)
}
console.log('上传成功')
}
19.防抖和节流
//防抖
function debounce(func,delay){
let timeoutId;
return function(...args){
clearTimeout(timeoutId)
timeoutId = setTimeout(()=>{
func.apply(this,args)
} , delay)
}
}
//节流
function throttle(func ,interval){
let lastTime = 0;
return function(...args){
const currentTime = Date.now();
if(currentTime - lastTime >= interval){
func.apply(this,args)
lastTime = currentTime
}
}
}
function throttle(func, interval) {
let timer = false;
return function (...args) {
if (!timer) {
timer = setTimeout(() => { func.apply(this, args); timer = false; }, interval);
}
};
}
20.手写一个getUserInfo函数,向后端发起请求,要求多次调用getUserInfo只返回第一次请求的结果,如果在第一次请求过程中再次调用,等待第一次请求的结果,即避免请求重新发送
let userInfoPromise = null
function getUseInfo() {
if(!userInfoPromise){
//第一次调用,发起请求,并将promise存储起来
UserInfoPromise= new Promise((resolve,reject) =>{
//在这里发起向后端的请求,获取用户信息
//假设请求返回的数据是user
setTimeout(() =>{
const user = {id : 1 , name: 'john'}
resolve(user)
} , 1000)//模拟请求延迟
})
}
return userInfoPromise;
}
// 示例用法 getUserInfo().then((user) => { console.log('User info:', user); });
// 在第一次请求还没完成时再次调用
getUserInfo().then((user) => {
console.log('User info (waiting for the first request):', user);
});
在这个示例中,我们使用一个 `userInfoPromise` 变量来存储第一次请求的 Promise,以便后续的调用都能返回同一个 Promise。当第一次调用 `getUserInfo` 时,会发起请求并存储 Promise。后续的调用会直接返回存储的 Promise,等待第一次请求的结果。
21.数组去重的两种方式
set实现
const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 4, 5]
循环遍历实现
function unique(array) {
const result = [];
for (let i = 0; i < array.length; i++) {
if (result.indexOf(array[i]) === -1) {
result.push(array[i]); } } return result;
}
const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = unique(array);
console.log(uniqueArray); // [1, 2, 3, 4, 5]
22.Map与Object
//Map转换为Object
const map = new Map();
map.set('key1', 'value1');
map.set('key2', 'value2');
const obj = Object.fromEntries(map);
console.log(obj) //{ key1: 'value1', key2: 'value2' }
或者循环遍历
const map = new Map()
map.set('key1','value1')
map.set('key2', 'value2')
const obj = {}
for(const [key,value] of map){
obj[key] = value
}
console.log(obj) //{ key1: 'value1', key2: 'value2' }
//Object转换为Map
const obj = {key1: 'value1' , key2: 'value2'}
const map = new Map(Object.entries(obj))
console.log(map)
// Map { 'key1' => 'value1', 'key2' => 'value2' }
需要注意的是,Map 和 Object 在数据结构和用法上有一些区别,转换时要考虑键值对的顺序、键的类型等因素。如果 Map 中存在复杂的数据结构,转换为 Object 可能会丢失一些信息。同样地,Object 转换为 Map 时,只能将 Object 中的字符串键转换为 Map 的键,其他类型的键会被忽略。