目录
2667. 创建 Hello World 函数
2677. 分块数组
2693. 使用自定义上下文调用函数
2695. 包装数组
2703. 返回传递的参数的长度
2704. 相等还是不相等
2705. 精简对象
2715. 执行可取消的延迟函数
2721. 并行执行异步函数
请你编写一个名为 createHelloWorld
的函数。它应该返回一个新的函数,该函数总是返回 "Hello World"
示例
输入:args = [] 输出:"Hello World" 解释: const f = createHelloWorld(); f(); // "Hello World" createHelloWorld 返回的函数应始终返回 "Hello World"。
题解
var createHelloWorld = function() {
return function(...args) {
return "Hello World";
}
};
这段代码定义了一个名为createHelloWorld
的函数。这个函数返回了另一个函数,这个函数可以接受任意数量的参数。这个第二个返回的函数总是返回字符串"Hello World"。
给定一个数组 arr
和一个块大小 size
,返回一个 分块 的数组。分块 的数组包含了 arr
中的原始元素,但是每个子数组的长度都是 size
。如果 arr.length
不能被 size
整除,那么最后一个子数组的长度可能小于 size
。
你可以假设该数组是 JSON.parse
的输出结果。换句话说,它是有效的JSON。
请你在不使用 lodash 的函数 _.chunk
的情况下解决这个问题。
示例
输入:arr = [1,2,3,4,5], size = 1
输出:[[1],[2],[3],[4],[5]]
解释:数组 arr 被分割成了每个只有一个元素的子数组。
题解
function chunkArray(arr, size) {
var chunkedArr = [];
var index = 0;
while (index < arr.length) {
chunkedArr.push(arr.slice(index, index + size));
index += size;
}
return chunkedArr;
}
这个函数chunkArray
接受一个数组arr
和块大小size
作为参数。我们首先创建一个空数组chunkedArr
来存放分块后的数组。然后定义一个变量index
来跟踪当前的起始索引。
我们使用一个while
循环来进行分块。在每次循环中,我们使用slice
方法从原始数组中提取从index
到index + size
之间的元素,然后将这个部分数组添加到chunkedArr
中。接着我们将index
增加size
以跳到下一个分块的起始索引。
最后,我们返回分块后的数组chunkedArr
作为结果。
增强所有函数,使其具有 callPolyfill
方法。该方法接受一个对象 obj
作为第一个参数,以及任意数量的附加参数。obj
成为函数的 this
上下文。附加参数将传递给该函数(即 callPolyfill
方法所属的函数)。
例如,如果有以下函数:
function tax(price, taxRate) {
const totalCost = price * (1 + taxRate);
console.log(`The cost of ${this.item} is ${totalCost}`);
}
调用 tax(10, 0.1)
将输出 "The cost of undefined is 11"
。这是因为 this
上下文未定义。
然而,调用 tax.callPolyfill({item: "salad"}, 10, 0.1)
将输出 "The cost of salad is 11"
。this
上下文被正确设置,函数输出了适当的结果。
请在不使用内置的 Function.call
方法的情况下解决这个问题。
示例 1:
输入:
fn = function add(b) {
return this.a + b;
}
args = [{"a": 5}, 7]
输出:12
解释:
fn.callPolyfill({"a": 5}, 7); // 12
callPolyfill 将 "this" 上下文设置为 {"a": 5} ,并将 7 作为参数传递。
题解
Function.prototype.callPolyfill = function (obj, ...args) {
// 将当前函数的 this 上下文设置为 obj
const context = obj || globalThis;
// 在 obj 上创建一个唯一的属性,以避免命名冲突
const uniqueProp = Symbol();
context[uniqueProp] = this;
// 调用函数,并传递参数
const result = context[uniqueProp](...args);
// 删除临时属性
delete context[uniqueProp];
// 返回函数的执行结果
return result;
};
这段代码定义了一个名为callPolyfill的函数原型方法。该方法的作用是模拟实现Function.prototype.call方法。
首先,在函数内部将当前函数的this上下文设置为obj参数,如果obj参数为undefined或null,则使用globalThis作为this上下文。
然后,在obj对象上创建一个唯一的属性uniqueProp,该属性用于保存当前函数,以避免命名冲突。
接着,调用函数,并将args参数作为参数传递进去,即调用contextuniqueProp。
然后,删除临时属性context[uniqueProp]。
最后,返回函数的执行结果result。
这样通过调用callPolyfill方法,可以模拟实现函数的call方法,将当前函数的this上下文设置为参数obj,并可以传递其他参数给当前函数,并返回函数的执行结果。
创建一个名为 ArrayWrapper
的类,它在其构造函数中接受一个整数数组作为参数。该类应具有以下两个特性:
+
运算符将两个该类的实例相加时,结果值为两个数组中所有元素的总和。String()
函数时,它将返回一个由逗号分隔的括在方括号中的字符串。例如,[1,2,3]
。示例
输入:nums = [[1,2],[3,4]], operation = "Add" 输出:10 解释: const obj1 = new ArrayWrapper([1,2]); const obj2 = new ArrayWrapper([3,4]); obj1 + obj2; // 10
题解
var ArrayWrapper = function(nums) {
this.nums = nums;
};
ArrayWrapper.prototype.valueOf = function() {
return this.nums.reduce((total, currentValue) => total += currentValue, 0)
}
ArrayWrapper.prototype.toString = function() {
return `[${this.nums.join(",")}]`;
}
这段代码定义了一个构造函数 ArrayWrapper
,它接受一个参数 nums
。构造函数内部将 nums
赋值给实例属性 this.nums
。
接下来,ArrayWrapper
的原型对象上定义了两个方法。
ArrayWrapper.prototype.valueOf
方法计算 this.nums
数组中所有元素的总和,使用 reduce
方法将每个元素加到累加器中并返回最终结果。
ArrayWrapper.prototype.toString
方法将 this.nums
数组转化为字符串形式,使用 join
方法将数组中的元素连接成字符串,每个元素之间用逗号分隔,并用方括号 []
包裹返回。
这样,我们可以使用 ArrayWrapper
构造函数创建对象,并通过 valueOf
方法获取数组元素的总和,通过 toString
方法获取数组的字符串表示形式。
请你编写一个函数 argumentsLength
,返回传递给该函数的参数数量。
示例
输入:argsArr = [5]
输出:1
解释:
argumentsLength(5); // 1
只传递了一个值给函数,因此它应返回 1。
题解
var argumentsLength = function(...args) {
return args.length;
};
这段代码定义了一个函数叫做argumentsLength,该函数使用了剩余参数(rest parameter)的语法,即可以接收任意数量的参数。当调用该函数时,传入的参数被收集到一个名为args的数组中。然后,通过返回args的长度,可以得到传入的参数的个数。
请你编写一个名为 expect
的函数,用于帮助开发人员测试他们的代码。它应该接受任何值 val
并返回一个包含以下两个函数的对象。
toBe(val)
接受另一个值并在两个值相等( ===
)时返回 true
。如果它们不相等,则应抛出错误 "Not Equal"
。notToBe(val)
接受另一个值并在两个值不相等( !==
)时返回 true
。如果它们相等,则应抛出错误 "Equal"
。示例
输入:func = () => expect(5).toBe(5) 输出:{"value": true} 解释:5 === 5 因此该表达式返回 true。
题解
function expect(val) {
return {
toBe: function(expected) {
if (val === expected) {
return true;
} else {
throw new Error("Not Equal");
}
},
notToBe: function(expected) {
if (val !== expected) {
return true;
} else {
throw new Error("Equal");
}
}
};
}
这段代码定义了一个名为expect
的函数。该函数接受一个参数val
,并返回一个对象。返回的对象具有两个方法:toBe
和notToBe
。
toBe
方法用于比较val
和expected
的值是否相等。如果相等,则返回true
;否则,抛出一个错误(Error
)信息为"Not Equal"的异常。
notToBe
方法则用于比较val
和expected
的值是否不相等。如果不相等,则返回true
;否则,抛出一个错误(Error
)信息为"Equal"的异常。
这段代码的作用是用于在测试中进行期望值的比较,当比较结果不符合期望时,抛出异常以提示测试失败。
现给定一个对象或数组 obj
,返回一个 精简对象 。精简对象 与原始对象相同,只是将包含 假 值的键移除。该操作适用于对象及其嵌套对象。数组被视为索引作为键的对象。当 Boolean(value)
返回 false
时,值被视为 假 值。
你可以假设 obj
是 JSON.parse
的输出结果。换句话说,它是有效的 JSON。
示例
示例 1:
输入:obj = [null, 0, false, 1] 输出:[1] 解释:数组中的所有假值已被移除。
题解
var compactObject = function(obj) {
// 不是对象就可能是null或者字符,数字(因为题目说是JSON转化,排除函数和奇怪的东西)
if (obj == null || typeof obj !== 'object') {
return obj;
}
// 数组的话可以直接枚举
if (Array.isArray(obj)) {
const res = [];
for (let it of obj) {
const val = compactObject(it);
if (val) res.push(val);
}
return res;
}
// 对象需要把key拿出来
const res = {};
const keys = Object.keys(obj);
for (let key of keys) {
const val = compactObject(obj[key]);
if (val) res[key] = val;
}
return res;
};
这段代码定义了一个名为compactObject的函数。这个函数的作用是将传入的对象进行压缩,去除其中的null、字符串和数字值为空的属性。
函数首先判断传入的参数obj是否是null或者不是对象类型。如果是null或者不是对象类型,就直接返回obj本身。
如果obj是数组类型,函数会创建一个空数组res,并遍历传入的数组obj。对于数组中的每个元素it,函数会用递归调用compactObject函数来获取其压缩后的值,并判断该值是否存在(非null或空字符串)。如果存在,就将其添加到res数组中。最后返回res数组。
如果obj是对象类型,函数会创建一个空对象res,并获取obj的所有属性名,存储在数组keys中。然后对于keys中的每个属性名key,函数会用递归调用compactObject函数来获取该属性值val的压缩后的值,并判断该值是否存在。如果存在,就将其作为属性值添加到res对象中,属性名不变。最后返回res对象。
总结来说,这段代码实现了对传入对象的压缩处理,去除了null、空字符串和数字值为空的属性,返回一个新的压缩后的对象或数组。
现给定一个函数 fn
,一个参数数组 args
和一个以毫秒为单位的超时时间 t
,返回一个取消函数 cancelFn
。
在经过 t
毫秒的延迟后,应该调用 fn
函数,并将 args
作为参数传递。除非 在 t
毫秒的延迟过程中,在 cancelT
毫秒时调用了 cancelFn
。并且在这种情况下,fn
函数不应该被调用。
示例
输入:fn = (x) => x * 5, args = [2], t = 20, cancelT = 50 输出:[{"time": 20, "returned": 10}] 解释: const cancel = cancellable((x) => x * 5, [2], 20); // fn(2) 在 t=20ms 时被调用 setTimeout(cancel, 50); 取消操作被安排在延迟了 cancelT(50毫秒)后进行,这发生在 fn(2) 在20毫秒时执行之后
题解
var cancellable = function(fn, args, t) {
let cancelled = false; // 标志位,初始值为false
const timeoutId = setTimeout(() => {
if (!cancelled) {
fn.apply(null, args);
}
}, t);
function cancelFn() {
cancelled = true; // 取消延迟调用
clearTimeout(timeoutId);
}
return cancelFn;
};
这段代码定义了一个名为cancellable的函数。该函数接受三个参数:fn(函数)、args(函数的参数列表)和t(延迟时间)。
函数内部声明了一个变量cancelled并将其初始化为false,用于标志延迟调用是否被取消。
然后使用setTimeout函数创建一个定时器,并在延迟时间t之后执行一个回调函数。回调函数内部通过检查cancelled的值来确定是否执行传入的函数fn,并使用apply方法调用该函数并传入args作为参数。
接下来,定义了一个名为cancelFn的函数,用于取消延迟调用。在cancelFn函数内,将cancelled的值设置为true,表示取消延迟调用,并通过clearTimeout清除定时器。
最后,返回cancelFn函数作为结果,使得外部调用者可以调用它来取消延迟调用。
给定一个异步函数数组 functions
,返回一个新的 promise 对象 promise
。数组中的每个函数都不接受参数并返回一个 promise。
promise
resolve 条件:
functions
返回的 promise 都成功解析时。promise
的解析值应该是一个按照它们在 functions
中的顺序排列的 promise 的解析值数组。promise
reject 条件:
functions
返回的 promise 被拒绝时。promise
也会被拒绝,并返回第一个拒绝的原因。请在不使用内置的 Promise.all
函数的情况下解决。
示例
输入:functions = [ () => new Promise(resolve => setTimeout(() => resolve(5), 200)) ] 输出:{"t": 200, "resolved": [5]} 解释: promiseAll(functions).then(console.log); // [5] 单个函数在 200 毫秒后以值 5 成功解析。
题解
var promiseAll = async function (functions) {
return new Promise((resolve, reject) => {
let results = []; // 存储解析值的数组
let resolvedCount = 0; // 已解析 promise 的计数器
let rejected = false; // 是否已有 promise 被拒绝的标记
functions.forEach((func, index) => {
func().then((result) => {
// 如果已有 promise 被拒绝,则不再处理后续的解析值
if (rejected) {
return;
}
results[index] = result; // 存储解析值
resolvedCount++; // 计数器加一
// 如果所有 promise 都已解析,则解析结果
if (resolvedCount === functions.length) {
resolve(results);
}
}).catch((reason) => {
// 如果已有 promise 被拒绝,则不再处理后续的拒绝原因
if (rejected) {
return;
}
rejected = true; // 标记已有 promise 被拒绝
reject(reason); // 拒绝并返回拒绝原因
});
});
});
};
这段代码定义了一个名为promiseAll的异步函数,它接受一个functions参数。函数的功能是并行执行传入的promise函数数组,并返回一个新的promise对象。
在函数内部,首先创建了一个promise对象,并使用resolve和reject作为参数创建了一个回调函数。接着定义了一个空数组results,用于存储解析值;一个计数器resolvedCount,用于记录已解析promise的数量;一个标志位rejected,用于标记是否已经有promise被拒绝。
然后通过forEach遍历传入的functions数组,对每个函数func进行处理。对每个函数func调用.then方法,如果之前没有promise被拒绝,则将result存储到results数组的对应索引位置,计数器resolvedCount加一。如果所有的promise都已解析,则调用resolve方法并传入results数组。
如果某个promise被拒绝,则设置rejected为true,并调用reject方法并传入拒绝的原因reason。
最后,返回新创建的promise对象。
总的来说,这段代码实现了一个类似Promise.all的功能,可以并行执行一组promise函数,并在所有promise都解析完成后返回一个包含所有解析值的数组。如果有任何一个promise被拒绝,则返回被拒绝的原因。