leetcode----JavaScript 详情题解(3)

目录

2667. 创建 Hello World 函数

2677. 分块数组

2693. 使用自定义上下文调用函数

2695. 包装数组

2703. 返回传递的参数的长度

2704. 相等还是不相等 

2705. 精简对象

 2715. 执行可取消的延迟函数

2721. 并行执行异步函数


2667. 创建 Hello World 函数

请你编写一个名为 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"。

2677. 分块数组

给定一个数组 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方法从原始数组中提取从indexindex + size之间的元素,然后将这个部分数组添加到chunkedArr中。接着我们将index增加size以跳到下一个分块的起始索引。

最后,我们返回分块后的数组chunkedArr作为结果。

2693. 使用自定义上下文调用函数

增强所有函数,使其具有 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,并可以传递其他参数给当前函数,并返回函数的执行结果。

2695. 包装数组

创建一个名为 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 方法获取数组的字符串表示形式。

2703. 返回传递的参数的长度

请你编写一个函数 argumentsLength,返回传递给该函数的参数数量。

示例 

输入:argsArr = [5]
输出:1
解释:
argumentsLength(5); // 1

只传递了一个值给函数,因此它应返回 1。

题解

var argumentsLength = function(...args) {
   return args.length;
};

这段代码定义了一个函数叫做argumentsLength,该函数使用了剩余参数(rest parameter)的语法,即可以接收任意数量的参数。当调用该函数时,传入的参数被收集到一个名为args的数组中。然后,通过返回args的长度,可以得到传入的参数的个数。

2704. 相等还是不相等 

请你编写一个名为 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,并返回一个对象。返回的对象具有两个方法:toBenotToBe

toBe方法用于比较valexpected的值是否相等。如果相等,则返回true;否则,抛出一个错误(Error)信息为"Not Equal"的异常。

notToBe方法则用于比较valexpected的值是否不相等。如果不相等,则返回true;否则,抛出一个错误(Error)信息为"Equal"的异常。

这段代码的作用是用于在测试中进行期望值的比较,当比较结果不符合期望时,抛出异常以提示测试失败。

2705. 精简对象

现给定一个对象或数组 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、空字符串和数字值为空的属性,返回一个新的压缩后的对象或数组。

 2715. 执行可取消的延迟函数

现给定一个函数 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函数作为结果,使得外部调用者可以调用它来取消延迟调用。

2721. 并行执行异步函数

给定一个异步函数数组 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被拒绝,则返回被拒绝的原因。

你可能感兴趣的:(javascript,开发语言,ecmascript)