实现一个名为 myFrom 的函数
请你实现一个名为 myFrom 的函数,该函数模仿原生 ES6 中 Array.from 的行为。myFrom 接收三个参数:
- arrayLike:一个类似数组的对象或可迭代对象(例如:具有
length属性的对象、字符串等)。 - mapFn (可选):若提供,则应为一个函数,用于对每个元素进行映射,如果存在则对每个元素执行
mapFn(element, index),并将返回结果插入新数组。 - thisArg (可选):若
mapFn存在且thisArg被提供,则调用映射函数时用thisArg作为this的值。
此外,你需要实现两个辅助函数:
- toInteger(value):转换
value为数值类型,如果不能转换则返回0;对正负数取绝对值并向下取整,再根据原始符号返回结果。 - toLength(value):保证
value转换成整数后位于合法范围 [0, Number.MAX_SAFE_INTEGER] 内(Number.MAX_SAFE_INTEGER等于 2^53 - 1)。
题目要求
输入验证
- 当
arrayLike为null或undefined时,抛出TypeError异常,提示"Array.from requires an array-like object - not null or undefined"。 - 当提供了
mapFn参数,但其类型不是函数时,抛出TypeError异常,提示"Array.from when provided mapFn must be a function"。
- 当
辅助函数
- 实现
toInteger(value),将传入值转换为数值,并按照要求返回整数。要求对NaN返回0,对无穷大或 0 直接返回其值,否则返回带符号的向下取整结果。 - 实现
toLength(value),利用toInteger保证返回值在[0, Number.MAX_SAFE_INTEGER]之间。
- 实现
数组转换
- 使用
Object(arrayLike)获取输入的对象副本,并通过toLength得到合法的长度,创建一个新的数组(或数组状对象)。 - 遍历
arrayLike每个索引的位置,对于每个元素:- 如果存在
mapFn,则:- 当
thisArg被提供,以thisArg作为this调用mapFn(iValue, i); - 否则直接调用
mapFn(iValue, i)。
- 当
- 如果不传入
mapFn,则直接将原始元素复制到新数组中。
- 如果存在
- 最终返回新生成的数组。
- 使用
示例和测试要求
- 输入:
myFrom({0: 'a', 1: 'b', length: 2})应返回['a', 'b']。 - 输入:
myFrom('hello')应返回['h', 'e', 'l', 'l', 'o']。 - 输入:js应返回
function addIndex(el, i) { return el + i } myFrom({ 0: 'a', 1: 'b', length: 2 }, addIndex)['a0', 'b1']。 - 输入:若
mapFn存在但其类型不是函数,则抛出相应错误。
- 输入:
题目模版
js
/**
* 自定义实现 Array.from 方法,将类数组对象或可迭代对象转换为数组。
*
* @param {ArrayLike|Iterable} arrayLike - 类数组对象或可迭代对象。
* @param {Function} [mapFn] - 可选的映射函数,用于对每个元素进行处理。
* @param {*} [thisArg] - 可选的上下文对象,用于绑定 `mapFn` 的 `this` 值。
* @returns {Array} 返回一个新数组,包含从 `arrayLike` 转换而来的元素。
* @throws {TypeError} 如果 `arrayLike` 为 null 或 undefined,抛出类型错误。
* @throws {TypeError} 如果提供的 `mapFn` 不是函数,抛出类型错误。
*/
export default function myFrom(arrayLike, mapFn, thisArg) {
}js
// 类型定义
interface ArrayLike<T> {
readonly length: number
readonly [n: number]: T
}
type MapFunction<T, U> = (value: T, index: number) => U
/**
* 自定义实现 Array.from 方法,将类数组对象或可迭代对象转换为数组。
*
* @param arrayLike - 类数组对象或可迭代对象。
* @param mapFn - 可选的映射函数,用于对每个元素进行处理。
* @param thisArg - 可选的上下文对象,用于绑定 `mapFn` 的 `this` 值。
* @returns 返回一个新数组,包含从 `arrayLike` 转换而来的元素。
* @throws 如果 `arrayLike` 为 null 或 undefined,抛出类型错误。
* @throws 如果提供的 `mapFn` 不是函数,抛出类型错误。
*/
export default function myFrom<T, U = T>(
arrayLike: ArrayLike<T> | Iterable<T> | null | undefined,
mapFn?: MapFunction<T, U>,
thisArg?: any,
): U[] {
}测试代码
js
import { describe, expect, it } from 'vitest'
import myFrom from './myFrom'
describe('myFrom function', () => {
it('应正确处理基本的数组类对象', () => {
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 }
const result = myFrom(arrayLike)
expect(result).toEqual(['a', 'b', 'c'])
})
it('应正确处理 length 为负数的情况', () => {
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: -1 }
const result = myFrom(arrayLike)
expect(result).toEqual([])
})
it('应正确处理字符串', () => {
const result = myFrom('abc')
expect(result).toEqual(['a', 'b', 'c'])
})
it('应正确处理空的数组类对象', () => {
const arrayLike = { length: 0 }
const result = myFrom(arrayLike)
expect(result).toEqual([])
})
it('应正确应用mapFn', () => {
const arrayLike = { 0: 1, 1: 2, 2: 3, length: 3 }
const result = myFrom(arrayLike, value => value * 2)
expect(result).toEqual([2, 4, 6])
})
it('应正确处理thisArg', () => {
const arrayLike = { 0: 1, 1: 2, 2: 3, length: 3 }
const context = { multiplier: 3 }
const result = myFrom(
arrayLike,
function (value) {
return value * this.multiplier
},
context,
)
expect(result).toEqual([3, 6, 9])
})
it('应抛出错误,当arrayLike为null或undefined时', () => {
expect(() => myFrom(null)).toThrow(TypeError)
expect(() => myFrom(undefined)).toThrow(TypeError)
})
it('应抛出错误,当mapFn不是函数时', () => {
const arrayLike = { 0: 1, 1: 2, length: 2 }
expect(() => myFrom(arrayLike, 123)).toThrow(TypeError)
})
it('应正确处理稀疏数组类对象', () => {
const arrayLike = { 0: 'a', 2: 'c', length: 4 }
const result = myFrom(arrayLike)
expect(result).toEqual(['a', undefined, 'c', undefined])
expect(result.length).toBe(4)
})
it('应正确处理长度超出Number.MAX_SAFE_INTEGER的情况', () => {
const arrayLike = { length: Number.MAX_SAFE_INTEGER + 1 }
expect(() => myFrom(arrayLike)).toThrow(RangeError)
})
it('应正确处理负数长度', () => {
const arrayLike = { length: -5 }
const result = myFrom(arrayLike)
expect(result).toEqual([])
expect(result.length).toBe(0)
})
it('应正确处理可迭代对象(如Set)', () => {
const set = new Set([1, 2, 3])
const result = myFrom(set)
expect(result).toEqual([1, 2, 3])
})
it('应正确处理可迭代对象(如Map)', () => {
const map = new Map([
['a', 1],
['b', 2],
])
const result = myFrom(map)
expect(result).toEqual([
['a', 1],
['b', 2],
])
})
it('应正确处理带有非数字length属性的对象', () => {
const arrayLike = { 0: 'a', 1: 'b', length: '2' }
const result = myFrom(arrayLike)
expect(result).toEqual(['a', 'b'])
})
it('应正确处理length为NaN的情况', () => {
const arrayLike = { 0: 'a', length: Number.NaN }
const result = myFrom(arrayLike)
expect(result).toEqual([])
})
it('应正确处理mapFn的第二个参数index', () => {
const arrayLike = { 0: 10, 1: 20, length: 2 }
const result = myFrom(arrayLike, (v, i) => i)
expect(result).toEqual([0, 1])
})
it('应正确处理mapFn返回undefined的情况', () => {
const arrayLike = { 0: 1, 1: 2, length: 2 }
const result = myFrom(arrayLike, () => undefined)
expect(result).toEqual([undefined, undefined])
})
it('应正确处理thisArg为null的情况', () => {
const arrayLike = { 0: 2, 1: 4, length: 2 }
function fn(v) {
return this ? this.x * v : v
}
const result = myFrom(arrayLike, fn, null)
expect(result).toEqual([2, 4])
})
it('应正确处理mapFn为箭头函数时的this', () => {
const arrayLike = { 0: 2, 1: 4, length: 2 }
const context = { x: 10 }
const result = myFrom(
arrayLike,
v => (this?.x ? this.x * v : v),
context,
)
expect(result).toEqual([2, 4])
})
it('应正确处理对象没有length属性的情况', () => {
const obj = { 0: 'a', 1: 'b' }
const result = myFrom(obj)
expect(result).toEqual([])
})
})ts
import { describe, expect, it } from 'vitest'
import myFrom from './myFrom'
describe('myFrom function', () => {
it('应正确处理基本的数组类对象', () => {
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 }
const result = myFrom(arrayLike)
expect(result).toEqual(['a', 'b', 'c'])
})
it('应正确处理 length 为负数的情况', () => {
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: -1 }
const result = myFrom(arrayLike)
expect(result).toEqual([])
})
it('应正确处理字符串', () => {
const result = myFrom('abc')
expect(result).toEqual(['a', 'b', 'c'])
})
it('应正确处理空的数组类对象', () => {
const arrayLike = { length: 0 }
const result = myFrom(arrayLike)
expect(result).toEqual([])
})
it('应正确应用mapFn', () => {
const arrayLike = { 0: 1, 1: 2, 2: 3, length: 3 }
const result = myFrom(arrayLike, (value: number) => value * 2)
expect(result).toEqual([2, 4, 6])
})
it('应正确处理thisArg', () => {
const arrayLike = { 0: 1, 1: 2, 2: 3, length: 3 }
const context = { multiplier: 3 }
const result = myFrom(
arrayLike,
function (this: { multiplier: number }, value: number) {
return value * this.multiplier
},
context,
)
expect(result).toEqual([3, 6, 9])
})
it('应抛出错误,当arrayLike为null或undefined时', () => {
expect(() => myFrom(null)).toThrow(TypeError)
expect(() => myFrom(undefined)).toThrow(TypeError)
})
it('应抛出错误,当mapFn不是函数时', () => {
const arrayLike = { 0: 1, 1: 2, length: 2 }
expect(() => myFrom(arrayLike, 123 as any)).toThrow(TypeError)
})
it('应正确处理稀疏数组类对象', () => {
const arrayLike = { 0: 'a', 2: 'c', length: 4 }
const result = myFrom(arrayLike)
expect(result).toEqual(['a', undefined, 'c', undefined])
expect(result.length).toBe(4)
})
it('应正确处理长度超出Number.MAX_SAFE_INTEGER的情况', () => {
const arrayLike = { length: Number.MAX_SAFE_INTEGER + 1 }
expect(() => myFrom(arrayLike)).toThrow(RangeError)
})
it('应正确处理负数长度', () => {
const arrayLike = { length: -5 }
const result = myFrom(arrayLike)
expect(result).toEqual([])
expect(result.length).toBe(0)
})
it('应正确处理可迭代对象(如Set)', () => {
const set = new Set([1, 2, 3])
const result = myFrom(set)
expect(result).toEqual([1, 2, 3])
})
it('应正确处理可迭代对象(如Map)', () => {
const map = new Map([
['a', 1],
['b', 2],
])
const result = myFrom(map)
expect(result).toEqual([
['a', 1],
['b', 2],
])
})
it('应正确处理带有非数字length属性的对象', () => {
const arrayLike = { 0: 'a', 1: 'b', length: '2' as any }
const result = myFrom(arrayLike)
expect(result).toEqual(['a', 'b'])
})
it('应正确处理length为NaN的情况', () => {
const arrayLike = { 0: 'a', length: Number.NaN }
const result = myFrom(arrayLike)
expect(result).toEqual([])
})
it('应正确处理mapFn的第二个参数index', () => {
const arrayLike = { 0: 10, 1: 20, length: 2 }
const result = myFrom(arrayLike, (v, i) => i)
expect(result).toEqual([0, 1])
})
it('应正确处理mapFn返回undefined的情况', () => {
const arrayLike = { 0: 1, 1: 2, length: 2 }
const result = myFrom(arrayLike, () => undefined)
expect(result).toEqual([undefined, undefined])
})
it('应正确处理thisArg为null的情况', () => {
const arrayLike = { 0: 2, 1: 4, length: 2 }
function fn(this: { x?: number } | null, v: number): number {
return this ? (this.x || 1) * v : v
}
const result = myFrom(arrayLike, fn, null)
expect(result).toEqual([2, 4])
})
it('应正确处理mapFn为箭头函数时的this', () => {
const arrayLike = { 0: 2, 1: 4, length: 2 }
const context = { x: 10 }
// 箭头函数不绑定this,即使传入thisArg也无效
const result = myFrom(arrayLike, (v: number) => v, context)
expect(result).toEqual([2, 4])
})
it('应正确处理对象没有length属性的情况', () => {
const obj = { 0: 'a', 1: 'b' } as any
const result = myFrom(obj)
expect(result).toEqual([])
})
})答案
| 类型 | 路径 |
|---|---|
| JS 版本 | problems/Day 06/answer.js |
| TS 版本 | problems/Day 06/ts/answer.ts |
| Review | 06.md |