From f6be4d7ad67c8fc4700e6c15d09f5a09bd141b8f Mon Sep 17 00:00:00 2001 From: raon0211 Date: Fri, 9 Aug 2024 09:32:42 +0900 Subject: [PATCH] feat(bind): Move bind to compat --- docs/.vitepress/en.mts | 2 +- docs/.vitepress/ko.mts | 1 + docs/.vitepress/zh_hans.mts | 2 +- docs/ko/reference/compat/function/bind.md | 52 ++++++++++++++++ docs/reference/{ => compat}/function/bind.md | 21 ++++--- .../reference/{ => compat}/function/bind.md | 7 ++- src/{ => compat}/function/bind.spec.ts | 10 ++-- src/compat/function/bind.ts | 60 +++++++++++++++++++ src/compat/index.ts | 1 + src/function/bind.ts | 48 --------------- src/function/index.ts | 1 - 11 files changed, 140 insertions(+), 65 deletions(-) create mode 100644 docs/ko/reference/compat/function/bind.md rename docs/reference/{ => compat}/function/bind.md (52%) rename docs/zh_hans/reference/{ => compat}/function/bind.md (85%) rename src/{ => compat}/function/bind.spec.ts (97%) create mode 100644 src/compat/function/bind.ts delete mode 100644 src/function/bind.ts diff --git a/docs/.vitepress/en.mts b/docs/.vitepress/en.mts index b782d177..39d492c5 100644 --- a/docs/.vitepress/en.mts +++ b/docs/.vitepress/en.mts @@ -122,7 +122,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'noop', link: '/reference/function/noop' }, { text: 'ary', link: '/reference/function/ary' }, { text: 'unary', link: '/reference/function/unary' }, - { text: 'bind', link: '/reference/function/bind' }, + { text: 'bind (compat)', link: '/reference/compat/function/bind' }, ], }, { diff --git a/docs/.vitepress/ko.mts b/docs/.vitepress/ko.mts index 52ea8712..098e65d8 100644 --- a/docs/.vitepress/ko.mts +++ b/docs/.vitepress/ko.mts @@ -133,6 +133,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'noop', link: '/ko/reference/function/noop' }, { text: 'ary', link: '/reference/function/ary' }, { text: 'unary', link: '/reference/function/unary' }, + { text: 'bind (호환성)', link: '/reference/compat/function/unary' }, ], }, { diff --git a/docs/.vitepress/zh_hans.mts b/docs/.vitepress/zh_hans.mts index f0aaf893..32651f47 100644 --- a/docs/.vitepress/zh_hans.mts +++ b/docs/.vitepress/zh_hans.mts @@ -116,7 +116,7 @@ function sidebar(): DefaultTheme.Sidebar { { text: 'noop', link: '/zh_hans/reference/function/noop' }, { text: 'ary', link: '/zh_hans/reference/function/ary' }, { text: 'unary', link: '/zh_hans/reference/function/unary' }, - { text: 'bind', link: '/zh_hans/reference/function/bind' }, + { text: 'bind (兼容性)', link: '/zh_hans/reference/function/bind' }, ], }, { diff --git a/docs/ko/reference/compat/function/bind.md b/docs/ko/reference/compat/function/bind.md new file mode 100644 index 00000000..6deefeef --- /dev/null +++ b/docs/ko/reference/compat/function/bind.md @@ -0,0 +1,52 @@ +# bind + +::: info +이 함수는 [lodash와 완전히 호환](../../../compatibility.md)돼요. `es-toolkit/compat` 라이브러리에서 쓸 수 있어요. +::: + +함수의 `this`를 고정하고, `partialArgs`로 미리 인자를 제공해요. + +Symbol 타입의 `bind.placeholder`를 쓰면, 미리 제공한 인자가 사용될 위치를 결정할 수 있어요. + +내장 `Function#bind` 과는 다르게, 함수의 `length` 프로퍼티는 설정하지 않아요. + +## 인터페이스 + +```typescript +function bind(func: F, thisObj?: unknown, ...partialArgs: any[]): F; + +namespace bind { + placeholder: symbol; +} +``` + +### 파라미터 + +- `func` (`F`): `this` 를 고정할 함수. +- `thisObj` (`any`, optional): 함수에 고정될 `this` 객체. +- `partialArgs` (`any[]`): 미리 주어질 인자. + +### 반환 값 + +(`F`): `this`가 고정된 함수. + +## 예시 + +```typescript +import { bind } from 'es-toolkit/compat'; + +function greet(greeting, punctuation) { + return greeting + ' ' + this.user + punctuation; +} + +const object = { user: 'fred' }; + +let bound = bind(greet, object, 'hi'); +bound('!'); +// => 'hi fred!' + +// Bound with placeholders. +bound = bind(greet, object, bind.placeholder, '!'); +bound('hi'); +// => 'hi fred!' +``` diff --git a/docs/reference/function/bind.md b/docs/reference/compat/function/bind.md similarity index 52% rename from docs/reference/function/bind.md rename to docs/reference/compat/function/bind.md index ea7734b1..4d916ac7 100644 --- a/docs/reference/function/bind.md +++ b/docs/reference/compat/function/bind.md @@ -1,15 +1,20 @@ # bind -Creates a function that invokes `func` with the `this` binding of `thisArg` and `partials` prepended to the arguments it receives. +::: info +This function is fully compatible with lodash. You can find it in our [compatibility library](../../../compatibility.md), `es-toolkit/compat`. +::: + +Creates a function that invokes `func` with the `this` binding of `thisObj` and `partials` prepended to the arguments it receives. The `bind.placeholder` value, which defaults to a `symbol`, may be used as a placeholder for partially applied arguments. -**Note:** Unlike native `Function#bind`, this method doesn't set the `length` property of bound functions. +Note that unlike native `Function#bind`, this method doesn't set the `length` property of bound functions. ## Signature ```typescript -function bind(func: (...args: any[]) => any, thisArg?: any, ...partials: any[]): (...args: any[]) => any; +function bind(func: F, thisObj?: unknown, ...partialArgs: any[]): F; + namespace bind { placeholder: symbol; } @@ -17,18 +22,18 @@ namespace bind { ### Parameters -- `fn` (`(...args: any[]) => any`): The function to bind. -- `thisArg` (`any`, optional): The `this` binding of `func`. -- `partials` (`any[]`): The arguments to be partially applied. +- `func` (`F`): The function to bind. +- `thisObj` (`any`, optional): The `this` binding of `func`. +- `partialArgs` (`any[]`): The arguments to be partially applied. ### Returns -(`(...args: any[]) => any`): Returns the new bound function. +(`F`): Returns the new bound function. ## Examples ```typescript -import { bind } from 'es-toolkit/function'; +import { bind } from 'es-toolkit/compat'; function greet(greeting, punctuation) { return greeting + ' ' + this.user + punctuation; diff --git a/docs/zh_hans/reference/function/bind.md b/docs/zh_hans/reference/compat/function/bind.md similarity index 85% rename from docs/zh_hans/reference/function/bind.md rename to docs/zh_hans/reference/compat/function/bind.md index edb2a03a..c40d1c94 100644 --- a/docs/zh_hans/reference/function/bind.md +++ b/docs/zh_hans/reference/compat/function/bind.md @@ -1,5 +1,10 @@ # bind +::: info +此函数与 lodash 完全兼容。您可以在我们的[兼容性库](../../../compatibility.md)中找到它,`es-toolkit/compat`。 +::: + + 创建一个调用 `func` 的函数,`thisArg` 绑定 `func` 函数中的 `this`,并且 `func` 函数会接收 `partials` 附加参数。 `bind.placeholder` 的值默认是一个 `symbol`,可以用作附加的部分参数的占位符。 @@ -28,7 +33,7 @@ namespace bind { ## 示例 ```typescript -import { bind } from 'es-toolkit/function'; +import { bind } from 'es-toolkit/compat'; function greet(greeting, punctuation) { return greeting + ' ' + this.user + punctuation; diff --git a/src/function/bind.spec.ts b/src/compat/function/bind.spec.ts similarity index 97% rename from src/function/bind.spec.ts rename to src/compat/function/bind.spec.ts index 1d51c6c9..63eca627 100644 --- a/src/function/bind.spec.ts +++ b/src/compat/function/bind.spec.ts @@ -1,8 +1,8 @@ import { describe, it, expect } from 'vitest'; import { bind } from './bind'; -import { isEqual } from '../predicate'; +import { isEqual } from '../../predicate/isEqual'; -function fn(this: any) { +function fn(this: any, ..._: any[]) { const result = [this]; // eslint-disable-next-line prefer-rest-params return result.concat(Array.from(arguments)); @@ -79,7 +79,7 @@ describe('bind', () => { it('should create a function with a `length` of `0`', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const fn = function (_a: unknown, _b: unknown, _c: unknown) {}; + const fn = function (_a: unknown, _b: unknown, _c: unknown) { }; let bound = bind(fn, {}); expect(bound.length).toBe(0); @@ -107,7 +107,7 @@ describe('bind', () => { return this; } - function Bar() {} + function Bar() { } const thisArg = { a: 1 }; const boundFoo = bind(Foo, thisArg) as any; @@ -173,7 +173,7 @@ describe('bind', () => { it('should not error when calling bound class constructors with the `new` operator', () => { const createCtor: any = function () { - return class A {}; + return class A { }; }; const bound = bind(createCtor()) as any; diff --git a/src/compat/function/bind.ts b/src/compat/function/bind.ts new file mode 100644 index 00000000..44e9b28b --- /dev/null +++ b/src/compat/function/bind.ts @@ -0,0 +1,60 @@ +/** + * Creates a function that invokes `func` with the `this` binding of `thisArg` and `partials` prepended to the arguments it receives. + * + * The `bind.placeholder` value, which defaults to a `symbol`, may be used as a placeholder for partially applied arguments. + * + * Note: Unlike native `Function#bind`, this method doesn't set the `length` property of bound functions. + * + * @param {(...args: any[]) => any} func The function to bind. + * @param {any} thisArg The `this` binding of `func`. + * @param {any[]} partials The arguments to be partially applied. + * @returns {(...args: any[]) => any} Returns the new bound function. + * + * @example + * function greet(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * const object = { user: 'fred' }; + * let bound = bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * bound = bind(greet, object, bind.placeholder, '!'); + * bound('hi'); + * // => 'hi fred!' + */ +export function bind(func: F, thisObj?: unknown, ...partialArgs: any[]): F { + const binded = function (this: any, ...providedArgs: any[]) { + const args: any[] = []; + + // Populate args by merging partialArgs and providedArgs. + // e.g.. when we call bind(func, {}, [1, bind.placeholder, 3])(2, 4); + // we have args with [1, 2, 3, 4]. + let startIndex = 0; + + for (let i = 0; i < partialArgs.length; i++) { + const arg = partialArgs[i]; + + if (arg === bind.placeholder) { + args.push(providedArgs[startIndex++]); + } else { + args.push(arg); + } + } + + for (let i = startIndex; i < providedArgs.length; i++) { + args.push(providedArgs[i]); + } + + if (this instanceof binded) { + // @ts-expect-error - fn is a constructor + return new func(...args); + } + + return func.apply(thisObj, args); + }; + + return binded as any as F; +} + +bind.placeholder = Symbol('bind.placeholder'); \ No newline at end of file diff --git a/src/compat/index.ts b/src/compat/index.ts index 0c674c74..3fc86f03 100644 --- a/src/compat/index.ts +++ b/src/compat/index.ts @@ -35,6 +35,7 @@ export { zipObjectDeep } from './array/zipObjectDeep.ts'; export { head as first } from '../array/head.ts'; export { ary } from './function/ary.ts'; +export { bind } from './function/bind.ts'; export { get } from './object/get.ts'; export { set } from './object/set.ts'; diff --git a/src/function/bind.ts b/src/function/bind.ts deleted file mode 100644 index c01e9ae0..00000000 --- a/src/function/bind.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * - * Creates a function that invokes `func` with the `this` binding of `thisArg` and `partials` prepended to the arguments it receives. - * - * The `bind.placeholder` value, which defaults to a `symbol`, may be used as a placeholder for partially applied arguments. - * - * **Note:** Unlike native `Function#bind`, this method doesn't set the `length` property of bound functions. - * - * @param {(...args: any[]) => any} func The function to bind. - * @param {any} thisArg The `this` binding of `func`. - * @param {any[]} partials The arguments to be partially applied. - * @returns {(...args: any[]) => any} Returns the new bound function. - * - * @example - * function greet(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * const object = { user: 'fred' }; - * let bound = bind(greet, object, 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * bound = bind(greet, object, bind.placeholder, '!'); - * bound('hi'); - * // => 'hi fred!' - */ -export function bind(func: (...args: any[]) => any, thisArg?: any, ...partials: any[]): (...args: any[]) => any { - const wrapper = function (this: any, ...args: any[]) { - let index = 0; - const result = partials.map(bindArg => { - if (bindArg === bind.placeholder) { - return args[index++]; - } - return bindArg; - }); - for (let i = index; i < args.length; i++) { - result.push(args[i]); - } - if (this instanceof wrapper) { - // @ts-expect-error - fn is a constructor - return new func(...result); - } - return func.apply(thisArg, result); - }; - return wrapper; -} - -bind.placeholder = Symbol('bind.placeholder') as symbol; diff --git a/src/function/index.ts b/src/function/index.ts index 248eeb11..a3b61df2 100644 --- a/src/function/index.ts +++ b/src/function/index.ts @@ -7,4 +7,3 @@ export { throttle } from './throttle.ts'; export { negate } from './negate.ts'; export { ary } from './ary.ts'; export { unary } from './unary.ts'; -export { bind } from './bind.ts';