30 天 JavaScript 挑战
专为 JavaScript 初学者设计
掌握必备 JavaScript 技能
前端人,前端魂,刷完 JS 即入门!
题目地址:https://leetcode.cn/studyplan/30-days-of-javascript/
个人学习笔记:https://github.com/kaimo313/30-days-of-javascript
【001】2667. 创建 Hello World 函数
// 请你编写一个名为 createHelloWorld 的函数。它应该返回一个新的函数,该函数总是返回 "Hello World" 。/*** @return {Function}*/
var createHelloWorld = function () {return function (...args) {return "Hello World";};
};const f = createHelloWorld();
console.log(f());
【002】2620. 计数器
// 给定一个整型参数 n,请你编写并返回一个 counter 函数。
// 这个 counter 函数最初返回 n,每次调用它时会返回前一个值加 1 的值 ( n , n + 1 , n + 2 ,等等)。/*** @param {number} n* @return {Function} counter*/
var createCounter = function (n) {return function () {return n++;};
};const counter = createCounter(10);
console.log(counter());
console.log(counter());
console.log(counter());
【003】2704. 相等还是不相等
// 请你编写一个名为 expect 的函数,用于帮助开发人员测试他们的代码。
// 它应该接受任何值 val 并返回一个包含以下两个函数的对象。// toBe(val) 接受另一个值并在两个值相等( === )时返回 true 。如果它们不相等,则应抛出错误 "Not Equal" 。
// notToBe(val) 接受另一个值并在两个值不相等( !== )时返回 true 。如果它们相等,则应抛出错误 "Equal" 。// 拓展知识:抛出聚合错误(AggregateError)/*** @param {string} val* @return {Object}*/
var expect = function (val) {return {toBe: function (value) {if (val !== value) throw new Error("Not Equal");return true;},notToBe: function (value) {if (val === value) throw new Error("Equal");return true;}};
};console.log(expect(5).toBe(5));
console.log(expect(5).notToBe(5));
【004】2665. 计数器 II
// 请你写一个函数 createCounter。这个函数接收一个初始的整数值 init。并返回一个包含三个函数的对象。
// 这三个函数是:
// increment() 将当前值加 1 并返回。
// decrement() 将当前值减 1 并返回。
// reset() 将当前值设置为 init 并返回。/*** @param {integer} init* @return { increment: Function, decrement: Function, reset: Function }*/
var createCounter = function (init) {let count = init;return {increment: () => ++count,decrement: () => --count,reset: () => (count = init)};
};const counter = createCounter(5);
console.log(counter.increment());
console.log(counter.reset());
console.log(counter.decrement());
【005】2635. 转换数组中的每个元素
// 编写一个函数,这个函数接收一个整数数组 arr 和一个映射函数 fn ,通过该映射函数返回一个新的数组。
// 返回数组的创建语句应为 returnedArray[i] = fn(arr[i], i) 。
// 请你在不使用内置方法 Array.map 的前提下解决这个问题。/*** @param {number[]} arr* @param {Function} fn* @return {number[]}*/
var map = function (arr, fn) {var returnedArray = [];for (var i = 0; i < arr.length; i++) {returnedArray.push(fn(arr[i], i));}return returnedArray;
};
【006】2634. 过滤数组中的元素
// 给定一个整数数组 arr 和一个过滤函数 fn,并返回一个过滤后的数组 filteredArr 。
// fn 函数接受一个或两个参数:
// arr[i] - arr 中的数字
// i - arr[i] 的索引
// filteredArr 应该只包含使表达式 fn(arr[i], i) 的值为 真值 的 arr 中的元素。
// 真值 是指 Boolean(value) 返回参数为 true 的值。
// 请在不使用内置的 Array.filter 方法的情况下解决该问题。/*** @param {number[]} arr* @param {Function} fn* @return {number[]}*/
var filter = function (arr, fn) {var filteredArr = [];for (var i = 0; i < arr.length; i++) {if (fn(arr[i], i)) {filteredArr.push(arr[i]);}}return filteredArr;
};
【007】2626. 数组归约运算
// 给定一个整数数组 nums、一个 reducer 函数 fn 和一个初始值 init,返回通过依次对数组的每个元素执行 fn 函数得到的最终结果。
// 通过以下操作实现这个结果:val = fn(init, nums[0]),val = fn(val, nums[1]),val = fn(val, nums[2]),... 直到处理数组中的每个元素。然后返回 val 的最终值。
// 如果数组的长度为 0,则函数应返回 init。
// 请你在不使用内置数组方法的 Array.reduce 前提下解决这个问题。/*** @param {number[]} nums* @param {Function} fn* @param {number} init* @return {number}*/
var reduce = function (nums, fn, init) {var val = init;for (var i = 0; i < nums.length; i++) {val = fn(val, nums[i]);}return val;
};
【008】2629. 复合函数
// 请你编写一个函数,它接收一个函数数组 [f1, f2, f3,…, fn] ,并返回一个新的函数 fn ,它是函数数组的 复合函数 。
// [f(x), g(x), h(x)] 的 复合函数 为 fn(x) = f(g(h(x))) 。
// 一个空函数列表的 复合函数 是 恒等函数 f(x) = x 。
// 你可以假设数组中的每个函数接受一个整型参数作为输入,并返回一个整型作为输出。/*** @param {Function[]} functions* @return {Function}*/
var compose = function (functions) {// 使用 reduceRight// return function (x) {// return functions.reduceRight((p, c) => c(p), x);// };return function (x) {if (functions.length === 0) return x;let input = x;for (const func of functions.reverse()) {input = func(input);}return input;};
};const fn = compose([(x) => x + 1, (x) => 2 * x]);
console.log(fn(4)); // 9
【009】2703. 返回传递的参数的长度
// 请你编写一个函数 argumentsLength,返回传递给该函数的参数数量。
/*** @param {...(null|boolean|number|string|Array|Object)} args* @return {number}*/
var argumentsLength = function (...args) {return args.length;
};console.log(argumentsLength(1, 2, 3)); // 3
【010】2666. 只允许一次函数调用
// 给定一个函数 fn ,它返回一个新的函数,返回的函数与原始函数完全相同,只不过它确保 fn 最多被调用一次。// 第一次调用返回的函数时,它应该返回与 fn 相同的结果。
// 第一次后的每次调用,它应该返回 undefined 。/*** @param {Function} fn* @return {Function}*/
var once = function (fn) {var called = false;return function (...args) {if (!called) {called = true;return fn.apply(this, args);}};
};let fn = (a, b, c) => a + b + c;
let onceFn = once(fn);console.log(onceFn(1, 2, 3)); // 6
console.log(onceFn(2, 3, 6)); // returns undefined without calling fn
【011】2623. 记忆函数
// 请你编写一个函数,它接收另一个函数作为输入,并返回该函数的 记忆化 后的结果。// 记忆函数 是一个对于相同的输入永远不会被调用两次的函数。相反,它将返回一个缓存值。// 你可以假设有 3 个可能的输入函数:sum 、fib 和 factorial 。// sum 接收两个整型参数 a 和 b ,并返回 a + b 。
// fib 接收一个整型参数 n ,如果 n <= 1 则返回 1,否则返回 fib (n - 1) + fib (n - 2)。
// factorial 接收一个整型参数 n ,如果 n <= 1 则返回 1 ,否则返回 factorial(n - 1) * n 。
/*** @param {Function} fn* @return {Function}*/
function memoize(fn) {var cache = {};return function (...args) {var key = JSON.stringify(args);if (key in cache) {return cache[key];} else {var res = fn(...args);cache[key] = res;return res;}};
}// var memoize = (fn, cache = {}) => (...args) => cache[args.join()] ?? (cache[args.join()] = fn(...args))let callCount = 0;
const memoizedFn = memoize(function (a, b) {callCount += 1;return a + b;
});
console.log(memoizedFn(2, 3)); // 5
console.log(memoizedFn(2, 3)); // 5
console.log(callCount); // 1
【012】2723. 两个 Promise 对象相加
// 给定两个 promise 对象 promise1 和 promise2,返回一个新的 promise。promise1 和 promise2 都会被解析为一个数字。
// 返回的 Promise 应该解析为这两个数字的和。// 提示:promise1 和 promise2 都是被解析为一个数字的 promise 对象/*** @param {Promise} promise1* @param {Promise} promise2* @return {Promise}*/
var addTwoPromises = async function (promise1, promise2) {return (await promise1) + (await promise2);
};console.log(addTwoPromises(Promise.resolve(2), Promise.resolve(2)).then((res) => {console.log(res);})
); // 4
【013】2621. 睡眠函数
// 请你编写一个异步函数,它接收一个正整数参数 millis ,并休眠 millis 毫秒。要求此函数可以解析任何值。/*** @param {number} millis* @return {Promise}*/
async function sleep(millis) {return new Promise((resolve) => {setTimeout(resolve, millis);});
}let t = Date.now();
sleep(100).then(() => console.log(Date.now() - t)); // 100
【014】2715. 执行可取消的延迟函数
// 给定一个函数 fn ,一个参数数组 args 和一个以毫秒为单位的超时时间 t ,返回一个取消函数 cancelFn 。// 在 cancelTimeMs 的延迟后,返回的取消函数 cancelFn 将被调用。// setTimeout(cancelFn, cancelTimeMs)
// 最初,函数 fn 的执行应该延迟 t 毫秒。// 如果在 t 毫秒的延迟之前调用了函数 cancelFn,它应该取消 fn 的延迟执行。
// 否则,如果在指定的延迟 t 内没有调用 cancelFn,则应执行 fn,并使用提供的 args 作为参数。// fn 是一个函数
// args 是一个有效的 JSON 数组
// 1 <= args.length <= 10
// 20 <= t <= 1000
// 10 <= cancelTimeMs <= 1000/*** @param {Function} fn* @param {Array} args* @param {number} t* @return {Function}*/
var cancellable = function (fn, args, t) {var timer = setTimeout(() => {fn(...args);}, t);return function () {clearTimeout(timer);};
};const result = [];const fn = (x) => x * 5;
const args = [2],t = 20,cancelTimeMs = 50;const start = performance.now();const log = (...argsArr) => {const diff = Math.floor(performance.now() - start);result.push({ time: diff, returned: fn(...argsArr) });
};const cancel = cancellable(log, args, t);const maxT = Math.max(t, cancelTimeMs);setTimeout(cancel, cancelTimeMs);setTimeout(() => {console.log(result); // [{"time":20,"returned":10}]
}, maxT + 15);
【015】2725. 间隔取消
// 现给定一个函数 fn,一个参数数组 args 和一个时间间隔 t,返回一个取消函数 cancelFn。// 在经过 cancelTimeMs 毫秒的延迟后,将调用返回的取消函数 cancelFn。// setTimeout(cancelFn, cancelTimeMs)
// 函数 fn 应立即使用参数 args 调用,然后每隔 t 毫秒调用一次,直到在 cancelTimeMs 毫秒时调用 cancelFn。/*** @param {Function} fn* @param {Array} args* @param {number} t* @return {Function}*/
var cancellable = function (fn, args, t) {fn(...args);var timer = setInterval(() => {fn(...args);}, t);return () => clearInterval(timer);
};const result = [];const fn = (x) => x * 2;
const args = [4],t = 35,cancelTimeMs = 190;const start = performance.now();const log = (...argsArr) => {const diff = Math.floor(performance.now() - start);result.push({ time: diff, returned: fn(...argsArr) });
};const cancel = cancellable(log, args, t);setTimeout(cancel, cancelTimeMs);setTimeout(() => {console.log(result); // [// {"time":0,"returned":8},// {"time":35,"returned":8},// {"time":70,"returned":8},// {"time":105,"returned":8},// {"time":140,"returned":8},// {"time":175,"returned":8}// ]
}, cancelTimeMs + t + 15);
【016】2637. 有时间限制的 Promise 对象
// 请你编写一个函数,它接受一个异步函数 fn 和一个以毫秒为单位的时间 t。它应根据限时函数返回一个有 限时 效果的函数。
// 函数 fn 接受提供给 限时 函数的参数。// 限时 函数应遵循以下规则:// 如果 fn 在 t 毫秒的时间限制内完成,限时 函数应返回结果。
// 如果 fn 的执行超过时间限制,限时 函数应拒绝并返回字符串 "Time Limit Exceeded" 。// 提示:// 0 <= inputs.length <= 10
// 0 <= t <= 1000
// fn 返回一个 Promise 对象/*** @param {Function} fn* @param {number} t* @return {Function}*/
var timeLimit = function (fn, t) {return async function (...args) {return new Promise(async (resolve, reject) => {var timer = setTimeout(() => {reject("Time Limit Exceeded");}, t);try {var res = await fn(args);resolve(res);} catch (error) {reject(error);}clearTimeout(timer);});};
};// var timeLimit = function (fn, t) {
// return async function (...args) {
// const timeLimitPromise = new Promise((resolve, reject) => {
// setTimeout(() => reject("Time Limit Exceeded"), t);
// });
// const returnedPromise = fn(...args);
// return Promise.race([timeLimitPromise, returnedPromise]);
// };
// };const limited = timeLimit((t) => new Promise((res) => setTimeout(res, t)), 100);
limited(150).catch(console.log); // "Time Limit Exceeded" at t=100ms
【017】2622. 有时间限制的缓存
// 编写一个类,它允许获取和设置键-值对,并且每个键都有一个 过期时间 。// 该类有三个公共方法:// set(key, value, duration) :接收参数为整型键 key 、整型值 value 和以毫秒为单位的持续时间 duration 。
// 一旦 duration 到期后,这个键就无法访问。
// 如果相同的未过期键已经存在,该方法将返回 true ,否则返回 false 。
// 如果该键已经存在,则它的值和持续时间都应该被覆盖。// get(key) :如果存在一个未过期的键,它应该返回这个键相关的值。否则返回 -1 。// count() :返回未过期键的总数。var TimeLimitedCache = function () {this.cache = new Map();
};/*** @param {number} key* @param {number} value* @param {number} duration time until expiration in ms* @return {boolean} if un-expired key already existed*/
TimeLimitedCache.prototype.set = function (key, value, duration) {let obj = this.cache.get(key);if (obj) {clearTimeout(obj.timeout);}this.cache.set(key, {value,timeout: setTimeout(() => {this.cache.delete(key);}, duration)});return Boolean(obj);
};/*** @param {number} key* @return {number} value associated with key*/
TimeLimitedCache.prototype.get = function (key) {return this.cache.has(key) ? this.cache.get(key).value : -1;
};/*** @return {number} count of non-expired keys*/
TimeLimitedCache.prototype.count = function () {return this.cache.size;
};const timeLimitedCache = new TimeLimitedCache();console.log(timeLimitedCache.set(1, 42, 1000)); // false
console.log(timeLimitedCache.get(1)); // 42
console.log(timeLimitedCache.count()); // 1
【018】2627. 函数防抖
// 请你编写一个函数,接收参数为另一个函数和一个以毫秒为单位的时间 t ,并返回该函数的 函数防抖 后的结果。// 函数防抖 方法是一个函数,它的执行被延迟了 t 毫秒,如果在这个时间窗口内再次调用它,它的执行将被取消。
// 你编写的防抖函数也应该接收传递的参数。// 例如,假设 t = 50ms ,函数分别在 30ms 、 60ms 和 100ms 时调用。
// 前两个函数调用将被取消,第三个函数调用将在 150ms 执行。
// 如果改为 t = 35ms ,则第一个调用将被取消,第二个调用将在 95ms 执行,第三个调用将在 135ms 执行。/*** @param {Function} fn* @param {number} t milliseconds* @return {Function}*/
var debounce = function (fn, t) {var timer = null;return function (...args) {if (timer) {clearTimeout(timer);}timer = setTimeout(() => {fn.apply(this, args);}, t);};
};const log = debounce(console.log, 100);
log("Hello"); // cancelled
log("Hello"); // cancelled
log("Hello"); // Logged at t=100ms
【019】2721. 并行执行异步函数
// 给定一个异步函数数组 functions,返回一个新的 promise 对象 promise。
// 数组中的每个函数都不接受参数并返回一个 promise。所有的 promise 都应该并行执行。// promise resolve 条件:// 当所有从 functions 返回的 promise 都成功的并行解析时。
// promise 的解析值应该是一个按照它们在 functions 中的顺序排列的 promise 的解析值数组。
// promise 应该在数组中的所有异步函数并行执行完成时解析。// promise reject 条件:// 当任何从 functions 返回的 promise 被拒绝时。promise 也会被拒绝,并返回第一个拒绝的原因。/*** @param {Array<Function>} functions* @return {Promise<any>}*/
var promiseAll = async function (functions) {return new Promise((resolve, reject) => {if (functions.length === 0) {resolve([]);return;}const results = new Array(functions.length).fill(null);let count = 0;functions.forEach((el, index) => {el().then((res) => {results[index] = res;count++;if (count === functions.length) {resolve(results);}}).catch((err) => {reject(err);});});});
};const promise = promiseAll([() => new Promise((res) => res(42))]);
promise.then(console.log); // [42]
【020】2727. 判断对象是否为空
// 给定一个对象或数组,判断它是否为空。// 一个空对象不包含任何键值对。
// 一个空数组不包含任何元素。
// 你可以假设对象或数组是通过 JSON.parse 解析得到的。/*** @param {Object|Array} obj* @return {boolean}*/
// 时间、空间复杂度 O(n)
var isEmpty = function (obj) {return Object.keys(obj).length === 0;
};// 时间、空间复杂度 O(1)
// var isEmpty = function (obj) {
// for (let a in obj) return false;
// return true;
// };
【021】2677. 分块数组
// 给定一个数组 arr 和一个块大小 size ,返回一个 分块 的数组。
// 分块 的数组包含了 arr 中的原始元素,但是每个子数组的长度都是 size 。
// 如果 arr.length 不能被 size 整除,那么最后一个子数组的长度可能小于 size 。// 你可以假设该数组是 JSON.parse 的输出结果。换句话说,它是有效的JSON。// 请你在不使用 lodash 的函数 _.chunk 的情况下解决这个问题。/*** @param {Array} arr* @param {number} size* @return {Array}*/
// var chunk = function (arr, size) {
// const result = [];
// for (let i = 0; i < arr.length; i += size) {
// result.push(arr.slice(i, i + size));
// }
// return result;
// };// var chunk = function (arr, size) {
// return arr.reduce((pre, next, index) => {
// const lastChunk = pre[pre.length - 1];
// if (!lastChunk || lastChunk.length === size) {
// pre.push([next]);
// } else {
// lastChunk.push(next);
// }
// return pre;
// }, []);
// };var chunk = function (arr, size) {return Array.from({ length: Math.ceil(arr.length / size) }, function (_, index) {return arr.slice(index * size, index * size + size);});
};console.log(chunk([1, 2, 3, 4, 5], 2));
【022】2619. 数组原型对象的最后一个元素
// 请你编写一段代码实现一个数组方法,使任何数组都可以调用 array.last() 方法,这个方法将返回数组最后一个元素。
// 如果数组中没有元素,则返回 -1 。/*** @return {null|boolean|number|string|Array|Object}*/
Array.prototype.last = function () {return this.length === 0 ? -1 : this[this.length - 1];
};const arr = [1, 2, 3];
arr.last(); // 3
【023】2631. 分组
// 请你编写一段可应用于所有数组的代码,使任何数组调用 array. groupBy(fn) 方法时,它返回对该数组 分组后 的结果。// 数组 分组 是一个对象,其中的每个键都是 fn(arr[i]) 的输出的一个数组,该数组中含有原数组中具有该键的所有项。// 提供的回调函数 fn 将接受数组中的项并返回一个字符串类型的键。// 每个值列表的顺序应该与元素在数组中出现的顺序相同。任何顺序的键都是可以接受的。/*** @param {Function} fn* @return {Object}*/
Array.prototype.groupBy = function (fn) {return this.reduce((acc, val) => {const key = fn(val);if (!acc[key]) {acc[key] = [];}acc[key].push(val);return acc;}, {});
};console.log([1, 2, 3].groupBy(String)); // {"1":[1],"2":[2],"3":[3]}
【024】2724. 排序方式
// 给定一个数组 arr 和一个函数 fn,返回一个排序后的数组 sortedArr。
// 你可以假设 fn 只返回数字,并且这些数字决定了 sortedArr 的排序顺序。
// sortedArr 必须按照 fn 的输出值 升序 排序。// 你可以假设对于给定的数组,fn 不会返回重复的数字。/*** @param {Array} arr* @param {Function} fn* @return {Array}*/
var sortBy = function (arr, fn) {return arr.sort(function (a, b) {return fn(a) - fn(b);});
};
【025】2722. 根据 ID 合并两个数组
// 现给定两个数组 arr1 和 arr2 ,返回一个新的数组 joinedArray 。
// 两个输入数组中的每个对象都包含一个 id 字段。
// joinedArray 是一个通过 id 将 arr1 和 arr2 连接而成的数组。
// joinedArray 的长度应为唯一值 id 的长度。返回的数组应按 id 升序 排序。// 如果一个 id 存在于一个数组中但不存在于另一个数组中,则该对象应包含在结果数组中且不进行修改。
// 如果两个对象共享一个 id ,则它们的属性应进行合并:
// 如果一个键只存在于一个对象中,则该键值对应该包含在对象中。
// 如果一个键在两个对象中都包含,则 arr2 中的值应覆盖 arr1 中的值。/*** @param {Array} arr1* @param {Array} arr2* @return {Array}*/
var join = function (arr1, arr2) {let temp = {};arr1.concat(arr2).forEach((item) => {if (!temp[item.id]) {temp[item.id] = item;} else {temp[item.id] = Object.assign(temp[item.id], item);}});return Object.values(temp).sort((a, b) => a.id - b.id);
};console.log(join([{ id: 1, b: { b: 94 }, v: [4, 3], y: 48 }], [{ id: 1, b: { c: 84 }, v: [1, 3] }]));// 另外还可以使用双指针实现
【026】2625. 扁平化嵌套数组
// 请你编写一个函数,它接收一个 多维数组 arr 和它的深度 n ,并返回该数组的 扁平化 后的结果。// 多维数组 是一种包含整数或其他 多维数组 的递归数据结构。// 数组 扁平化 是对数组的一种操作,定义是将原数组部分或全部子数组删除,并替换为该子数组中的实际元素。
// 只有当嵌套的数组深度大于 n 时,才应该执行扁平化操作。第一层数组中元素的深度被认为是 0。// 请在没有使用内置方法 Array.flat 的前提下解决这个问题。/*** @param {Array} arr* @param {number} depth* @return {Array}*/
var flat = function (arr, n) {let result = [];arr.forEach((item) => {if (Array.isArray(item) && n > 0) {result.push(...flat(item, n - 1));} else {result.push(item);}});return result;
};
【027】2705. 精简对象
// 现给定一个对象或数组 obj,返回一个 精简对象 。
// 精简对象 与原始对象相同,只是将包含 假 值的键移除。
// 该操作适用于对象及其嵌套对象。
// 数组被视为索引作为键的对象。
// 当 Boolean(value) 返回 false 时,值被视为 假 值。// 你可以假设 obj 是 JSON.parse 的输出结果。换句话说,它是有效的 JSON。/*** @param {Object|Array} obj* @return {Object|Array}*/
var compactObject = function (obj) {if (Array.isArray(obj)) {let result = [];obj.forEach((ele) => {if (Boolean(ele)) {result.push(compactObject(ele));}});return result;} else if (typeof obj === "object") {let temp = {};for (let key in obj) {if (Boolean(obj[key])) {temp[key] = compactObject(obj[key]);}}return temp;} else {return obj;}
};
【028】2694. 事件发射器
// 设计一个 EventEmitter 类。这个接口与 Node.js 或 DOM 的 Event Target 接口相似,但有一些差异。
// EventEmitter 应该允许订阅事件和触发事件。// 你的 EventEmitter 类应该有以下两个方法:// subscribe - 这个方法接收两个参数:
// 一个作为字符串的事件名和一个回调函数。当事件被触发时,这个回调函数将被调用。
// 一个事件应该能够有多个监听器。当触发带有多个回调函数的事件时,应按照订阅的顺序依次调用每个回调函数。应返回一个结果数组。
// 你可以假设传递给 subscribe 的回调函数都不是引用相同的。
// subscribe 方法还应返回一个对象,其中包含一个 unsubscribe 方法,使用户可以取消订阅。
// 当调用 unsubscribe 方法时,回调函数应该从订阅列表中删除,并返回 undefined。
// emit - 这个方法接收两个参数:一个作为字符串的事件名和一个可选的参数数组,这些参数将传递给回调函数。
// 如果没有订阅给定事件的回调函数,则返回一个空数组。
// 否则,按照它们被订阅的顺序返回所有回调函数调用的结果数组。class EventEmitter {constructor() {this.subscribers = {};}/*** @param {string} eventName* @param {Function} callback* @return {Object}*/subscribe(eventName, callback) {if (this.subscribers[eventName]) {this.subscribers[eventName].push(callback);} else {this.subscribers[eventName] = [callback];}return {unsubscribe: () => {this.subscribers[eventName].splice(this.subscribers[eventName].indexOf(callback), 1);}};}/*** @param {string} eventName* @param {Array} args* @return {Array}*/emit(eventName, args = []) {return this.subscribers[eventName]?.map((callback) => callback(...args)) || [];}
}const emitter = new EventEmitter();// Subscribe to the onClick event with onClickCallback
function onClickCallback() {return 99;
}
const sub = emitter.subscribe("onClick", onClickCallback);console.log(emitter.emit("onClick")); // [99]
console.log(sub.unsubscribe()); // undefined
console.log(emitter.emit("onClick")); // []
【029】2695. 包装数组
// 创建一个名为 ArrayWrapper 的类,它在其构造函数中接受一个整数数组作为参数。该类应具有以下两个特性:// 当使用 + 运算符将两个该类的实例相加时,结果值为两个数组中所有元素的总和。
// 当在实例上调用 String() 函数时,它将返回一个由逗号分隔的括在方括号中的字符串。例如,[1,2,3] 。/*** @param {number[]} nums* @return {void}*/
var ArrayWrapper = function (nums) {this.nums = nums;
};/*** @return {number}*/
ArrayWrapper.prototype.valueOf = function () {return this.nums.reduce((acc, val) => acc + val, 0);
};/*** @return {string}*/
ArrayWrapper.prototype.toString = function () {// return JSON.stringify(this.nums);return `[${this.nums}]`;
};const obj1 = new ArrayWrapper([1, 2]);
const obj2 = new ArrayWrapper([3, 4]);console.log(obj1 + obj2); // 10
console.log(String(obj1)); // "[1,2]"
console.log(String(obj2)); // "[3,4]"
【030】2726. 使用方法链的计算器
// 设计一个类 Calculator 。
// 该类应提供加法、减法、乘法、除法和乘方等数学运算功能。
// 同时,它还应支持连续操作的方法链式调用。
// Calculator 类的构造函数应接受一个数字作为 result 的初始值。// 你的 Calculator 类应包含以下方法:// add - 将给定的数字 value 与 result 相加,并返回更新后的 Calculator 对象。
// subtract - 从 result 中减去给定的数字 value ,并返回更新后的 Calculator 对象。
// multiply - 将 result 乘以给定的数字 value ,并返回更新后的 Calculator 对象。
// divide - 将 result 除以给定的数字 value ,并返回更新后的 Calculator 对象。
// 如果传入的值为 0 ,则抛出错误 "Division by zero is not allowed" 。
// power - 计算 result 的幂,指数为给定的数字 value ,并返回更新后的 Calculator 对象。(result = result ^ value )
// getResult - 返回 result 的值。// 结果与实际结果相差在 10的-5次幂 范围内的解被认为是正确的。class Calculator {/*** @param {number} value*/constructor(value) {this.result = value;}/*** @param {number} value* @return {Calculator}*/add(value) {this.result += value;return this;}/*** @param {number} value* @return {Calculator}*/subtract(value) {this.result -= value;return this;}/*** @param {number} value* @return {Calculator}*/multiply(value) {this.result *= value;return this;}/*** @param {number} value* @return {Calculator}*/divide(value) {if (value === 0) {throw new Error("Division by zero is not allowed");}this.result /= value;return this;}/*** @param {number} value* @return {Calculator}*/power(value) {this.result = Math.pow(this.result, value);// this.result **= value;return this;}/*** @return {number}*/getResult() {return this.result;}
}