Appearance
1.使用篇
1. Promise基本使用
Promise 是 JavaScript 中处理异步操作的一种方式。它是一个代表了异步操作最终完成或者失败的对象。
1.1 创建一个Promise
我们可以通过 new Promise() 来创建一个新的 Promise。这个构造函数接受一个函数作为参数,这个函数接受两个参数:resolve 和 reject,它们是两个函数,由 JavaScript 引擎提供,不需要自己定义。
javascript
let promise = new Promise((resolve, reject) => {
// 这里是你的异步操作
// 当异步操作成功时,你应该调用 resolve 方法
resolve("成功");
// 当异步操作失败时,你应该调用 reject 方法
// reject("失败")
});
1.2 使用Promise
Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。
Promise 实例生成以后,可以用 then 方法分别指定 Resolved 状态和 Reject 状态的回调函数。
javascript
promise.then(value => {
// 这里是当 promise 成功时要执行的代码
console.log(value)
// value 是 resolve 方法传入的值
}, error => {
// 这里是当 promise 失败时要执行的代码
console.log(error)
// error 是 reject 方法传入的值
});
1.3 Promise链
then 方法返回的是一个新的 Promise 实例,因此可以采用链式写法。
javascript
let promise = new Promise(function(resolve, reject) {
resolve(1);
});
promise.then(function(value) {
console.log(value); // 1
return value + 1;
}).then(function(value) {
console.log(value); // 2
});
1.4 错误处理
在Promise 链中,如果任何一个 Promise 被 reject,那么整个链条都会立即终止,并且后续的 Promise 不会再执行。
javascript
let promise = new Promise(function(resolve, reject) {
// reject('失败')
resolve('成功')
// alert(1)
// throw new Error('test');
});
// 写法1: 使用catch不会
promise.then(function (value) {
console.log(value)
}).catch(function(error) {
console.log(error,22);
});
1.5 Promise最终只有一种状态
你可以把它想象成一个正在进行的比赛。比赛只有两种可能的结果:你赢了(Fulfilled)或者你输了(Rejected)。一旦比赛结果出来,就不能再改变了,无论你之后做什么,结果都是确定的。
javascript
let promise = new Promise((resolve, reject) => {
resolve("成功"); // 这里我们 resolve 了 Promise
reject("失败"); // 这里的 reject 不会有任何效果,因为 Promise 的状态已经确定为 Fulfilled
});
promise.then(value => {
console.log(value); // 输出 "成功"
}, error => {
console.log(error); // 这里的代码不会执行
});
2.Promise实例方法
1.6 all方法
Promise.all是一个 Promise 静态方法,它接收一个Promise对象的数组,然后返回一个新的Promise对象。这个新的Promise对象会在所有的Promise对象都成功时成功,如果有任何一个Promise对象失败,新的Promise对象就会失败。
javascript
let promise1 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功1')
}, 1000);
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功2')
}, 1000);
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(()=>{
// resolve('成功3')
reject('失败')
}, 1000);
});
Promise.all([promise1,promise2,promise3]).then((res)=>{
console.log(res,'res');
}).catch(err=>{
console.log(err);
})
1.7 race方法
Promise.race 是一个 Promise 静态方法,它接收一个 Promise 对象的数组作为参数,然后返回一个新的 Promise 对象。这个新的 Promise 对象会在数组中的任何一个 Promise 对象的状态变为 fulfilled 或 rejected 时,立即改变自己的状态。
这意味着,Promise.race 返回的 Promise 对象的状态由最快完成的 Promise 对象决定。如果最快的 Promise 成功了,那么 Promise.race 返回的 Promise 也会成功;如果最快的 Promise 失败了,那么 Promise.race 返回的 Promise 也会失败。
javascript
let promise1 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功1')
}, 1000);
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功2')
}, 1000);
});
let promise3 = new Promise((resolve, reject) => {
setTimeout(()=>{
// resolve('成功3')
reject('失败')
}, 10);
});
Promise.race([promise1,promise2,promise3]).then((res)=>{
console.log(res,'res');
}).catch(err=>{
console.log(err);
})
1.8then()
方法
用于处理成功的结果。
javascript
promise.then(function(result) {
// 处理成功的结果
console.log(result);
}, function(error) {
// 处理失败的原因
console.log(error);
});
1.9 catch()
方法
用于处理失败的结果。
javascript
promise.catch(function(error) {
// 处理失败的原因
console.log(error);
});
1.10 finally()
方法
无论结果如何,都会执行的回调。
javascript
promise.finally(function() {
// 总是会执行的代码
console.log("Promise 完成");
});
3. Promise静态方法
Promise.resolve()
返回一个成功状态的 Promise。
javascript
let promise = Promise.resolve('成功');
promise.then(function(value) {
console.log(value); // "成功"
});
Promise.reject()
返回一个失败状态的 Promise。
javascript
let promise = Promise.reject('失败');
promise.catch(function(reason) {
console.log(reason); // "失败"
});
Promise.all()
用于将多个 Promise 实例组合成一个新的 Promise 实例。
javascript
let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values); // [3, 42, "foo"]
});
Promise.race()
只要其中一个 Promise 完成或失败,就会返回那个 Promise 的结果。
javascript
let promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then(function(value) {
console.log(value); // "two"
});
Promise.allSettled()
等所有的 Promise 都完成后,返回一个包含每个 Promise 结果的数组。
javascript
let promise1 = Promise.resolve('成功');
let promise2 = Promise.reject('失败');
Promise.allSettled([promise1, promise2]).then(function(results) {
results.forEach((result) => console.log(result.status));
// "fulfilled"
// "rejected"
});
Promise.any()
只要其中一个 Promise 成功,就会返回那个 Promise 的结果。
javascript
let promise1 = Promise.reject('失败');
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, '成功');
});
Promise.any([promise1, promise2]).then(function(value) {
console.log(value); // "成功"
});
2.手写篇
1.手写Promise
1.简单实现
javascript
// 定义一个名为MyPromise的类
class MyPromise {
// 构造函数接收一个执行器函数作为参数
constructor (executor) {
// 初始化状态为"pending"
this.state = "pending"
// 初始化值为undefined
this.value = undefined;
// 初始化原因为undefined
this.reason = undefined;
// 定义resolve函数
const resolve = (value)=>{
// 当状态为"pending"时,改变状态为"fulfilled"并设置值
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value;
}
}
// 定义reject函数
const reject = (reason)=>{
// 当状态为"pending"时,改变状态为"rejected"并设置原因
if (this.state === 'pending') {
this.state = "rejected"
this.reason = reason;
}
}
// 执行执行器函数,并将resolve和reject函数作为参数传入
executor(resolve, reject)
}
// 定义then方法,接收两个函数作为参数,分别对应fulfilled和rejected状态
then (onFulfilled, onRejected) {
// 当状态为"fulfilled"时,执行onFulfilled函数,并将值作为参数传入
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
// 当状态为"rejected"时,执行onRejected函数,并将原因作为参数传入
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}
// 导出MyPromise类
module.exports = MyPromise
注意:
这是一个简单的Promise实现,并没有考虑异步情况
2.支持异步
javascript
class MyPromsie {
// 构造函数接收一个执行器
constructor (exector) {
// 初始化状态为"pending"
this.state = "pending"
// 初始化值为undefined
this.value = undefined;
// 初始化原因为undefined
this.reason = undefined;
// 初始化成功回调函数队列
this.onResolvedCallBacks = []
// 初始化失败回调函数队列
this.onRejectedCallBacks = []
// 定义resolve函数
const resolve = (value)=>{
// 当状态为"pending"时,改变状态为"fulfilled",并设置值
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value;
// 执行所有成功的回调函数
this.onResolvedCallBacks.forEach(fn=>fn(this.value))
}
}
// 定义reject函数
const reject = (reason)=>{
// 当状态为"pending"时,改变状态为"rejected",并设置原因
if (this.state === 'pending') {
this.state = "rejected"
this.reason = reason;
// 执行所有失败的回调函数
this.onRejectedCallBacks.forEach(fn=>fn(this.reason))
}
}
// 执行执行器函数,并将resolve和reject函数传入
exector(resolve, reject)
}
// 定义then方法
then (onFulfilled, onRejected) {
// 当状态为"fulfilled"时,执行成功的回调函数
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
// 当状态为"rejected"时,执行失败的回调函数
if (this.state === 'rejected') {
onRejected(this.reason)
}
// 当状态为"pending"时,将成功和失败的回调函数加入对应的队列中
if (this.state === 'pending') {
this.onResolvedCallBacks.push(()=> onFulfilled(this.value))
this.onRejectedCallBacks.push(()=> onRejected(this.reason))
}
}
}
// 导出MyPromise类
module.exports = MyPromsie
这个Promise虽然解决了异步的问题,但是没有解决链式调用的问题
3.支持链式调用
javascript
class MyPromsie {
// 构造函数接收一个执行器
constructor(exector) {
// 初始化状态为"pending"
this.state = "pending"
// 初始化值为undefined
this.value = undefined;
// 初始化原因为undefined
this.reason = undefined;
// 初始化成功回调函数队列
this.onResolvedCallBacks = []
// 初始化失败回调函数队列
this.onRejectedCallBacks = []
// 定义resolve函数
const resolve = (value) => {
// 当状态为"pending"时,改变状态为"fulfilled",并设置值
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value;
// 执行所有成功的回调函数
this.onResolvedCallBacks.forEach(fn => fn(this.value))
}
}
// 定义reject函数
const reject = (reason) => {
// 当状态为"pending"时,改变状态为"rejected",并设置原因
if (this.state === 'pending') {
this.state = "rejected"
this.reason = reason;
// 执行所有失败的回调函数
this.onRejectedCallBacks.forEach(fn => fn(this.reason))
}
}
// 执行执行器函数,并将resolve和reject函数传入
exector(resolve, reject)
}
// 定义then方法
then(onFulfilled, onRejected) {
return new MyPromsie((resolve, reject) => {
// 当状态为"fulfilled"时,执行成功的回调函数
if (this.state === 'fulfilled') {
const x = onFulfilled(this.value)
try {
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
} else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
}
// 当状态为"rejected"时,执行失败的回调函数
if (this.state === 'rejected') {
const x = onRejected(this.reason)
try {
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
}else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
}
// 当状态为"pending"时,将成功和失败的回调函数加入对应的队列中
if (this.state === 'pending') {
this.onResolvedCallBacks.push(() => {
try {
const x = onFulfilled(this.value)
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
}else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
})
this.onRejectedCallBacks.push(() => {
try {
const x = onRejected(this.reason)
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
}else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
})
}
})
}
}
// 导出MyPromise类
module.exports = MyPromsie
以上代码虽然支持链式调用,但是存在冗余代码,需要把冗余代码进一步封装
冗余代码如下
javascript
try {
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
} else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
封装成如下方法
javascript
// 处理then方法中回调函数的返回值,并根据返回值的类型决定如何改变新的Promise对象的状态。
function resolvePromise (x, resolve, reject) {
try {
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
} else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
}
优化后的完整代码如下
javascript
class MyPromsie {
static PEDDING = "PEDDING"
static FULLFILLED = "FULLFILLED"
static REJECTED = "REJECTED"
// 构造函数接收一个执行器
constructor(exector) {
// 初始化状态为"pending"
this.state = MyPromsie.PEDDING
// 初始化值为undefined
this.value = undefined;
// 初始化原因为undefined
this.reason = undefined;
// 初始化成功回调函数队列
this.onResolvedCallBacks = []
// 初始化失败回调函数队列
this.onRejectedCallBacks = []
// 定义resolve函数
const resolve = (value) => {
// 当状态为"pending"时,改变状态为"fulfilled",并设置值
if (this.state === MyPromsie.PEDDING) {
this.state = MyPromsie.FULLFILLED
this.value = value;
// 执行所有成功的回调函数
this.onResolvedCallBacks.forEach(fn => fn(this.value))
}
}
// 定义reject函数
const reject = (reason) => {
// 当状态为"pending"时,改变状态为"rejected",并设置原因
if (this.state ===MyPromsie.PEDDING) {
this.state = MyPromsie.REJECTED
this.reason = reason;
// 执行所有失败的回调函数
this.onRejectedCallBacks.forEach(fn => fn(this.reason))
}
}
// 执行执行器函数,并将resolve和reject函数传入
exector(resolve, reject)
}
// 定义then方法
then(onFulfilled, onRejected) {
return new MyPromsie((resolve, reject) => {
// 当状态为"fulfilled"时,执行成功的回调函数
if (this.state === MyPromsie.FULLFILLED) {
const x = onFulfilled(this.value)
resolvePromise(x, resolve,reject)
}
// 当状态为"rejected"时,执行失败的回调函数
if (this.state === MyPromsie.REJECTED) {
const x = onRejected(this.reason)
resolvePromise(x, resolve,reject)
}
// 当状态为"pending"时,将成功和失败的回调函数加入对应的队列中
if (this.state === MyPromsie.PEDDING) {
this.onResolvedCallBacks.push(() => {
const x = onFulfilled(this.value)
resolvePromise(x, resolve,reject)
})
this.onRejectedCallBacks.push(() => {
const x = onRejected(this.reason)
resolvePromise(x, resolve,reject)
})
}
})
}
}
function resolvePromise (x,resolve,reject) {
try {
// 如果回调函数返回的是Promise对象,那么需要等待这个Promise对象状态改变后,再执行resolve或reject
if (x instanceof MyPromsie) {
x.then(resolve, reject)
} else {
// 如果回调函数返回的不是Promise对象,那么直接将返回值作为新的Promise对象的结果
resolve(x)
}
} catch (error) {
// 如果执行回调函数过程中出现错误,那么新的Promise对象的状态为rejected
reject(error)
}
}
// 导出MyPromise类
module.exports = MyPromsie
4.手写all方法
javascript
static all (promises) {
// 返回一个新的Promise对象
return new Promise((resolve,reject)=>{
// 初始化计数器,用于跟踪已解决的Promise数量
let count = 0;
// 初始化结果数组,用于存储所有Promise的结果
let result = [];
// 遍历传入的Promise数组
for (let i = 0; i < promises.length; i++) {
// 对每个Promise调用then方法
promises[i].then(value=>{
// 当Promise解决时,增加计数器并将结果存储在结果数组中的相应位置
count++;
result[i] = value;
// 如果所有Promise都已解决,那么解决包含所有结果的新Promise
if (count === promises.length) {
resolve(result)
}
},reason=>{
// 如果任何Promise被拒绝,那么拒绝新的Promise并传递原因
reject(reason)
})
}
})
}
5.手写race方法
javascript
// 定义静态race方法
static race (promises) {
// 返回一个新的Promise对象
return new Promise((resolve, reject)=>{
// 遍历传入的Promise数组
for (let i = 0; i < promises.length; i++) {
// 对每个Promise对象调用then方法
// 当任何一个Promise对象状态改变(无论是fulfilled还是rejected),就会调用resolve或reject方法
// 这样新的Promise对象的状态就会改变,race方法就会返回
promises[i].then(resolve, reject)
}
})
}
6.Promise 3秒钟之后求和
javascript
function sumAfter3Seconds(...args) {
// 创建一个新的 Promise
return new Promise((resolve, reject) => {
// 使用 setTimeout 来延迟 3 秒
setTimeout(() => {
// 计算参数的和
let sum = args.reduce((a, b) => a + b, 0);
// 使用 resolve 函数来改变 Promise 的状态为 fulfilled,并传递结果
resolve(sum);
}, 3000);
});
}
// 使用这个函数
sumAfter3Seconds(1, 2, 3, 4, 5).then(value=> {console.log(value)}); // 输出:15
2.手写浅拷贝
javascript
function shallowCopy(obj) {
// 如果obj不是对象或者obj是null,直接返回obj
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 判断obj是数组还是对象,创建一个相应的空数组或对象
let copy = Array.isArray(obj) ? [] : {};
// 遍历obj的所有属性
for (let key in obj) {
// 如果属性是obj自身的(不是继承来的)
if (obj.hasOwnProperty(key)) {
// 将属性复制到新的对象或数组中
copy[key] = obj[key];
}
}
// 返回新的对象或数组
return copy;
}
3.手写深拷贝
javascript
// 定义一个名为deepCopy的函数,接受一个参数obj
function deepCopy (obj) {
// 如果obj不是对象或者obj是null,直接返回obj
// 这是递归的基本情况
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 根据obj是否为数组,创建一个新的空数组或空对象
let copy = Array.isArray(obj) ? [] : {};
// 遍历obj的所有属性
for (let key in obj) {
// 如果当前属性是obj自身的属性(不是从原型链继承的属性)
if (obj.hasOwnProperty(key) ) {
// 如果当前属性的值是一个非null对象,递归调用deepCopy函数
if (typeof obj[key] === 'object' && obj[key] !== null) {
copy[key] = deepCopy(obj[key])
} else {
// 如果当前属性的值不是对象,直接复制
copy[key] = obj[key]
}
}
}
// 返回复制后的对象
return copy;
}
4.手写防抖
防抖(debounce)的主要思想是:在一定时间内,事件被触发多次,只执行最后一次。
javascript
/
防抖函数
@param {Function} func 需要进行防抖处理的目标函数
@param {number} delay 延迟时间,单位是毫秒
@return {Function} 经过防抖处理的函数
/
function debounce(func, delay) {
let timer = null; // 声明一个定时器
// 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 func 函数
return function(...args) {
// 每次触发时清除上一次的定时器
if(timer) {
clearTimeout(timer);
}
// 然后设置新的定时器,延迟执行用户传入的方法
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
}
}
5.手写节流
节流(throttle)的主要思想是:在一定时间内,事件被触发多次,只执行第一次。
javascript
/
节流函数
@param {Function} func 需要进行节流处理的目标函数
@param {number} delay 延迟时间,单位是毫秒
@return {Function} 经过节流处理的函数
/
function throttle(func, delay) {
let lastTime = 0; // 声明一个变量记录上一次执行的时间
// 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 func 函数
return function(...args) {
// 获取当前时间
let nowTime = Date.now();
// 如果当前时间与上一次执行的时间间隔大于设定的延迟时间,则执行函数
if (nowTime - lastTime > delay) {
func.apply(this, args);
// 更新上一次执行的时间
lastTime = nowTime;
}
}
}
6.手写Object.create
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto。
javascript
/
实现Object.create方法
@param {Object} proto 要用作原型的对象
@return {Object} 新创建的对象
/
function create(proto) {
// 创建一个空的构造函数
function F() {}
// 将构造函数的原型指向传入的对象
F.prototype = proto;
// 使用构造函数创建一个新的对象,这个新对象的__proto__就指向传入的对象
const obj = new F();
// 返回新对象
return obj;
}
7.手写instanceof方法
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
javascript
// 手写instanceof方法
function instanceOf(left, right) {
// 获取对象的原型
let proto = Object.getPrototypeOf(left);
// 循环判断对象的原型是否等于指定类型的原型,直到对象的原型为null,因为原型链最顶端是null
while (true) {
// 如果原型为null,说明检查到了原型链的顶端,返回false
if (proto === null) return false;
// 如果原型等于指定类型的原型,返回true
if (proto === right.prototype) return true;
// 如果不等于,那么沿着原型链继续向上查找
proto = Object.getPrototypeOf(proto);
}
}
8.手写new
javascript
// 定义一个myNew函数,模拟new操作符的行为
function myNew (constructor, ...args) {
// 使用Object.create创建一个新对象,该对象的原型指向构造函数的prototype
let obj = Object.create(constructor.prototype);
// 使用apply方法调用构造函数,改变this指向新创建的对象,并传入参数
let result = constructor.apply(obj, args )
// 如果构造函数返回的是一个对象,则返回该对象,否则返回新创建的对象
return typeof result === 'object' ? result : obj;
}
// 定义一个Person构造函数
function Person (name) {
// 为实例添加name属性
this.name = name;
}
// 使用myNew函数创建一个Person实例,传入参数'刘德华'
let p = myNew (Person, '刘德华');
// 打印创建的实例
console.log(p);
9.手写bind
1.借助apply实现
javascript
// 为Function.prototype添加一个myBind方法
// 这个方法接收一个context参数和若干个args参数
Function.prototype.myBind = function (context, ...args) {
// 保存当前函数的引用
let self = this;
// 返回一个新的函数
return function () {
// 在新的函数中,使用apply方法调用原函数
// 并将context作为原函数的上下文
// 将myBind方法接收的参数和新函数接收的参数一起传递给原函数
return self.apply(context,[...args, ...arguments])
}
}
2.原生JS实现
javascript
// 为Function.prototype添加一个myBind方法
// 这个方法接收一个context参数和若干个args参数
Function.prototype.myBind = function (context, ...args) {
// 保存当前函数的引用
// let self = this;
context.temp = this;
// 返回一个新的函数
return (...args1) => {
// 在新的函数中,使用apply方法调用原函数
// 并将context作为原函数的上下文
// 将myBind方法接收的参数和新函数接收的参数一起传递给原函数
context.temp(...args, ...args1)
delete context.temp;
}
}
10.手写call
javascript
// 在 Function.prototype 上定义 myCall 方法
Function.prototype.myCall = function(context) {
// 如果没有传入 context 或者 context 是 null,那么 context 默认为全局对象
context = context || window;
// 为 context 添加一个临时的方法,这个方法就是当前函数(this 指向当前函数)
context.temp = this;
// 获取参数,从第二个参数开始,因为第一个参数是 context
let args = [...arguments].slice(1);
// 调用这个临时方法,并传入参数
let result = context.temp(...args);
// 删除这个临时方法
delete context.temp;
// 返回结果
return result;
}
11.手写Apply
javascript
// 在 Function.prototype 上定义 myApply 方法
Function.prototype.myApply = function(context, arr) {
// 如果没有传入 context 或者 context 是 null,那么 context 默认为全局对象
context = context || window;
// 为 context 添加一个临时的方法,这个方法就是当前函数(this 指向当前函数)
context.temp = this;
// 定义一个变量用来保存结果
let result;
// 判断是否存在第二个参数
if (!arr) {
// 如果不存在第二个参数,直接执行函数
result = context.temp();
} else {
// 如果存在第二个参数,使用 spread operator (...) 将数组展开,然后传入函数
result = context.temp(...arr);
}
// 删除这个临时方法
delete context.temp;
// 返回结果
return result;
}
12.循环打印红黄绿灯
红灯3秒亮一次,绿灯1s亮一次,黄灯2s亮一次,如何让三个灯不断交替重复亮灯
javascript
// 定义一个函数,用于返回一个在指定时间后解析的Promise
function delay(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
// 定义一个异步函数,用于循环打印红黄绿灯
async function loopLight() {
while (true) { // 使用while(true)来创建一个无限循环
console.log('红灯'); // 打印红灯
await delay(3000); // 等待3秒
console.log('绿灯'); // 打印绿灯
await delay(1000); // 等待1秒
console.log('黄灯'); // 打印黄灯
await delay(2000); // 等待2秒
}
}
// 调用函数
loopLight();
13.递归实现斐波那契数列
javascript
// 定义斐波那契数列的递归函数
function fibonacci(n) {
// 基本情况:n为0或1时,斐波那契数列的值就是n本身
if (n <= 1) {
return n;
}
// 递归情况:n大于1时,斐波那契数列的值是前两项的和
else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
// 使用这个函数
console.log(fibonacci(10)); // 输出:55
14.冒泡排序
javascript
// 定义冒泡排序函数
function bubbleSort(arr) {
// 外层循环控制所有的回合
for(let i = 0; i < arr.length - 1; i++) {
// 内层循环控制每一轮的冒泡处理
for(let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j+1]) {
// 如果前一个元素大于后一个元素,则交换他们
[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
// 返回排序后的数组
return arr;
}
// 使用这个函数
console.log(bubbleSort([5, 3, 8, 4, 6])); // 输出:[3, 4, 5, 6, 8]
15.快速排序
javascript
// 定义快速排序函数
function quickSort(arr) {
// 如果数组长度小于等于1,无需排序,直接返回
if (arr.length <= 1) {
return arr;
}
// 选择一个基准元素,这里我们选择数组的中间元素
const middleIndex = Math.floor(arr.length / 2);
const middle = arr.splice(middleIndex, 1)[0];
// 定义两个数组,left用于存放比基准元素小的元素,right用于存放比基准元素大的元素
const left = [];
const right = [];
// 遍历数组,进行划分
for (let i = 0; i < arr.length; i++) {
if (arr[i] < middle) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 递归地对左右子数组进行快速排序,然后合并结果
return [...quickSort(left),middle, ...quickSort(right)]
}
// 使用这个函数
console.log(quickSort([5, 3, 8, 4, 6])); // 输出:[3, 4, 5, 6, 8]
16.Promise实现3秒后函数参数求和
javascript
// 定义一个函数,该函数接受两个参数a和b
function sumAfter3Seconds(a, b) {
// 返回一个新的Promise对象
return new Promise((resolve, reject) => {
// 使用setTimeout函数,设置3秒后执行的代码
setTimeout(() => {
// 检查输入的参数a和b是否都是数字
if (typeof a === 'number' && typeof b === 'number') {
// 如果是,那么调用resolve函数,将a和b的和作为Promise的结果
resolve(a + b);
} else {
// 如果不是,那么调用reject函数,将错误信息作为Promise的结果
reject('无效输入');
}
}, 3000); // 设置延迟时间为3秒
});
}
javascript
sumAfter3Seconds(1, 2)
.then(sum => console.log(sum)) // 如果Promise成功,打印结果
.catch(error => console.log(error)); // 如果Promise失败,打印错误信息
17.Promise应用:图片异步加载
javascript
// 定义一个函数,该函数接受一个url作为参数
function loadImage(url) {
// 返回一个新的Promise对象
return new Promise((resolve, reject) => {
// 创建一个新的Image对象
let image = new Image();
// 当图片加载成功时,调用resolve函数,将Image对象作为Promise的结果
image.onload = function() {
resolve(image);
};
// 当图片加载失败时,调用reject函数,将错误信息作为Promise的结果
image.onerror = function() {
reject(new Error('无法加载图片 ' + url));
};
// 设置图片的源为传入的url
image.src = url;
});
}
javascript
loadImage('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F2eb217dc-f9a0-4b7a-896e-b1d8db766d8c%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1696548898&t=8957321ea32cf51845e76eb9fcf2083d')
.then(image => {
// 如果Promise成功,将图片添加到body中
document.body.appendChild(image);
})
.catch(error => {
// 如果Promise失败,打印错误信息
console.log(error);
});
18.1实现Sleep函数(使用Promise封装setTimeout)
javascript
// 定义一个sleep函数,接受一个参数ms,表示延迟的毫秒数
function sleep(ms) {
// 返回一个新的Promise对象
return new Promise((resolve) => {
// 使用setTimeout函数,设置ms毫秒后执行的代码
setTimeout(() => {
// ms毫秒后,调用resolve函数,表示Promise已经完成
resolve();
}, ms);
});
}
javascript
sleep(1000).then(() => {
console.log('1秒后打印这行');
});
19. 1秒后打印1,2秒后打印2,3秒后打印3重复交替打印下去
分别使用callback、promise、async、await3种方式实现
1.callback 实现
javascript
// 定义一个函数,该函数接受一个数字n和一个回调函数callback作为参数
function printAfterNSeconds(n, callback) {
// 使用setTimeout函数,设置n秒后执行的代码
setTimeout(() => {
// n秒后,调用callback函数,将n作为参数
callback(n);
// 递归调用printAfterNSeconds函数,实现交替打印
printAfterNSeconds(n % 3 + 1, callback);
}, n *1000);
}
// 调用printAfterNSeconds函数,开始打印
printAfterNSeconds(1, console.log);
2.promise实现
javascript
// 定义一个名为sleep的函数,该函数接受一个参数ms(毫秒)
function sleep(ms) {
// 返回一个新的Promise对象
return new Promise((resolve, reject) => {
// 使用setTimeout函数,设置ms毫秒后执行resolve函数
setTimeout(resolve, ms);
});
}
// 定义一个名为printAfterNSeconds的函数,该函数接受一个参数n(秒)
function printAfterNSeconds(n) {
// 调用sleep函数,等待1000毫秒(1秒)
sleep(n*1000).then(() => {
// 等待结束后,打印参数n
console.log(n);
// 递归调用printAfterNSeconds函数,参数为(n % 3) + 1
printAfterNSeconds((n % 3) + 1);
});
}
// 调用printAfterNSeconds函数,参数为1
printAfterNSeconds(1);
3.async、await
javascript
async function printNumbers() {
let i = 1;
// 无限循环
while (true) {
// 每隔i秒(i在1,2,3之间循环)执行一次
await new Promise(resolve => setTimeout(resolve, i*1000));
// 打印当前的i值
console.log(i);
// 更新i的值,使其在1,2,3之间循环
i = (i % 3) + 1;
}
}
// 调用printNumbers函数
printNumbers();
20. 递归取出每个菜单的id
javascript
// 定义一个函数getIds,参数为menu
function getIds(menu) {
// 初始化一个空数组ids,用于存储id
let ids = [];
// 如果menu对象有id属性
if (menu.id) {
// 将id添加到ids数组中
ids.push(menu.id);
}
// 如果menu对象有children属性
if (menu.children) {
// 遍历menu的children数组
for (let child of menu.children) {
// 对每个child对象,递归调用getIds函数,并将返回的id数组合并到ids数组中
ids.push(...getIds(child));
}
}
// 返回ids数组
return ids;
}
// 定义一个menu对象,包含id、name和children属性
let menu = {
id: 1, // id属性
name:"系统管理", // name属性
children: [ // children属性,是一个数组,包含多个子菜单对象
{
id: 2,
name:"用户管理",
children: [ // 子菜单对象也可以有自己的children属性
{
id: 3,
name:"添加用户"
},
{
id: 4,
name:"修改用户"
}
]
},
{
id: 5 ,
name:"角色管理"
}
]
};
21.递归-数组扁平化输出
javascript
// 定义一个函数 flattenArray,它接受一个数组作为参数
function flattenArray(arr) {
// 使用 reduce 方法对数组进行迭代
// reduce 方法接受一个回调函数,该函数有两个参数:累加器(acc)和当前值(val)
// 累加器是上一次调用回调时返回的值,或者是提供的初始值(这里是一个空数组 [])
// 当前值是数组中正在处理的元素
return arr.reduce((acc, val) =>
// 如果当前值是一个数组,那么递归调用 flattenArray 函数
// 并将返回的结果(一个扁平化的数组)与累加器合并
Array.isArray(val) ? acc.concat(flattenArray(val))
// 如果当前值不是一个数组,那么直接将其与累加器合并
: acc.concat(val),
// 初始值是一个空数组
[]);
}
// 定义一个嵌套数组
let arr = [1, [2, [3, 4], 5], 6];
22.递归-数组求和
javascript
function sumArray(arr) {
// 基本情况:如果数组为空,那么和就是0
if (arr.length === 0) {
return 0;
} else {
// 递归情况:将数组的第一个元素与剩余部分的和相加
// 使用 slice(1) 获取数组的剩余部分
return arr[0] + sumArray(arr.slice(1));
}
}
let arr = [1, 2, 3, 4, 5];
console.log(sumArray(arr)); // 输出: 15
23.递归组件
1.vue2写法
在App.vue
vue
<template>
<div id="app">
<!-- 如果menu存在,则显示MenuComponent组件,并将menu作为属性传递 -->
<MenuComponent v-if="menu" :menu="menu" />
</div>
</template>
<script>
// 导入MenuComponent组件
import MenuComponent from './components/menu-component.vue';
export default {
name: 'App',
components: {
// 注册MenuComponent组件
MenuComponent
},
data () {
return {
// 定义menu数据
menu: [
{
id: 1,
name: '系统管理',
children: [
{
id: 2,
name: '用户管理',
children: [
{
id: 3,
name: '添加用户'
},
{
id: 4,
name: '修改用户'
}
]
},
{
id: 5,
name: '角色管理'
}
]
}
]
}
}
}
</script>
在components/menu-component.vue
vue
<template>
<div>
<!-- 如果菜单存在,显示菜单列表 -->
<ul v-if="menu">
<!-- 遍历菜单项 -->
<li v-for="item in menu" :key="item.id">
<!-- 显示菜单项名称 -->
{{ item.name }}
<!-- 如果菜单项有子菜单,递归显示子菜单 -->
<menu-component v-if="item.children" :menu="item.children"></menu-component>
</li>
</ul>
</div>
</template>
<script>
export default {
name:"MenuComponent",
props:{
// 接收一个必要的菜单数组作为属性
menu:{
type:Array,
required:true
}
}
}
</script>
2.vue3写法
app.vue
vue
<template>
<!-- 应用主体 -->
<div id="app">
<!-- 如果菜单存在,渲染菜单组件 -->
<MenuComponent v-if="menu" :menu="menu" />
</div>
</template>
<script setup>
// 导入菜单组件
import MenuComponent from './components/menu-component.vue';
// 导入vue的ref函数
import {ref} from 'vue'
// 定义菜单数据
const menu = ref([
{
// 系统管理菜单项
id: 1,
name: '系统管理',
children: [
{
// 用户管理子菜单
id: 2,
name: '用户管理',
children: [
{
// 添加用户选项
id: 3,
name: '添加用户'
},
{
// 修改用户选项
id: 4,
name: '修改用户'
}
]
},
{
// 角色管理菜单项
id: 5,
name: '角色管理'
}
]
}
]);
</script>
components/men-components.vue
vue
<template>
<!-- 定义无序列表 -->
<ul>
<!-- 列表项,使用v-for循环遍历menu数组 -->
<li v-for="item in menu" :key="item.id">
<!-- 显示菜单项名称 -->
{{ item.name }}
<!-- 如果菜单项有子菜单,递归调用menu-component组件 -->
<menu-component v-if="item.children" :menu="item.children"></menu-component>
</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue'
defineProps({
menu: {
type: Array,
required: true
}
})
</script>
24.日期格式化
'2-digit' 是一个选项,它表示在格式化输出时,如果数字的位数少于2位,那么将在数字前面补0,以确保总是输出2位数字。
javascript
// 定义一个名为formatDate的函数,接受两个参数:date和pattern
function formatDate(date, pattern) {
// 创建一个options对象,包含年、月、日的格式
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
// 如果pattern字符串中包含'HH',则在options对象中添加小时的格式
if (pattern.includes('HH')) options.hour = '2-digit';
// 如果pattern字符串中包含'mm',则在options对象中添加分钟的格式
if (pattern.includes('mm')) options.minute = '2-digit';
// 如果pattern字符串中包含'ss',则在options对象中添加秒的格式
if (pattern.includes('ss')) options.second = '2-digit';
// 使用toLocaleDateString方法将date转换为指定格式的字符串,然后使用replace方法将所有的'/'替换为'-'
return new Date(date).toLocaleDateString('zh-CN', options).replace(/\//g, '-');
}
// 调用formatDate函数,并将当前日期和指定的格式字符串作为参数传入,然后打印返回的结果
console.log(formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss'));
25.数据脱敏-手机号的中间4位替换成
javascript
// 定义一个函数maskPhoneNumber,接受一个参数phoneNumber
function maskPhoneNumber(phoneNumber) {
// 使用正则表达式替换phoneNumber中的数字,将中间四位数字替换为
// 正则表达式(\d{3})\d{4}(\d{4})表示匹配三位数字,然后是四位数字,然后是四位数字
// $1$2表示将匹配到的第一组和第三组数字保留,中间四位数字替换为
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}
// 调用maskPhoneNumber函数,传入一个电话号码字符串,然后打印结果
console.log(maskPhoneNumber('13812345678')); // 输出: 1385678
26.手写Vue2双向数据绑定
- observe 函数
- Observer 类
- defineReactive 函数
- 对象的创建和观察
1.observe 函数
javascript
// 1. observe函数用于观察一个对象
function observe(obj) {
// 如果obj不是对象或者是null,那么直接返回
if (typeof obj !== 'object' || obj === null) {
return;
}
// 创建一个Observer实例,用于观察对象
new Observer(obj);
}
2.Observer 类
javascript
// 2. Observer类,用于观察对象
class Observer {
// 构造函数接收一个value参数
constructor(value) {
// 调用walk方法,遍历value对象的所有属性
this.walk(value);
}
// walk方法,用于遍历对象的所有属性
walk(obj) {
// 遍历对象的所有属性,对每个属性调用defineReactive方法
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
}
3.defineReactive 函数
javascript
// defineReactive函数,用于定义一个响应式的属性
function defineReactive(obj, key, val) {
// 对val进行观察,实现深度监听
observe(val);
// 使用Object.defineProperty方法,定义一个响应式的属性
Object.defineProperty(obj, key, {
// getter方法,用于获取属性值
get() {
return val;
},
// setter方法,用于设置属性值
set(newVal) {
// 如果新的值和旧的值不同
if (newVal !== val) {
// 对新的值进行观察,如果新的值是对象,也需要监听
observe(newVal);
// 更新属性值
val = newVal;
// 通知更新,打印日志
console.log(`set ${key}: ${newVal}`);
}
}
});
}
4.对象的创建和观察
javascript
// 4. 创建一个对象并进行观察
let data = { name: 'Vue', age: { number: 20 } };
// 对对象进行观察
observe(data);
// 修改对象的属性值
data.name = 'Vue2';
data.age.number = 21;
5.完整代码
javascript
// observe函数用于观察一个对象
function observe(obj) {
// 如果obj不是对象或者是null,那么直接返回
if (typeof obj !== 'object' || obj === null) {
return;
}
// 创建一个Observer实例,用于观察对象
new Observer(obj);
}
// Observer类,用于观察对象
class Observer {
// 构造函数接收一个value参数
constructor(value) {
// 调用walk方法,遍历value对象的所有属性
this.walk(value);
}
// walk方法,用于遍历对象的所有属性
walk(obj) {
// 遍历对象的所有属性,对每个属性调用defineReactive方法
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
}
// defineReactive函数,用于定义一个响应式的属性
function defineReactive(obj, key, val) {
// 对val进行观察,实现深度监听
observe(val);
// 使用Object.defineProperty方法,定义一个响应式的属性
Object.defineProperty(obj, key, {
// getter方法,用于获取属性值
get() {
return val;
},
// setter方法,用于设置属性值
set(newVal) {
// 如果新的值和旧的值不同
if (newVal !== val) {
// 对新的值进行观察,如果新的值是对象,也需要监听
observe(newVal);
// 更新属性值
val = newVal;
// 通知更新,打印日志
console.log(`set ${key}: ${newVal}`);
}
}
});
}
// 创建一个对象
let data = { name: 'Vue', age: { number: 20 } };
// 对对象进行观察
observe(data);
// 修改对象的属性值
data.name = 'Vue2';
data.age.number = 21;
27.手写Vue3双向数据绑定
javascript
// observe函数用于观察一个对象
function observe(obj) {
// 如果obj不是对象或者是null,那么直接返回
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 创建一个Proxy实例,用于观察对象
return new Proxy(obj, {
get(target, key) {
return observe(target[key]);
},
set(target, key, value) {
// 如果新的值和旧的值不同
if (value !== target[key]) {
// 对新的值进行观察,如果新的值是对象,也需要监听
observe(value);
// 更新属性值
target[key] = value;
// 通知更新,打印日志
console.log(`set ${key}: ${value}`);
}
// 表示属性的赋值操作已经成功完成。
return true;
}
});
}
// 创建一个对象
let data = { name: 'Vue', age: { number: 20 } };
// 对对象进行观察
data = observe(data);
// 修改对象的属性值
data.name = 'Vue2';
data.age.number = 21;
27.其他手写代码
1.实现数组的flat方法
javascript
// 定义一个函数,名为flatArray,参数为arr
function flatArray (arr) {
// 使用reduce方法对数组进行遍历,acc为累计值,val为当前值
// 如果当前值是数组,则递归调用flatArray函数,将返回的结果与累计值进行合并
// 如果当前值不是数组,则直接与累计值进行合并
return arr.reduce((acc,val)=> Array.isArray(val) ? acc.concat(flatArray(val)) : acc.concat(val), [])
}
// 调用flatArray函数,参数为一个包含多层嵌套的数组
// 打印flatArray函数的返回结果
console.log(flatArray([10,[20,30], 20]));
2.实现数组的push方法
javascript
// 在Array的原型上定义一个名为myPush的方法
Array.prototype.myPush = function () {
// 遍历传入的参数
for (let i = 0; i< arguments.length; i++) {
// 将参数添加到数组的末尾
this[this.length] = arguments[i]
}
// 返回新的数组长度
return this.length;
}
// 定义一个数组
let arr = [1,2,3]
// 调用自定义的myPush方法,将4和5添加到数组的末尾
var r = arr.myPush(4,5)
// 打印新的数组和新的数组长度
console.log(arr, r);
3.实现数组的filter方法
javascript
// 在Array的原型上添加一个名为myFilter的方法,参数为callback
Array.prototype.myFilter = function (callback) {
// 初始化一个新的数组
let newArr = []
// 遍历当前数组
for (let i = 0; i< this.length; i++) {
// 如果callback函数返回真值,将当前元素添加到新数组中
if (callback(this[i],i, this)){
newArr.push(this[i])
}
}
// 返回新数组
return newArr
}
// 定义一个数组
let arr = [1,2,3]
// 使用自定义的myFilter方法过滤数组,保留大于1的元素
const newArr = arr.myFilter(item=> item > 1)
// 打印新数组
console.log(newArr);
4.实现数组的map方法
javascript
// 在Array的原型上定义一个新的方法myMap,接收一个回调函数作为参数
Array.prototype.myMap = function (callback) {
// 初始化一个新的空数组
let newArr = []
// 遍历当前数组
for (let i = 0; i< this.length; i++) {
// 将回调函数的返回值添加到新数组中
// 回调函数接收当前元素、当前索引和原数组作为参数
newArr.push(callback(this[i],i, this))
}
// 返回新数组
return newArr
}
// 定义一个数组
let arr = [1,2,3]
// 使用自定义的myMap方法对数组进行处理,将每个元素乘以2
const newArr = arr.myMap(item=> item * 2 )
// 打印处理后的新数组
console.log(newArr);
5.实现字符串的repeat方法
javascript
// 在String的原型上添加一个名为myRepeat的方法,参数为count
String.prototype.myRepeat = function (count) {
// 初始化一个空字符串str
let str = ""
// 使用for循环,循环次数为count
for (let i = 0; i < count; i++) {
// 在每次循环中,将当前字符串(this)添加到str后面
str += this;
}
// 返回拼接后的字符串str
return str;
}
// 调用"abc"字符串的myRepeat方法,参数为3,打印返回结果
console.log("abc".myRepeat(3))
6.实现字符串反转
javascript
String.prototype.reverse = function() {
// 使用split方法将字符串转换为字符数组
// 使用reverse方法反转字符数组
// 使用join方法将字符数组重新组合为字符串
return this.split('').reverse().join('');
}
console.log("abc".reverse()); // 输出:cba
7.将数字每千分位用逗号隔开
javascript
// 定义一个名为formatNumber的函数,参数为num
function formatNumber(num) {
// 使用toLocaleString方法将数字转换为本地化的字符串
// 这通常会在数字中添加逗号作为千位分隔符
return num.toLocaleString();
}
// 调用formatNumber函数,参数为1234567890
// 打印formatNumber函数的返回结果
// 输出 "1,234,567,890"
console.log(formatNumber(1234567890));
28.斐波那契
1.递归实现
javascript
// 定义一个函数,名为fibonacci,参数为n
function fibonacci(n) {
// 如果n小于等于1,直接返回n
if (n <= 1) {
return n;
} else {
// 否则,返回前两个斐波那契数的和
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
// 调用fibonacci函数,参数为10
// 打印fibonacci函数的返回结果
console.log(fibonacci(10)); // 输出:55
2.动态规划
javascript
// 定义一个名为fibonacci的函数,参数为n
function fibonacci(n) {
// 初始化一个数组fib,包含两个元素0和1,这是斐波那契数列的前两个数
let fib = [0, 1];
// 从2开始,到n结束,遍历这个范围内的每一个数
for (let i = 2; i <= n; i++) {
// 对于每一个数i,它的值是数组fib中前两个数的和
fib[i] = fib[i - 1] + fib[i - 2];
}
// 返回数组fib中的第n个数,这就是斐波那契数列的第n个数
return fib[n];
}
// 调用fibonacci函数,参数为10,打印返回的结果
console.log(fibonacci(10)); // 输出:55
3.力扣题
1.1. 两数之和
1.前置知识map
以下是 Map 的一些基本用法:
javascript
//创建 Map
let map = new Map();
//设置键值对
map.set('name', 'John');
map.set(1, 'num1');
map.set(true, 'bool1');
//获取键值
console.log(map.get('name'));
console.log(map.get(1));
console.log(map.get(true));
// 检查键是否存在
console.log(map.has('name'));
// 获取 Map 的大小
console.log(map.size);
// 删除键值对
map.delete('name')
console.log(map.has('name'));
// 清空 Map
map.clear();
console.log(map.size);
// 遍历 Map
for (let [key, value] of map) {
console.log(key, value);
}
javascript
// 两数之和
// 定义两数之和函数,输入为一个数组和一个目标值
function twoSum (nums,target) {
// 创建一个新的Map对象
const map = new Map()
// 遍历输入的数组
for (let i = 0; i<nums.length;i++) {
// 如果Map对象中有目标值减去当前元素的值
if (map.has(target - nums[i])) {
// 打印Map对象
// 返回Map对象中目标值减去当前元素的值的值和当前元素的索引
return [map.get(target - nums[i]), i]
}else {
// 否则在Map对象中设置当前元素的值和索引
map.set(nums[i], i)
}
}
return []
}
// 打印调用两数之和函数的结果
console.log( twoSum([6,3, 4,7, 5,8], 10));
2. 回文数
给你一个整数 x
,如果 x
是一个回文整数,返回 true
;否则,返回 false
。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
- 例如,
121
是回文,而123
不是。
1. 字符串操作方式
javascript
function isPalindrome(x) {
// 将整数转换为字符串
let str = x.toString();
// 反转字符串
let reversedStr = str.split('').reverse().join('');
// 比较原始字符串和反转后的字符串
return str === reversedStr;
}
2.双指针完成
javascript
var isPalindrome = function (x) {
// 将输入转换为字符串
let str = x.toString()
// 获取字符串的长度
for (let i = 0,len = str.length; i < len; i++) {
// 比较字符串的前后字符,如果不相等,则返回false
if (str[i] !== str[len-1 -i]) {
return false
}
}
// 如果所有的前后字符都相等,那么这个字符串就是一个回文,返回true
return true
};
// 测试函数,输入121,期望输出true
console.log(isPalindrome(121));
3.两个数组的交集
给你两个整数数组 nums1
和 nums2
,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
1.数组方法完成
javascript
var intersect = function(nums1, nums2) {
return nums1.filter(value => nums2.includes(value));
};
注意:这个解决方案并不考虑元素出现的次数
2.算法实现
javascript
// 定义一个名为intersect的函数,接收两个参数nums1和nums2
var intersect = function (nums1, nums2) {
// 创建一个空对象map用于存储nums1中的数字及其出现的次数
var map = {};
// 创建一个空数组res用于存储交集结果
var res = [];
// 遍历nums1
for (var num of nums1) {
// 如果map中已经有这个数字,那么将其出现的次数加1
if (map[num]) {
map[num]++;
} else {
// 如果map中没有这个数字,那么将其出现的次数设为1
map[num] = 1;
}
}
console.log(map);
// 遍历nums2
for (var num of nums2) {
// 如果map中有这个数字,并且其出现的次数大于0,那么将其添加到res中,并将其在map中的出现次数减1
if (map[num] && map[num] > 0) {
res.push(num);
map[num]--;
}
}
// 返回交集结果
return res;
};
console.log(intersect([1,2,2,1], [2,2]));
4.删除有序数组中的重复项
给你一个 升序排列 的数组 nums
,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
javascript
function removeDuplicates(nums) {
// 如果数组长度为0,直接返回0
if(nums.length == 0) return 0;
// 初始化一个指针i
let i = 0;
// 从数组的第二个元素开始遍历
for(let j = 1; j < nums.length; j++) {
// 如果当前元素和指针i指向的元素不同
if(nums[j] != nums[i]) {
// 将指针i向前移动一位
i++;
// 并将当前元素赋值给新的位置
nums[i] = nums[j];
}
}
// 返回去重后的数组长度
return i + 1;
}
5.二叉树
给定一个二叉树 root
,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:3
示例 2:
输入:root = [1,null,2]
输出:2
提示:
- 树中节点的数量在
[0, 104]
区间内。 -100 <= Node.val <= 100
javascript
// 定义一个二叉树节点
function TreeNode(val, left, right) {
this.val = (val === undefined ? 0 : val)
this.left = (left === undefined ? null : left)
this.right = (right === undefined ? null : right)
}
// 定义一个函数,用于计算二叉树的最大深度
function maxDepth(root) {
// 如果节点为空,深度为0
if (root == null) {
return 0;
} else {
// 计算左子树的最大深度
let leftHeight = maxDepth(root.left);
// 计算右子树的最大深度
let rightHeight = maxDepth(root.right);
// 返回左右子树深度的最大值加1
return Math.max(leftHeight, rightHeight) + 1;
}
}
javascript
// 创建二叉树节点
let node15 = new TreeNode(15);
let node7 = new TreeNode(7);
let node20 = new TreeNode(20, node15, node7);
let node9 = new TreeNode(9);
let root = new TreeNode(3, node9, node20);
// 计算二叉树的最大深度
console.log(maxDepth(root)); // 输出:3