第一天
考察知识点: 闭包
2667. 创建 Hello World 函数
var createHelloWorld = function() {
return function(...args) {
return "Hello World"
}
};
2620. 计数器
var createCounter = function(n) {
return function() {
return n++;
};
};
2704. 相等还是不相等
var expect = function(val) {
return {
toBe(val2) {
if (val2 === val) {
return true;
}
throw new Error("Not Equal");
},
notToBe(val2) {
if (val2 !== val) {
return true;
}
throw new Error("Equal");
},
}
};
2665. 计数器 II
var createCounter = function(init) {
let _init = init;
return {
increment() {
init++;
return init;
},
decrement() {
init--;
return init;
},
reset() {
init = _init;
return init;
},
}
};
第二天
考察知识点:基本数组转换
2635. 转换数组中的每个元素
var map = function(arr, fn) {
const res = [];
for (let i = 0; i < arr.length; i++) {
res[i] = fn(arr[i], i);
}
return res;
};
2634. 过滤数组中的元素
/**
* @param {number[]} arr
* @param {Function} fn
* @return {number[]}
*/
var filter = function(arr, fn) {
const filteredArr = [];
let j = 0;
for (let i = 0; i < arr.length; i++) {
if (fn(arr[i], i)) {
filteredArr[j++] = arr[i];
}
}
return filteredArr;
};
2626. 数组归约运算
/**
* @param {number[]} nums
* @param {Function} fn
* @param {number} init
* @return {number}
*/
var reduce = function(nums, fn, init) {
for (let i = 0; i < nums.length; i++) {
init = fn(init, nums[i]);
}
return init;
};
第三天
考察知识点:函数转换
2629. 复合函数
/**
* @param {Function[]} functions
* @return {Function}
*/
var compose = function(functions) {
return function(x) {
let res = x;
for (let i = functions.length - 1; i >= 0; i--) {
res = functions[i](res);
}
return res;
}
};
2703. 返回传递的参数的长度
/**
* @return {number}
*/
var argumentsLength = function(...args) {
return args.length;
};
2666. 只允许一次函数调用
/**
* @param {Function} fn
* @return {Function}
*/
var once = function(fn) {
let called = false;
return function(...args){
if (called) {
return;
}
const res = fn(...args);
called = true;
return res;
}
};
/**
* let fn = (a,b,c) => (a + b + c)
* let onceFn = once(fn)
*
* onceFn(1,2,3); // 6
* onceFn(2,3,6); // returns undefined without calling fn
*/
2623. 记忆函数
/**
* @param {Function} fn
*/
function memoize(fn) {
const map = new Map();
return function(...args) {
const key = args.join('-');
if (map.has(key)) {
return map.get(key);
}
const res = fn(...args);
map.set(key, res);
return res;
}
}
/**
* let callCount = 0;
* const memoizedFn = memoize(function (a, b) {
* callCount += 1;
* return a + b;
* })
* memoizedFn(2, 3) // 5
* memoizedFn(2, 3) // 5
* console.log(callCount) // 1
*/
第四天
考察知识点:Promise和定时器
2723. 添加两个Promise对象
/**
* @param {Promise} promise1
* @param {Promise} promise2
* @return {Promise}
*/
var addTwoPromises = async function(promise1, promise2) {
return Promise.all([promise1, promise2]).then(([val1, val2]) => {
return val1 + val2
})
};
/**
* addTwoPromises(Promise.resolve(2), Promise.resolve(2))
* .then(console.log); // 4
*/
2621. 睡眠函数
/**
* @param {number} millis
*/
async function sleep(millis) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, millis);
})
}
/**
* let t = Date.now()
* sleep(100).then(() => console.log(Date.now() - t)) // 100
*/
2715. 执行可取消的延迟函数
/**
* @param {Function} fn
* @param {Array} args
* @param {number} t
* @return {Function}
*/
var cancellable = function(fn, args, t) {
const timerId = setTimeout(() => fn(...args), t)
return () => {
clearTimeout(timerId);
}
};
/**
* const result = []
*
* const fn = (x) => x * 5
* const args = [2], t = 20, cancelT = 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, cancelT)
*
* setTimeout(() => {
* cancel()
* }, cancelT)
*
* setTimeout(() => {
* console.log(result) // [{"time":20,"returned":10}]
* }, maxT + 15)
*/
2725. 间隔取消
/**
* @param {Function} fn
* @param {Array} args
* @param {number} t
* @return {Function}
*/
var cancellable = function(fn, args, t) {
const timerId = setInterval(() => fn(...args), t);
fn(...args);
return () => {
clearInterval(timerId);
}
};
/**
* const result = []
*
* const fn = (x) => x * 2
* const args = [4], t = 20, cancelT = 110
*
* 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()
* }, cancelT)
*
* setTimeout(() => {
* console.log(result) // [
* // {"time":0,"returned":8},
* // {"time":20,"returned":8},
* // {"time":40,"returned":8},
* // {"time":60,"returned":8},
* // {"time":80,"returned":8},
* // {"time":100,"returned":8}
* // ]
* }, cancelT + t + 15)
*/
2637. 有时间限制的Promise对象
/**
* @param {Function} fn
* @param {number} t
* @return {Function}
*/
var timeLimit = function(fn, t) {
return async function(...args) {
const timerLimitP = new Promise((_, reject) => setTimeout(() => reject("Time Limit Exceeded"), t));
return Promise.race([fn(...args), timerLimitP]);
}
};
/**
* const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
* limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms
*/
2622. 有时间限制的缓存
var TimeLimitedCache = function() {
this.map = new Map();
this.timerIds = {};
};
/**
* @param {number} key
* @param {number} value
* @param {number} time until expiration in ms
* @return {boolean} if un-expired key already existed
*/
TimeLimitedCache.prototype.set = function(key, value, duration) {
const res = this.map.has(key);
this.map.set(key, value);
clearTimeout(this.timerIds[key]);
this.timerIds[key] = setTimeout(() => {
this.map.delete(key);
}, duration);
return res;
};
/**
* @param {number} key
* @return {number} value associated with key
*/
TimeLimitedCache.prototype.get = function(key) {
return this.map.get(key) ?? -1;
};
/**
* @return {number} count of non-expired keys
*/
TimeLimitedCache.prototype.count = function() {
return this.map.size;
};
/**
* Your TimeLimitedCache object will be instantiated and called as such:
* var obj = new TimeLimitedCache()
* obj.set(1, 42, 1000); // false
* obj.get(1) // 42
* obj.count() // 1
*/
2627. 函数防抖
/**
* @param {Function} fn
* @param {number} t milliseconds
* @return {Function}
*/
var debounce = function(fn, t) {
let timerId = null;
return function(...args) {
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
timerId = setTimeout(() => {
fn(...args);
}, t)
}
};
/**
* const log = debounce(console.log, 100);
* log('Hello'); // cancelled
* log('Hello'); // cancelled
* log('Hello'); // Logged at t=100ms
*/
2721. 并行执行异步函数
/**
* @param {Array<Function>} functions
* @return {Promise<any>}
*/
var promiseAll = async function(functions) {
return new Promise((resolve, reject) => {
const ret = [];
let count = 0;
for (let i = 0; i < functions.length; i++) {
functions[i]().then((val) => {
ret[i] = val;
count++;
if (count === functions.length) {
resolve(ret);
}
}).catch(reject);
}
})
};
/**
* const promise = promiseAll([() => new Promise(res => res(42))])
* promise.then(console.log); // [42]
*/
第五天
考察知识点:对象
2727. 判断对象是否为空
/**
* @param {Object | Array} obj
* @return {boolean}
*/
var isEmpty = function(obj) {
return Object.keys(obj).length === 0;
};
2677. 分块数组
/**
* @param {Array} arr
* @param {number} size
* @return {Array[]}
*/
var chunk = function(arr, size) {
const res = [];
let j = 0;
for (let i = 0; i < arr.length; i+=size) {
res[j++] = arr.slice(i, i+size);
}
return res;
};
2619. 数组原型对象的最后一个元素
Array.prototype.last = function() {
return this.length ? this[this.length-1] : -1;
};
/**
* const arr = [1, 2, 3];
* arr.last(); // 3
*/
2631. 分组
/**
* @param {Function} fn
* @return {Array}
*/
Array.prototype.groupBy = function(fn) {
const res = {};
for (let i = 0; i < this.length; i++) {
const cur = this[i];
const key = fn(this[i]);
if (!res[key]) {
res[key] = [];
}
res[key].push(cur);
}
return res;
};
/**
* [1,2,3].groupBy(String) // {"1":[1],"2":[2],"3":[3]}
*/
2724. 排序方式
/**
* @param {Array} arr
* @param {Function} fn
* @return {Array}
*/
var sortBy = function(arr, fn) {
return arr.sort((a, b) => fn(a) - fn(b));
};
2722. 根据ID合并两个数组
/**
* @param {Array} arr1
* @param {Array} arr2
* @return {Array}
*/
var join = function(arr1, arr2) {
let i = 0;
let j = 0;
const res = [];
while(i < arr1.length && j < arr2.length) {
if (arr1[i].id === arr2[j].id) {
res.push({...arr1[i], ...arr2[j]})
i++;
j++;
} else if (arr1[i].id < arr2[j].id) {
res.push(arr1[i]);
i++;
} else {
res.push(arr2[j]);
j++;
}
}
while(i < arr1.length) {
res.push(arr1[i]);
i++;
}
while(j < arr2.length) {
res.push(arr2[j]);
j++;
}
return res;
};
2625. 扁平化嵌套数组
/**
* 递归版本
* @param {any[]} arr
* @param {number} depth
* @return {any[]}
*/
var flat = function (arr, n) {
if (n <= 0) return arr;
const result = [];
arr.forEach(item => result.push(...(Array.isArray(item) ? flat(item, n -1) : [item])));
return result;
};
/**迭代版本
* @param {any[]} arr
* @param {number} depth
* @return {any[]}
*/
var flat = function (arr, n) {
while(n > 0 && arr.some(Array.isArray)) {
arr = [].concat(...arr);
n--;
}
return arr;
};
2705. 精简对象
/**
* @param {Object} obj
* @return {Object}
*/
var compactObject = function(obj) {
if (obj == null || typeof obj !== 'object') {
return obj
}
const ret = Array.isArray(obj) ? [] : {};
if (Array.isArray(obj)) {
for (let item of obj) {
let val = compactObject(item);
if (val) {
ret.push(val);
}
}
return ret;
}
let keys = Object.keys(obj);
for (let key of keys) {
let val = compactObject(obj[key]);
if (val){
ret[key] = val;
}
}
return ret;
};
第六天
考察知识点: 类
2694. 事件发射器
class EventEmitter {
constructor() {
this.subscribers = {};
}
subscribe(event, cb) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(cb);
return {
unsubscribe: () => {
const cbIndex = this.subscribers[event].findIndex(fn => fn === cb);
if (cbIndex > -1) {
this.subscribers[event].splice(cbIndex, 1);
}
}
};
}
emit(event, args = []) {
if (!this.subscribers[event]) {
return [];
}
return this.subscribers[event].map(cb => cb(...args));
}
}
/**
* const emitter = new EventEmitter();
*
* // Subscribe to the onClick event with onClickCallback
* function onClickCallback() { return 99 }
* const sub = emitter.subscribe('onClick', onClickCallback);
*
* emitter.emit('onClick'); // [99]
* sub.unsubscribe(); // undefined
* emitter.emit('onClick'); // []
*/
2695. 包装数组
/**
* @param {number[]} nums
*/
var ArrayWrapper = function(nums) {
this.nums = nums;
};
ArrayWrapper.prototype.valueOf = function() {
return this.nums.reduce((acc, prev) => acc + prev, 0);
}
ArrayWrapper.prototype.toString = function() {
return '[' + this.nums.join(',') + ']'
}
/**
* const obj1 = new ArrayWrapper([1,2]);
* const obj2 = new ArrayWrapper([3,4]);
* obj1 + obj2; // 10
* String(obj1); // "[1,2]"
* String(obj2); // "[3,4]"
*/
2726. 使用方法链的计算器
class Calculator {
/**
* @param {number} value
*/
constructor(value) {
this.val = value;
}
/**
* @param {number} value
* @return {Calculator}
*/
add(value){
this.val += value;
return this;
}
/**
* @param {number} value
* @return {Calculator}
*/
subtract(value){
this.val -= value;
return this;
}
/**
* @param {number} value
* @return {Calculator}
*/
multiply(value) {
this.val *= value;
return this;
}
/**
* @param {number} value
* @return {Calculator}
*/
divide(value) {
if (value === 0) {
throw new Error('Division by zero is not allowed');
}
this.val /= value;
return this;
}
/**
* @param {number} value
* @return {Calculator}
*/
power(value) {
this.val **= value;
return this;
}
/**
* @return {number}
*/
getResult() {
return this.val;
}
}