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

128 阅读10分钟

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

目录

[TOC]

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 时,值被视为 值。

你可以假设 objJSON.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被拒绝,则返回被拒绝的原因。