Repository: toss/es-toolkit
Pull request #1160: feat(compat): add `clone`, `cloneWith` to compat/object
Author: login: greenheadHQ
Created: 2025-05-02T06:30:31Z | Merged: 2025-05-08T07:52:49Z

close #1017 close #1022

> The testcases follow [lodash.clone](https://github.com/lodash/lodash/blob/v5-wip/test/clone-methods.spec.js).

## 1️⃣ Test Results (`clone`)

### Test List

<img width="539" alt="image" src="https://github.com/user-attachments/assets/f0cade56-f0bc-45a9-af40-5f5b6d0155aa" />


### Test Coverage

<img width="373" alt="image" src="https://github.com/user-attachments/assets/a859af79-996f-4da7-8efb-57765bd7cc18" />


## Bench Test 

<img width="893" alt="image" src="https://github.com/user-attachments/assets/05e6956d-ff8f-4c3c-bd77-0641faf55150" />

--- 

## 2️⃣ Test Results (`cloneWith`)

### Test List

<img width="373" alt="image" src="https://github.com/user-attachments/assets/a98aa267-10d1-4704-8f2a-7183552ff2c7" />


### Test Coverage

<img width="514" alt="image" src="https://github.com/user-attachments/assets/ebd40425-a763-44eb-a02c-c950f6d6eb26" />


## Bench Test 

<img width="1040" alt="image" src="https://github.com/user-attachments/assets/a7687b6b-edb0-436a-af1e-90a87c726f84" />


Linked issue #1017: Add `clone` to compat package
## Description  
We have to add `clone` to `compat/object` package.  

If there are no assignees, feel free to assign yourself and contribute. Thank you! 😊  

## References  

- [Lodash Document](https://lodash.com/docs/4.17.15#clone)  
- [Test Case](https://github.com/lodash/lodash/blob/v5-wip/test/clone-methods.spec.js)  


Diff:
diff --git a/benchmarks/performance/clone.bench.ts b/benchmarks/performance/clone.bench.ts
index 605470853..a44b918ed 100644
--- a/benchmarks/performance/clone.bench.ts
+++ b/benchmarks/performance/clone.bench.ts
@@ -1,8 +1,10 @@
 import { bench, describe } from 'vitest';
 import { clone as cloneToolkit_ } from 'es-toolkit';
+import { clone as cloneToolkitCompat_ } from 'es-toolkit/compat';
 import { clone as cloneLodash_ } from 'lodash';
 
 const cloneToolkit = cloneToolkit_;
+const cloneToolkitCompat = cloneToolkitCompat_;
 const cloneLodash = cloneLodash_;
 
 const obj = {
@@ -20,6 +22,11 @@ describe('clone', () => {
   bench('es-toolkit/clone', () => {
     cloneToolkit(obj);
   });
+
+  bench('es-toolkit/clone/compat', () => {
+    cloneToolkitCompat(obj);
+  });
+
   bench('lodash/clone', () => {
     cloneLodash(obj);
   });
diff --git a/benchmarks/performance/cloneWith.bench.ts b/benchmarks/performance/cloneWith.bench.ts
new file mode 100644
index 000000000..90f22d5e4
--- /dev/null
+++ b/benchmarks/performance/cloneWith.bench.ts
@@ -0,0 +1,39 @@
+import { bench, describe } from 'vitest';
+import { cloneWith as cloneWithToolkitCompat_ } from 'es-toolkit/compat';
+import { cloneWith as cloneWithLodash_ } from 'lodash';
+
+const cloneWithLodash = cloneWithLodash_;
+const cloneWithToolkitCompat = cloneWithToolkitCompat_;
+
+const obj = {
+  number: 29,
+  string: 'es-toolkit',
+  boolean: true,
+  array: [1, 2, 3],
+  object: { a: 1, b: 'es-toolkit' },
+  date: new Date(),
+  regex: /abc/g,
+  nested: { a: [1, 2, 3], b: { c: 'es-toolkit' }, d: new Date() },
+};
+
+function customizer(value: any) {
+  if (typeof value === 'number') {
+    return value * 2;
+  }
+}
+
+describe('cloneWith', () => {
+  bench('lodash/cloneWith', () => {
+    cloneWithLodash(obj);
+  });
+  bench('es-toolkit/compat/cloneWith', () => {
+    cloneWithToolkitCompat(obj);
+  });
+
+  bench('lodash/cloneWith (with customizer)', () => {
+    cloneWithLodash(obj, customizer);
+  });
+  bench('es-toolkit/compat/cloneWith (with customizer)', () => {
+    cloneWithToolkitCompat(obj, customizer);
+  });
+});
diff --git a/docs/ja/reference/compat/object/cloneWith.md b/docs/ja/reference/compat/object/cloneWith.md
new file mode 100644
index 000000000..a0cc30855
--- /dev/null
+++ b/docs/ja/reference/compat/object/cloneWith.md
@@ -0,0 +1,54 @@
+# cloneWith
+
+::: info
+この関数は互換性のために `es-toolkit/compat` からのみインポートできます。代替可能なネイティブ JavaScript API があるか、まだ十分に最適化されていないためです。
+
+`es-toolkit/compat` からこの関数をインポートすると、[lodash と完全に同じように動作](mdc:../../../compatibility.md)します。
+:::
+
+与えられたオブジェクトのシャローコピーを作成し、カスタマイズ関数を使用してクローン方法をカスタマイズできます。このメソッドは `clone` と似ていますが、クローンされた値を生成するためのカスタマイザー関数を受け取ることができる点が異なります。カスタマイザーが undefined を返した場合、デフォルトのクローン処理が使用されます。
+
+カスタマイザーを指定しない場合は、`clone` と同じように動作します。
+
+## インターフェース
+
+```typescript
+function cloneWith<T>(value: T, customizer?: (value: any) => any): T;
+```
+
+### パラメータ
+
+- `value` (`T`): クローンする値です。
+- `customizer` (`Function`): オプション。クローン処理をカスタマイズする関数です。
+
+### 戻り値
+
+(`T`): 与えられたオブジェクトのシャローコピーを返します。
+
+## 例
+
+```typescript
+const num = 29;
+const clonedNum = cloneWith(num);
+console.log(clonedNum); // 29
+console.log(clonedNum === num); // true
+
+const arr = [1, 2, 3];
+const clonedArr = cloneWith(arr);
+console.log(clonedArr); // [1, 2, 3]
+console.log(clonedArr === arr); // false
+
+const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };
+const clonedObj = cloneWith(obj);
+console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }
+console.log(clonedObj === obj); // false
+
+const obj2 = { a: 1, b: 2 };
+const clonedObjWithCustomizer = cloneWith(obj2, value => {
+  if (typeof value === 'number') {
+    return value * 2; // 数値を2倍にする
+  }
+  // undefined を返すとデフォルトのクローン処理が使用されます
+});
+console.log(clonedObjWithCustomizer); // { a: 2, b: 4 }
+```
diff --git a/docs/ko/reference/compat/object/cloneWith.md b/docs/ko/reference/compat/object/cloneWith.md
new file mode 100644
index 000000000..1f1cae7bd
--- /dev/null
+++ b/docs/ko/reference/compat/object/cloneWith.md
@@ -0,0 +1,54 @@
+# cloneWith
+
+::: info
+이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요.
+
+`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](mdc:../../../compatibility.md)해요.
+:::
+
+주어진 객체의 얕은 복사본을 만들되, 사용자 정의 함수를 통해 복사 방식을 커스터마이즈할 수 있어요. 이 메서드는 `clone`과 비슷하지만, 복제된 값을 생성하기 위한 커스터마이저 함수를 받을 수 있다는 점이 달라요. 만약 커스터마이저가 undefined를 반환하면, 기본 복제 방식을 사용해요.
+
+커스터마이저를 제공하지 않으면 `clone`과 동일하게 동작해요.
+
+## 인터페이스
+
+```typescript
+function cloneWith<T>(value: T, customizer?: (value: any) => any): T;
+```
+
+### 파라미터
+
+- `value` (`T`): 복제할 값이에요.
+- `customizer` (`Function`): 선택사항이에요. 복제 방식을 커스터마이즈하는 함수예요.
+
+### 반환 값
+
+(`T`): 주어진 객체의 얕은 복사본을 반환해요.
+
+## 예시
+
+```typescript
+const num = 29;
+const clonedNum = cloneWith(num);
+console.log(clonedNum); // 29
+console.log(clonedNum === num); // true
+
+const arr = [1, 2, 3];
+const clonedArr = cloneWith(arr);
+console.log(clonedArr); // [1, 2, 3]
+console.log(clonedArr === arr); // false
+
+const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };
+const clonedObj = cloneWith(obj);
+console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }
+console.log(clonedObj === obj); // false
+
+const obj2 = { a: 1, b: 2 };
+const clonedObjWithCustomizer = cloneWith(obj2, value => {
+  if (typeof value === 'number') {
+    return value * 2; // 숫자를 2배로 만들기
+  }
+  // undefined를 반환하면 기본 복제 방식을 사용해요
+});
+console.log(clonedObjWithCustomizer); // { a: 2, b: 4 }
+```
diff --git a/docs/reference/compat/object/cloneWith.md b/docs/reference/compat/object/cloneWith.md
new file mode 100644
index 000000000..841ecb6d9
--- /dev/null
+++ b/docs/reference/compat/object/cloneWith.md
@@ -0,0 +1,54 @@
+# cloneWith
+
+::: info
+This function is only available in `es-toolkit/compat` for compatibility reasons. It either has alternative native JavaScript APIs or isn't fully optimized yet.
+
+When imported from `es-toolkit/compat`, it behaves exactly like lodash and provides the same functionalities, as detailed @here.
+:::
+
+Creates a shallow clone of the given object with customization. This method is like `clone` except that it accepts a customizer which is invoked to produce the cloned value. If customizer returns undefined, cloning is handled by the method instead.
+
+If no customizer is provided, it behaves like `clone`.
+
+## Signature
+
+```typescript
+function cloneWith<T>(value: T, customizer?: (value: any) => any): T;
+```
+
+### Parameters
+
+- `value` (`T`): The value to clone.
+- `customizer` (`Function`): Optional. The function to customize cloning.
+
+### Returns
+
+(`T`): A shallow clone of the given object.
+
+## Examples
+
+```typescript
+const num = 29;
+const clonedNum = cloneWith(num);
+console.log(clonedNum); // 29
+console.log(clonedNum === num); // true
+
+const arr = [1, 2, 3];
+const clonedArr = cloneWith(arr);
+console.log(clonedArr); // [1, 2, 3]
+console.log(clonedArr === arr); // false
+
+const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };
+const clonedObj = cloneWith(obj);
+console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }
+console.log(clonedObj === obj); // false
+
+const obj2 = { a: 1, b: 2 };
+const clonedObjWithCustomizer = cloneWith(obj2, value => {
+  if (typeof value === 'number') {
+    return value * 2; // Double the number
+  }
+  // Returning undefined uses the default cloning
+});
+console.log(clonedObjWithCustomizer); // { a: 2, b: 4 }
+```
diff --git a/docs/zh_hans/reference/compat/object/cloneWith.md b/docs/zh_hans/reference/compat/object/cloneWith.md
new file mode 100644
index 000000000..24b5cbe19
--- /dev/null
+++ b/docs/zh_hans/reference/compat/object/cloneWith.md
@@ -0,0 +1,54 @@
+# cloneWith
+
+::: info
+出于兼容性原因，此函数仅在 `es-toolkit/compat` 中提供。它可能具有替代的原生 JavaScript API，或者尚未完全优化。
+
+从 `es-toolkit/compat` 导入时，它的行为与 lodash 完全一致，并提供相同的功能，详情请见 [这里](mdc:../../../compatibility.md)。
+:::
+
+创建给定对象的浅拷贝，并允许通过自定义函数来定制克隆过程。这个方法类似于 `clone`，但它接受一个自定义函数来生成克隆值。如果自定义函数返回 undefined，则会使用默认的克隆方法。
+
+如果没有提供自定义函数，它的行为与 `clone` 完全相同。
+
+## 签名
+
+```typescript
+function cloneWith<T>(value: T, customizer?: (value: any) => any): T;
+```
+
+### 参数
+
+- `value` (`T`): 要克隆的值。
+- `customizer` (`Function`): 可选。用于自定义克隆过程的函数。
+
+### 返回值
+
+(`T`): 返回给定对象的浅拷贝。
+
+## 示例
+
+```typescript
+const num = 29;
+const clonedNum = cloneWith(num);
+console.log(clonedNum); // 29
+console.log(clonedNum === num); // true
+
+const arr = [1, 2, 3];
+const clonedArr = cloneWith(arr);
+console.log(clonedArr); // [1, 2, 3]
+console.log(clonedArr === arr); // false
+
+const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };
+const clonedObj = cloneWith(obj);
+console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }
+console.log(clonedObj === obj); // false
+
+const obj2 = { a: 1, b: 2 };
+const clonedObjWithCustomizer = cloneWith(obj2, value => {
+  if (typeof value === 'number') {
+    return value * 2; // 将数字值翻倍
+  }
+  // 返回 undefined 时使用默认克隆方法
+});
+console.log(clonedObjWithCustomizer); // { a: 2, b: 4 }
+```
diff --git a/src/compat/compat.ts b/src/compat/compat.ts
index b657541ba..f9d77df5d 100644
--- a/src/compat/compat.ts
+++ b/src/compat/compat.ts
@@ -151,12 +151,10 @@ export { assignInWith } from './object/assignInWith.ts';
 export { assignInWith as extendWith } from './object/assignInWith.ts';
 export { assignWith } from './object/assignWith.ts';
 export { at } from './object/at.ts';
-// FIXME: Replace with compat version
-export { clone } from '../object/clone.ts';
+export { clone } from './object/clone.ts';
 export { cloneDeep } from './object/cloneDeep.ts';
 export { cloneDeepWith } from './object/cloneDeepWith.ts';
-// FIXME: Replace with compat version
-// export { cloneWith } from './object/cloneWith.ts';
+export { cloneWith } from './object/cloneWith.ts';
 // FIXME: Replace with compat version
 // export { countBy } from './object/countBy.ts';
 export { create } from './object/create.ts';
diff --git a/src/compat/object/clone.spec.ts b/src/compat/object/clone.spec.ts
new file mode 100644
index 000000000..7d4cdb755
--- /dev/null
+++ b/src/compat/object/clone.spec.ts
@@ -0,0 +1,549 @@
+import { describe, expect, it } from 'vitest';
+import * as lodashStable from 'es-toolkit/compat';
+import { clone } from './clone';
+import { args } from '../_internal/args';
+import { typedArrays } from '../_internal/typedArrays';
+
+describe('clone', () => {
+  it('should perform a shallow clone', () => {
+    const array = [{ a: 0 }, { b: 1 }];
+    const actual = clone(array);
+
+    expect(actual).toEqual(array);
+    expect(actual).not.toBe(array);
+    expect(actual[0]).toBe(array[0]);
+  });
+
+  it('should clone arguments objects', () => {
+    const actual = clone(args);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('2', 3);
+    expect(actual).toHaveProperty('length', 3);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arguments objects with Symbol.iterator', () => {
+    const args = {
+      0: 1,
+      1: 2,
+      length: 2,
+      [Symbol.iterator]: Array.prototype[Symbol.iterator],
+    };
+
+    const actual = clone(args);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('length', 2);
+    expect(actual[Symbol.iterator]).toBe(Array.prototype[Symbol.iterator]);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arrays', () => {
+    const object = ['a', ''];
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone array-like objects', () => {
+    const object = { 0: 'a', length: 1 };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone booleans', () => {
+    const object = false;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone boolean objects', () => {
+    const object = Object(false);
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone date objects', () => {
+    const object = new Date();
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone Foo instances', () => {
+    class Foo {}
+
+    const object = new Foo();
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects', () => {
+    const object = { a: 0, b: 1, c: 2 };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects with object values', () => {
+    const object = { a: /a/, b: ['B'], c: { C: 1 } };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone maps', () => {
+    const map = new Map([
+      ['a', 1],
+      ['b', 2],
+    ]);
+
+    const actual = clone(map);
+
+    expect(actual).toEqual(map);
+    expect(actual).not.toBe(map);
+  });
+
+  it('should clone null values', () => {
+    const object = null;
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone numbers', () => {
+    const object = 0;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone number objects', () => {
+    const object = Object(0);
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone regexes', () => {
+    const object = /a/gim;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone sets', () => {
+    const set = new Set([1, 2]);
+
+    const actual = clone(set);
+
+    expect(actual).toEqual(set);
+    expect(actual).not.toBe(set);
+  });
+
+  it('should clone strings', () => {
+    const object = 'a';
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone string objects', () => {
+    const object = Object('a');
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+  it('should clone undefined values', () => {
+    const object = undefined;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone array buffers', () => {
+    if (typeof ArrayBuffer === 'undefined') {
+      return;
+    }
+
+    const buffer = new ArrayBuffer(10);
+    const actual = clone(buffer);
+
+    expect(actual.byteLength).toBe(buffer.byteLength);
+    expect(actual).not.toBe(buffer);
+  });
+
+  it('should clone array buffers of various sizes', () => {
+    if (typeof ArrayBuffer === 'undefined') {
+      return;
+    }
+
+    const sizes = [0, 1, 16, 1024];
+
+    lodashStable.each(sizes, size => {
+      const buffer = new ArrayBuffer(size);
+      const actual = clone(buffer);
+
+      expect(actual.byteLength).toBe(size);
+      expect(actual).not.toBe(buffer);
+    });
+  });
+
+  it('should clone DataView objects', () => {
+    if (typeof ArrayBuffer === 'undefined' || typeof DataView === 'undefined') {
+      return;
+    }
+
+    const buffer = new ArrayBuffer(16);
+    const dataView = new DataView(buffer);
+
+    dataView.setInt8(0, 42);
+    dataView.setInt16(2, 12345, true);
+    dataView.setFloat32(4, 3.14159, true);
+    dataView.setFloat64(8, 123456789.123456, true);
+
+    const cloned = clone(dataView);
+
+    expect(cloned).not.toBe(dataView);
+    expect(cloned.buffer).not.toBe(dataView.buffer);
+    expect(cloned.byteOffset).toBe(dataView.byteOffset);
+    expect(cloned.byteLength).toBe(dataView.byteLength);
+
+    expect(cloned.getInt8(0)).toBe(42);
+    expect(cloned.getInt16(2, true)).toBe(12345);
+    expect(cloned.getFloat32(4, true)).toBeCloseTo(3.14159, 5);
+    expect(cloned.getFloat64(8, true)).toBeCloseTo(123456789.123456, 10);
+
+    dataView.setInt8(0, 100);
+    expect(cloned.getInt8(0)).toBe(42);
+  });
+
+  it('should clone DataView objects with new buffer', () => {
+    if (typeof ArrayBuffer === 'undefined' || typeof DataView === 'undefined') {
+      return;
+    }
+
+    const buffer = new ArrayBuffer(16);
+    const dataView = new DataView(buffer);
+
+    const cloned = clone(dataView);
+
+    expect(cloned).not.toBe(dataView);
+    expect(cloned.buffer).not.toBe(dataView.buffer);
+    expect(cloned.byteLength).toBe(dataView.byteLength);
+  });
+
+  it('should clone `index` and `input` array properties', () => {
+    const array = /c/.exec('abcde') || [];
+    const actual = clone(array) as any;
+
+    expect(actual.index).toBe(2);
+    expect(actual.input).toBe('abcde');
+  });
+
+  it('should clone `lastIndex` regexp property', () => {
+    const regexp = /c/g;
+    regexp.exec('abcde');
+
+    const actual = clone(regexp);
+    expect(actual.lastIndex).toBe(3);
+  });
+
+  it('should clone expando properties', () => {
+    const values = [false, true, 1, 'a'];
+    const expected = values.map(() => true);
+
+    const actual = values.map(value => {
+      const object = Object(value);
+      object.a = 1;
+      const cloned = clone(object);
+      return cloned.a === 1;
+    });
+
+    expect(actual).toEqual(expected);
+  });
+
+  it('should clone prototype objects', () => {
+    class Foo {
+      b = 1;
+    }
+    Foo.prototype.b = 2;
+
+    const actual = clone(Foo.prototype);
+
+    expect(actual instanceof Foo).toBe(false);
+    expect(actual).toEqual({ b: 2 });
+  });
+
+  it('should set the `[[Prototype]]` of a clone', () => {
+    class Foo {}
+
+    expect(clone(new Foo()) instanceof Foo).toBe(true);
+  });
+
+  it('should set the `[[Prototype]]` of a clone even when the `constructor` is incorrect', () => {
+    class Foo {}
+
+    Foo.prototype.constructor = Object;
+    expect(clone(new Foo()) instanceof Foo).toBe(true);
+    Foo.prototype.constructor = Foo;
+  });
+
+  it('should ensure `value` constructor is a function before using its `[[Prototype]]`', () => {
+    class Foo {}
+
+    Foo.prototype.constructor = null as any;
+    expect(clone(new Foo()) instanceof Foo).toBe(false);
+    Foo.prototype.constructor = Foo;
+  });
+
+  it('should handle objects with null prototype', () => {
+    const obj = Object.create(null);
+    obj.a = 1;
+    obj.b = 2;
+
+    const actual = clone(obj);
+
+    expect(actual).toEqual({ a: 1, b: 2 });
+  });
+
+  it('should handle objects with modified prototype chain', () => {
+    function CustomProto() {}
+    CustomProto.prototype.customMethod = function () {
+      return 'custom';
+    };
+
+    const obj = Object.create(CustomProto.prototype);
+    obj.a = 1;
+
+    const actual = clone(obj);
+
+    expect(actual.a).toBe(1);
+    expect(Object.getPrototypeOf(actual)).toBe(CustomProto.prototype);
+    expect((actual as any).customMethod()).toBe('custom');
+  });
+
+  it('should clone properties that shadow those on `Object.prototype`', () => {
+    const object = {
+      constructor: Object.prototype.constructor,
+      hasOwnProperty: Object.prototype.hasOwnProperty,
+      isPrototypeOf: Object.prototype.isPrototypeOf,
+      propertyIsEnumerable: Object.prototype.propertyIsEnumerable,
+      toLocaleString: Object.prototype.toLocaleString,
+      toString: Object.prototype.toString,
+      valueOf: Object.prototype.valueOf,
+    };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone symbol properties', () => {
+    function Foo(this: any) {
+      this[Symbol.for('c')] = { c: 1 };
+    }
+
+    if (Symbol) {
+      const symbol2 = Symbol('b');
+      Foo.prototype[symbol2] = 2;
+
+      const symbol3 = Symbol('c');
+      Object.defineProperty(Foo.prototype, symbol3, {
+        configurable: true,
+        enumerable: false,
+        writable: true,
+        value: 3,
+      });
+
+      const object = { a: { b: new (Foo as any)() } } as any;
+      object[Symbol.for('a')] = { b: 1 };
+
+      const actual = clone(object) as any;
+
+      expect(actual[Symbol.for('a')]).toBe(object[Symbol.for('a')]);
+      expect(actual.a).toBe(object.a);
+      expect(actual[Symbol.for('a')]).toEqual(object[Symbol.for('a')]);
+
+      const symbols = Object.getOwnPropertySymbols(actual.a.b);
+      expect(symbols.length).toBe(1);
+      expect(symbols[0]).toBe(Symbol.for('c'));
+      expect(actual.a.b[Symbol.for('c')]).toEqual(object.a.b[Symbol.for('c')]);
+      expect(actual.a.b[symbol2]).toEqual(object.a.b[symbol2]);
+      expect(actual.a.b[symbol3]).toEqual(object.a.b[symbol3]);
+    }
+  });
+
+  it('should handle mixed enumerable and non-enumerable symbol properties', () => {
+    const enumSymbol = Symbol('enumerable');
+    const nonEnumSymbol = Symbol('non-enumerable');
+
+    const object = { a: 1 } as any;
+    object[enumSymbol] = 'visible';
+
+    Object.defineProperty(object, nonEnumSymbol, {
+      value: 'hidden',
+      enumerable: false,
+      configurable: true,
+      writable: true,
+    });
+
+    const actual = clone(object);
+
+    expect(actual.a).toBe(1);
+    expect(actual[enumSymbol]).toBe('visible');
+    expect(actual[nonEnumSymbol]).toBeUndefined();
+
+    const symbols = Object.getOwnPropertySymbols(actual);
+    expect(symbols).toContain(enumSymbol);
+    expect(symbols).not.toContain(nonEnumSymbol);
+  });
+
+  it('should clone symbol objects', () => {
+    const symbol = Symbol('a');
+    expect(clone(symbol)).toBe(symbol);
+
+    const object = Object(symbol);
+    const actual = clone(object);
+
+    expect(typeof actual).toBe('object');
+    expect(typeof actual.valueOf()).toBe('symbol');
+    expect(actual).not.toBe(object);
+  });
+
+  it('should not clone symbol primitives', () => {
+    const symbol = Symbol('a');
+    expect(clone(symbol)).toBe(symbol);
+  });
+
+  it('should not error on DOM elements', () => {
+    if (typeof document === 'undefined') {
+      return;
+    }
+
+    const element = document.createElement('div');
+
+    try {
+      expect(clone(element)).toEqual({});
+    } catch (e) {
+      expect(false).toBe(true);
+    }
+  });
+
+  it('should perform a shallow clone when used as an iteratee for methods like `_.map`', () => {
+    const expected = [{ a: [0] }, { b: [1] }];
+    const actual = expected.map(clone);
+
+    expect(actual).toEqual(expected);
+    expect(actual[0]).not.toBe(expected[0]);
+    expect(actual[0].a).toBe(expected[0].a);
+    expect(actual[1].b).toBe(expected[1].b);
+  });
+
+  lodashStable.each(typedArrays, type => {
+    it(`should clone ${type} values`, () => {
+      const Ctor = globalThis[type as keyof typeof globalThis] as any;
+
+      if (!Ctor) {
+        return;
+      }
+
+      lodashStable.times(2, index => {
+        const buffer = new ArrayBuffer(24);
+        const view = index ? new Ctor(buffer, 8, 1) : new Ctor(buffer);
+        const actual = clone(view);
+
+        expect(actual).toEqual(view);
+        expect(actual).not.toBe(view);
+        expect(actual.buffer === view.buffer).toBe(true);
+        expect(actual.byteOffset).toBe(view.byteOffset);
+        expect(actual.length).toBe(view.length);
+      });
+    });
+  });
+
+  const uncloneableObjects = [
+    'DOM elements',
+    'functions',
+    'async functions',
+    'generator functions',
+    'the Proxy constructor',
+  ];
+
+  lodashStable.each(uncloneableObjects, type => {
+    it(`should not clone ${type}`, () => {
+      let value;
+
+      if (type === 'DOM elements' && typeof document !== 'undefined') {
+        value = document.body;
+      } else if (type === 'functions') {
+        value = function () {};
+      } else if (type === 'async functions') {
+        value = async function () {};
+      } else if (type === 'generator functions') {
+        value = function* () {};
+      } else if (type === 'the Proxy constructor') {
+        value = Proxy;
+      }
+
+      if (value) {
+        const object = { a: value, b: { c: value } };
+        const actual = clone(object);
+
+        expect(actual).toEqual(object);
+        expect(actual).not.toBe(object);
+        expect(clone(value)).toEqual({});
+      }
+    });
+  });
+
+  const errorTypes = ['Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError'];
+
+  lodashStable.each(errorTypes, type => {
+    it(`should not clone ${type}s`, () => {
+      const Ctor = globalThis[type as keyof typeof globalThis];
+      const value = new Ctor('error');
+
+      const object = { a: value, b: { c: value } };
+      const actual = clone(object);
+
+      expect(actual).toEqual(object);
+      expect(actual).not.toBe(object);
+      expect(clone(value)).toEqual({});
+    });
+  });
+});
diff --git a/src/compat/object/clone.ts b/src/compat/object/clone.ts
new file mode 100644
index 000000000..106af4687
--- /dev/null
+++ b/src/compat/object/clone.ts
@@ -0,0 +1,243 @@
+import { isPrimitive } from '../../predicate/isPrimitive.ts';
+import { getTag } from '../_internal/getTag.ts';
+import {
+  argumentsTag,
+  arrayBufferTag,
+  arrayTag,
+  booleanTag,
+  dataViewTag,
+  dateTag,
+  float32ArrayTag,
+  float64ArrayTag,
+  int8ArrayTag,
+  int16ArrayTag,
+  int32ArrayTag,
+  mapTag,
+  numberTag,
+  objectTag,
+  regexpTag,
+  setTag,
+  stringTag,
+  symbolTag,
+  uint8ArrayTag,
+  uint8ClampedArrayTag,
+  uint16ArrayTag,
+  uint32ArrayTag,
+} from '../_internal/tags.ts';
+import { isArray } from '../predicate/isArray.ts';
+import { isTypedArray } from '../predicate/isTypedArray.ts';
+
+/**
+ * Creates a shallow clone of the given object.
+ *
+ * @template T - The type of the object.
+ * @param {T} obj - The object to clone.
+ * @returns {T} - A shallow clone of the given object.
+ *
+ * @example
+ * // Clone a primitive objs
+ * const num = 29;
+ * const clonedNum = clone(num);
+ * console.log(clonedNum); // 29
+ * console.log(clonedNum === num) ; // true
+ *
+ * @example
+ * // Clone an array
+ * const arr = [1, 2, 3];
+ * const clonedArr = clone(arr);
+ * console.log(clonedArr); // [1, 2, 3]
+ * console.log(clonedArr === arr); // false
+ *
+ * @example
+ * // Clone an object
+ * const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };
+ * const clonedObj = clone(obj);
+ * console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }
+ * console.log(clonedObj === obj); // false
+ */
+export function clone<T>(obj: T): T {
+  if (isPrimitive(obj)) {
+    return obj;
+  }
+
+  const tag = getTag(obj);
+
+  if (!isCloneableObject(obj)) {
+    return {} as T;
+  }
+
+  if (isArray(obj)) {
+    const result = Array.from(obj as any) as any;
+
+    if (obj.length > 0 && typeof obj[0] === 'string' && Object.hasOwn(obj, 'index')) {
+      result.index = (obj as any).index;
+      result.input = (obj as any).input;
+    }
+
+    return result;
+  }
+
+  if (isTypedArray(obj)) {
+    const typedArray = obj as unknown as ArrayBufferView;
+    const Ctor = typedArray.constructor as any;
+    return new Ctor(typedArray.buffer, typedArray.byteOffset, (typedArray as any).length) as any;
+  }
+
+  if (tag === arrayBufferTag) {
+    return new ArrayBuffer((obj as unknown as ArrayBuffer).byteLength) as any;
+  }
+
+  if (tag === dataViewTag) {
+    const dataView = obj as unknown as DataView;
+    const buffer = dataView.buffer;
+    const byteOffset = dataView.byteOffset;
+    const byteLength = dataView.byteLength;
+
+    const clonedBuffer = new ArrayBuffer(byteLength);
+    const srcView = new Uint8Array(buffer, byteOffset, byteLength);
+    const destView = new Uint8Array(clonedBuffer);
+    destView.set(srcView);
+
+    return new DataView(clonedBuffer) as any;
+  }
+
+  if (tag === booleanTag || tag === numberTag || tag === stringTag) {
+    const Ctor = (obj as any).constructor;
+    const clone = new Ctor((obj as any).valueOf()) as any;
+
+    if (tag === stringTag) {
+      cloneStringObjectProperties(clone, obj as any);
+    } else {
+      copyOwnProperties(clone, obj as any);
+    }
+
+    return clone;
+  }
+
+  if (tag === dateTag) {
+    return new Date(Number(obj as unknown as Date)) as any;
+  }
+
+  if (tag === regexpTag) {
+    const regExp = obj as unknown as RegExp;
+    const clone = new RegExp(regExp.source, regExp.flags) as any;
+    clone.lastIndex = regExp.lastIndex;
+    return clone;
+  }
+
+  if (tag === symbolTag) {
+    return Object(Symbol.prototype.valueOf.call(obj)) as any;
+  }
+
+  if (tag === mapTag) {
+    const map = obj as unknown as Map<any, any>;
+    const result = new Map();
+
+    map.forEach((obj, key) => {
+      result.set(key, obj);
+    });
+
+    return result as unknown as T;
+  }
+
+  if (tag === setTag) {
+    const set = obj as unknown as Set<any>;
+    const result = new Set();
+
+    set.forEach(obj => {
+      result.add(obj);
+    });
+
+    return result as unknown as T;
+  }
+
+  if (tag === argumentsTag) {
+    const args = obj as any;
+    const result = {} as any;
+
+    copyOwnProperties(result, args);
+
+    result.length = args.length;
+    result[Symbol.iterator] = args[Symbol.iterator];
+
+    return result as T;
+  }
+
+  const result = {} as any;
+
+  copyPrototype(result, obj);
+  copyOwnProperties(result, obj);
+  copySymbolProperties(result, obj);
+
+  return result;
+}
+
+function isCloneableObject(object: any): boolean {
+  switch (getTag(object)) {
+    case argumentsTag:
+    case arrayTag:
+    case arrayBufferTag:
+    case dataViewTag:
+    case booleanTag:
+    case dateTag:
+    case float32ArrayTag:
+    case float64ArrayTag:
+    case int8ArrayTag:
+    case int16ArrayTag:
+    case int32ArrayTag:
+    case mapTag:
+    case numberTag:
+    case objectTag:
+    case regexpTag:
+    case setTag:
+    case stringTag:
+    case symbolTag:
+    case uint8ArrayTag:
+    case uint8ClampedArrayTag:
+    case uint16ArrayTag:
+    case uint32ArrayTag: {
+      return true;
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
+function copyOwnProperties(target: any, source: any): void {
+  for (const key in source) {
+    if (Object.hasOwn(source, key)) {
+      target[key] = source[key];
+    }
+  }
+}
+
+function copySymbolProperties(target: any, source: any): void {
+  const symbols = Object.getOwnPropertySymbols(source);
+  for (let i = 0; i < symbols.length; i++) {
+    const symbol = symbols[i];
+    if (Object.prototype.propertyIsEnumerable.call(source, symbol)) {
+      target[symbol] = source[symbol];
+    }
+  }
+}
+
+function cloneStringObjectProperties(target: any, source: any): void {
+  const stringLength = source.valueOf().length;
+
+  for (const key in source) {
+    if (Object.hasOwn(source, key) && (Number.isNaN(Number(key)) || Number(key) >= stringLength)) {
+      target[key] = source[key];
+    }
+  }
+}
+
+function copyPrototype(target: any, source: any): void {
+  const proto = Object.getPrototypeOf(source);
+  if (proto !== null) {
+    const Ctor = source.constructor;
+    if (typeof Ctor === 'function') {
+      Object.setPrototypeOf(target, proto);
+    }
+  }
+}
diff --git a/src/compat/object/cloneWith.spec.ts b/src/compat/object/cloneWith.spec.ts
new file mode 100644
index 000000000..6ab385048
--- /dev/null
+++ b/src/compat/object/cloneWith.spec.ts
@@ -0,0 +1,738 @@
+import { describe, expect, it } from 'vitest';
+import * as lodashStable from 'es-toolkit/compat';
+import { cloneWith } from './cloneWith';
+import { noop } from '../../function/noop';
+import { args } from '../_internal/args';
+import { slice } from '../_internal/slice';
+import { typedArrays } from '../_internal/typedArrays';
+
+describe('cloneWith', () => {
+  it('should provide the correct `customizer` arguments', () => {
+    const argsList: any[] = [];
+
+    class Foo {}
+    const object = new Foo();
+
+    cloneWith(object, function () {
+      const length = arguments.length;
+      // eslint-disable-next-line prefer-rest-params
+      const args = slice.call(arguments, 0, length - (length > 1 ? 1 : 0));
+      argsList.push(args);
+    });
+
+    expect(argsList).toEqual([[object]]);
+  });
+
+  it('should handle cloning when `customizer` returns `undefined`', () => {
+    const actual = cloneWith({ a: { b: 'c' } }, noop);
+    expect(actual).toEqual({ a: { b: 'c' } });
+  });
+
+  it('should clone a plain object when `customizer` returns `undefined`', () => {
+    const object = { a: 1, b: 2 };
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone an array when `customizer` returns `undefined`', () => {
+    const array = [1, 2, 3];
+    const actual = cloneWith(array, noop);
+
+    expect(actual).toEqual(array);
+    expect(actual).not.toBe(array);
+  });
+
+  it('should perform a shallow clone when `customizer` returns `undefined`', () => {
+    const object = { a: [1, 2, 3] };
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+    expect(actual.a).toBe(object.a);
+  });
+
+  it('should accept a customizer callback', () => {
+    const customizer = (value: any) => {
+      if (lodashStable.isPlainObject(value)) {
+        return { value: 'replaced' };
+      }
+    };
+
+    const object = { a: 1, b: 2 };
+    const actual1 = cloneWith(object, customizer);
+    expect(actual1).toEqual({ value: 'replaced' });
+
+    const array = [1, 2, 3];
+    const actual2 = cloneWith(array, customizer);
+    expect(actual2).toEqual(array);
+    expect(actual2).not.toBe(array);
+
+    const nestedObject = { a: { b: 1 }, c: [1, 2] };
+    const actual3 = cloneWith(nestedObject, customizer);
+    expect(actual3).toEqual({ value: 'replaced' });
+  });
+
+  it('should handle customizer returning primitives', () => {
+    const customizer = (value: any) => {
+      if (typeof value === 'number') {
+        return value * 2;
+      }
+      if (typeof value === 'string') {
+        return value.toUpperCase();
+      }
+    };
+
+    expect(cloneWith(42, customizer)).toBe(84);
+    expect(cloneWith('hello', customizer)).toBe('HELLO');
+    expect(cloneWith(true, customizer)).toBe(true);
+  });
+
+  it('should clone arguments objects with customizer', () => {
+    const customizer = (value: any) => {
+      if (lodashStable.isArguments(value)) {
+        return ['replaced'];
+      }
+    };
+
+    const actual = cloneWith(args, customizer);
+    expect(actual).toEqual(['replaced']);
+  });
+
+  it('should clone date objects with customizer', () => {
+    const date = new Date();
+    const customizer = (value: any) => {
+      if (lodashStable.isDate(value)) {
+        return new Date(2000, 0, 1);
+      }
+    };
+
+    const actual = cloneWith(date, customizer);
+    expect(actual).toEqual(new Date(2000, 0, 1));
+  });
+
+  it('should provide correct arguments to customizer in array values', () => {
+    const argsList: any[] = [];
+    const array = [1, 2, 3];
+
+    cloneWith(array, function () {
+      const length = arguments.length;
+      // eslint-disable-next-line prefer-rest-params
+      argsList.push(slice.call(arguments, 0, length - (length > 1 ? 1 : 0)));
+    });
+
+    expect(argsList[0]).toEqual([array]);
+  });
+
+  it('should work with a function customizer', () => {
+    const customizer = function (value: any) {
+      return lodashStable.isPlainObject(value) ? undefined : value;
+    };
+
+    const actual = cloneWith({ a: { b: 'c' } }, customizer);
+    expect(actual).toEqual({ a: { b: 'c' } });
+  });
+
+  it('should clone objects with a customizer that returns a primitive', () => {
+    const customizer = function (value: any) {
+      if (lodashStable.isPlainObject(value)) {
+        return 42;
+      }
+    };
+
+    const actual = cloneWith({ a: { b: 'c' } }, customizer);
+    expect(actual).toBe(42);
+  });
+
+  it('should clone arguments objects when customizer returns undefined', () => {
+    const actual = cloneWith(args, noop);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('2', 3);
+    expect(actual).toHaveProperty('length', 3);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arguments objects with Symbol.iterator when customizer returns undefined', () => {
+    const args = {
+      0: 1,
+      1: 2,
+      length: 2,
+      [Symbol.iterator]: Array.prototype[Symbol.iterator],
+    };
+
+    const actual = cloneWith(args, noop);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('length', 2);
+    expect(actual[Symbol.iterator]).toBe(Array.prototype[Symbol.iterator]);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone array-like objects when customizer returns undefined', () => {
+    const object = { 0: 'a', length: 1 };
+
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone booleans when customizer returns undefined', () => {
+    const object = false;
+
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone boolean objects when customizer returns undefined', () => {
+    const object = Object(false);
+    const customizer = () => undefined;
+    const actual = cloneWith(object, customizer);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone date objects when customizer returns undefined', () => {
+    const object = new Date();
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone Foo instances when customizer returns undefined', () => {
+    class Foo {}
+
+    const object = new Foo();
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects with object values when customizer returns undefined', () => {
+    const object = { a: /a/, b: ['B'], c: { C: 1 } };
+
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone maps when customizer returns undefined', () => {
+    const map = new Map([
+      ['a', 1],
+      ['b', 2],
+    ]);
+
+    const actual = cloneWith(map, noop);
+
+    expect(actual).toEqual(map);
+    expect(actual).not.toBe(map);
+  });
+
+  it('should clone null values even with customizer', () => {
+    const object = null;
+    const customizer = () => undefined;
+    const actual = cloneWith(object, customizer);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone number objects when customizer returns undefined', () => {
+    const object = Object(0);
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone regexes when customizer returns undefined', () => {
+    const object = /a/gim;
+
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone sets when customizer returns undefined', () => {
+    const set = new Set([1, 2]);
+
+    const actual = cloneWith(set, noop);
+
+    expect(actual).toEqual(set);
+    expect(actual).not.toBe(set);
+  });
+
+  it('should clone string objects when customizer returns undefined', () => {
+    const object = Object('a');
+    const customizer = () => undefined;
+    const actual = cloneWith(object, customizer);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+    expect(typeof actual).toBe('object');
+  });
+
+  it('should clone undefined values even with customizer', () => {
+    const object = undefined;
+    const customizer = () => undefined;
+    const actual = cloneWith(object, customizer);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone array buffers when customizer returns undefined', () => {
+    if (typeof ArrayBuffer === 'undefined') {
+      return;
+    }
+
+    const buffer = new ArrayBuffer(10);
+    const actual = cloneWith(buffer, noop);
+
+    expect(actual.byteLength).toBe(buffer.byteLength);
+    expect(actual).not.toBe(buffer);
+  });
+
+  it('should clone array buffers of various sizes when customizer returns undefined', () => {
+    if (typeof ArrayBuffer === 'undefined') {
+      return;
+    }
+
+    const sizes = [0, 1, 16, 1024];
+
+    lodashStable.each(sizes, size => {
+      const buffer = new ArrayBuffer(size);
+      const actual = cloneWith(buffer, noop);
+
+      expect(actual.byteLength).toBe(size);
+      expect(actual).not.toBe(buffer);
+    });
+  });
+
+  it('should clone DataView objects when customizer returns undefined', () => {
+    if (typeof ArrayBuffer === 'undefined' || typeof DataView === 'undefined') {
+      return;
+    }
+
+    const buffer = new ArrayBuffer(16);
+    const dataView = new DataView(buffer);
+
+    dataView.setInt8(0, 42);
+    dataView.setInt16(2, 12345, true);
+    dataView.setFloat32(4, 3.14159, true);
+    dataView.setFloat64(8, 123456789.123456, true);
+
+    const cloned = cloneWith(dataView, noop);
+
+    expect(cloned).not.toBe(dataView);
+    expect(cloned.buffer).not.toBe(dataView.buffer);
+    expect(cloned.byteOffset).toBe(dataView.byteOffset);
+    expect(cloned.byteLength).toBe(dataView.byteLength);
+
+    expect(cloned.getInt8(0)).toBe(42);
+    expect(cloned.getInt16(2, true)).toBe(12345);
+    expect(cloned.getFloat32(4, true)).toBeCloseTo(3.14159, 5);
+    expect(cloned.getFloat64(8, true)).toBeCloseTo(123456789.123456, 10);
+
+    dataView.setInt8(0, 100);
+    expect(cloned.getInt8(0)).toBe(42);
+  });
+
+  it('should clone `index` and `input` array properties when customizer returns undefined', () => {
+    const array = /c/.exec('abcde') || [];
+    const actual = cloneWith(array, noop) as any;
+
+    expect(actual.index).toBe(2);
+    expect(actual.input).toBe('abcde');
+  });
+
+  it('should clone `lastIndex` regexp property when customizer returns undefined', () => {
+    const regexp = /c/g;
+    regexp.exec('abcde');
+
+    const actual = cloneWith(regexp, noop);
+    expect(actual.lastIndex).toBe(3);
+  });
+
+  it('should clone expando properties when customizer returns undefined', () => {
+    const values = [false, true, 1, 'a'];
+    const expected = values.map(() => true);
+
+    const actual = values.map(value => {
+      const object = Object(value);
+      object.a = 1;
+      const cloned = cloneWith(object, noop);
+      return cloned.a === 1;
+    });
+
+    expect(actual).toEqual(expected);
+  });
+
+  it('should clone prototype objects when customizer returns undefined', () => {
+    class Foo {
+      b = 1;
+    }
+    Foo.prototype.b = 2;
+
+    const actual = cloneWith(Foo.prototype, noop);
+
+    expect(actual instanceof Foo).toBe(false);
+    expect(actual).toEqual({ b: 2 });
+  });
+
+  it('should set the `[[Prototype]]` of a clone when customizer returns undefined', () => {
+    class Foo {}
+
+    expect(cloneWith(new Foo(), noop) instanceof Foo).toBe(true);
+  });
+
+  it('should set the `[[Prototype]]` of a clone even when the `constructor` is incorrect and customizer returns undefined', () => {
+    class Foo {}
+
+    Foo.prototype.constructor = Object;
+    expect(cloneWith(new Foo(), noop) instanceof Foo).toBe(true);
+    Foo.prototype.constructor = Foo;
+  });
+
+  it('should ensure `value` constructor is a function before using its `[[Prototype]]` when customizer returns undefined', () => {
+    class Foo {}
+
+    Foo.prototype.constructor = null as any;
+    expect(cloneWith(new Foo(), noop) instanceof Foo).toBe(false);
+    Foo.prototype.constructor = Foo;
+  });
+
+  it('should handle objects with null prototype when customizer returns undefined', () => {
+    const obj = Object.create(null);
+    obj.a = 1;
+    obj.b = 2;
+
+    const actual = cloneWith(obj, noop);
+
+    expect(actual).toEqual({ a: 1, b: 2 });
+  });
+
+  it('should handle objects with modified prototype chain when customizer returns undefined', () => {
+    function CustomProto() {}
+    CustomProto.prototype.customMethod = function () {
+      return 'custom';
+    };
+
+    const obj = Object.create(CustomProto.prototype);
+    obj.a = 1;
+
+    const actual = cloneWith(obj, noop);
+
+    expect(actual.a).toBe(1);
+    expect(Object.getPrototypeOf(actual)).toBe(CustomProto.prototype);
+    expect((actual as any).customMethod()).toBe('custom');
+  });
+
+  it('should clone properties that shadow those on `Object.prototype` when customizer returns undefined', () => {
+    const object = {
+      constructor: Object.prototype.constructor,
+      hasOwnProperty: Object.prototype.hasOwnProperty,
+      isPrototypeOf: Object.prototype.isPrototypeOf,
+      propertyIsEnumerable: Object.prototype.propertyIsEnumerable,
+      toLocaleString: Object.prototype.toLocaleString,
+      toString: Object.prototype.toString,
+      valueOf: Object.prototype.valueOf,
+    };
+
+    const actual = cloneWith(object, noop);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone symbol properties when customizer returns undefined', () => {
+    function Foo(this: any) {
+      this[Symbol.for('c')] = { c: 1 };
+    }
+
+    if (Symbol) {
+      const symbol2 = Symbol('b');
+      Foo.prototype[symbol2] = 2;
+
+      const symbol3 = Symbol('c');
+      Object.defineProperty(Foo.prototype, symbol3, {
+        configurable: true,
+        enumerable: false,
+        writable: true,
+        value: 3,
+      });
+
+      const object = { a: { b: new (Foo as any)() } } as any;
+      object[Symbol.for('a')] = { b: 1 };
+
+      const actual = cloneWith(object, noop) as any;
+
+      expect(actual[Symbol.for('a')]).toBe(object[Symbol.for('a')]);
+      expect(actual.a).toBe(object.a);
+      expect(actual[Symbol.for('a')]).toEqual(object[Symbol.for('a')]);
+
+      const symbols = Object.getOwnPropertySymbols(actual.a.b);
+      expect(symbols.length).toBe(1);
+      expect(symbols[0]).toBe(Symbol.for('c'));
+      expect(actual.a.b[Symbol.for('c')]).toEqual(object.a.b[Symbol.for('c')]);
+      expect(actual.a.b[symbol2]).toEqual(object.a.b[symbol2]);
+      expect(actual.a.b[symbol3]).toEqual(object.a.b[symbol3]);
+    }
+  });
+
+  it('should handle mixed enumerable and non-enumerable symbol properties when customizer returns undefined', () => {
+    const enumSymbol = Symbol('enumerable');
+    const nonEnumSymbol = Symbol('non-enumerable');
+
+    const object = { a: 1 } as any;
+    object[enumSymbol] = 'visible';
+
+    Object.defineProperty(object, nonEnumSymbol, {
+      value: 'hidden',
+      enumerable: false,
+      configurable: true,
+      writable: true,
+    });
+
+    const actual = cloneWith(object, noop);
+
+    expect(actual.a).toBe(1);
+    expect(actual[enumSymbol]).toBe('visible');
+    expect(actual[nonEnumSymbol]).toBeUndefined();
+
+    const symbols = Object.getOwnPropertySymbols(actual);
+    expect(symbols).toContain(enumSymbol);
+    expect(symbols).not.toContain(nonEnumSymbol);
+  });
+
+  it('should clone symbol objects when customizer returns undefined', () => {
+    const symbol = Symbol('a');
+    expect(cloneWith(symbol, noop)).toBe(symbol);
+
+    const object = Object(symbol);
+    const actual = cloneWith(object, noop);
+
+    expect(typeof actual).toBe('object');
+    expect(typeof actual.valueOf()).toBe('symbol');
+    expect(actual).not.toBe(object);
+  });
+
+  it('should not clone symbol primitives when customizer returns undefined', () => {
+    const symbol = Symbol('a');
+    expect(cloneWith(symbol, noop)).toBe(symbol);
+  });
+
+  it('should not error on DOM elements when customizer returns undefined', () => {
+    if (typeof document === 'undefined') {
+      return;
+    }
+
+    const element = document.createElement('div');
+
+    try {
+      expect(cloneWith(element, noop)).toEqual({});
+    } catch (e) {
+      expect(false).toBe(true);
+    }
+  });
+
+  it('should perform a shallow clone when used as an iteratee for methods like `_.map` and customizer returns undefined', () => {
+    const expected = [{ a: [0] }, { b: [1] }];
+    const customizer = () => undefined;
+    const actual = expected.map(obj => cloneWith(obj, customizer));
+
+    expect(actual).toEqual(expected);
+    expect(actual[0]).not.toBe(expected[0]);
+    expect(actual[0].a).toBe(expected[0].a);
+    expect(actual[1].b).toBe(expected[1].b);
+  });
+
+  lodashStable.each(typedArrays, type => {
+    it(`should clone ${type} values when customizer returns undefined`, () => {
+      const Ctor = globalThis[type as keyof typeof globalThis] as any;
+
+      if (!Ctor) {
+        return;
+      }
+
+      lodashStable.times(2, index => {
+        const buffer = new ArrayBuffer(24);
+        const view = index ? new Ctor(buffer, 8, 1) : new Ctor(buffer);
+        const actual = cloneWith(view, noop);
+
+        expect(actual).toEqual(view);
+        expect(actual).not.toBe(view);
+        expect(actual.buffer === view.buffer).toBe(true);
+        expect(actual.byteOffset).toBe(view.byteOffset);
+        expect(actual.length).toBe(view.length);
+      });
+    });
+  });
+
+  const uncloneableObjects = [
+    'DOM elements',
+    'functions',
+    'async functions',
+    'generator functions',
+    'the Proxy constructor',
+  ];
+
+  lodashStable.each(uncloneableObjects, type => {
+    it(`should not clone ${type} when customizer returns undefined`, () => {
+      let value;
+
+      if (type === 'DOM elements' && typeof document !== 'undefined') {
+        value = document.body;
+      } else if (type === 'functions') {
+        value = function () {};
+      } else if (type === 'async functions') {
+        value = async function () {};
+      } else if (type === 'generator functions') {
+        value = function* () {};
+      } else if (type === 'the Proxy constructor') {
+        value = Proxy;
+      }
+
+      if (value) {
+        const object = { a: value, b: { c: value } };
+        const actual = cloneWith(object, noop);
+
+        expect(actual).toEqual(object);
+        expect(actual).not.toBe(object);
+        expect(cloneWith(value, noop)).toEqual({});
+      }
+    });
+  });
+
+  const errorTypes = ['Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError'];
+
+  lodashStable.each(errorTypes, type => {
+    it(`should not clone ${type}s when customizer returns undefined`, () => {
+      const Ctor = globalThis[type as keyof typeof globalThis];
+      const value = new Ctor('error');
+
+      const object = { a: value, b: { c: value } };
+      const actual = cloneWith(object, noop);
+
+      expect(actual).toEqual(object);
+      expect(actual).not.toBe(object);
+      expect(cloneWith(value, noop)).toEqual({});
+    });
+  });
+
+  it('should handle customizer with return values for custom types', () => {
+    class CustomType {
+      value: number;
+      constructor(value: number) {
+        this.value = value;
+      }
+    }
+
+    const customizer = (value: any) => {
+      if (value instanceof CustomType) {
+        return new CustomType(value.value * 2);
+      }
+    };
+
+    const original = new CustomType(5);
+    const cloned = cloneWith(original, customizer);
+
+    expect(cloned).not.toBe(original);
+    expect(cloned instanceof CustomType).toBe(true);
+    expect((cloned as CustomType).value).toBe(10);
+  });
+
+  it('should customize only the top level value', () => {
+    const customizer = (value: any) => {
+      if (typeof value === 'number') {
+        return value * 2;
+      }
+    };
+
+    const obj = {
+      num: 42,
+      str: 'test',
+    };
+
+    const result = cloneWith(obj, customizer);
+
+    expect(result.num).toBe(42);
+    expect(result.str).toBe('test');
+    expect(result).not.toBe(obj);
+  });
+
+  it('should apply customizer to objects with custom tags', () => {
+    if (typeof Map === 'undefined') {
+      return;
+    }
+
+    const map = new Map([
+      ['a', 1],
+      ['b', 2],
+    ]);
+
+    const customizer = (value: any) => {
+      if (value instanceof Map) {
+        const newMap = new Map();
+        value.forEach((val, key) => {
+          newMap.set(key.toUpperCase(), val * 10);
+        });
+        return newMap;
+      }
+    };
+
+    const result = cloneWith(map, customizer);
+
+    expect(result).not.toBe(map);
+    expect(result.get('A')).toBe(10);
+    expect(result.get('B')).toBe(20);
+    expect(result.get('a')).toBeUndefined();
+  });
+
+  it('should allow returning null from customizer', () => {
+    const obj = { a: 1, b: 2 };
+
+    const customizer = () => null;
+
+    const result = cloneWith(obj, customizer);
+    expect(result).toBe(null);
+  });
+
+  it('should apply customizer only to the top level value', () => {
+    const obj = {
+      a: 1,
+      b: 'keep',
+      c: { nested: true },
+    };
+
+    const customizer = (value: any) => {
+      if (typeof value === 'object' && value !== null) {
+        return { modified: true };
+      }
+    };
+
+    const result = cloneWith(obj, customizer);
+
+    expect(result).toEqual({ modified: true });
+  });
+
+  it('should use clone when customizer is not provided', () => {
+    const obj = { a: 1, b: 2 };
+
+    const result = cloneWith(obj);
+
+    expect(result).toEqual(obj);
+    expect(result).not.toBe(obj);
+  });
+});
diff --git a/src/compat/object/cloneWith.ts b/src/compat/object/cloneWith.ts
new file mode 100644
index 000000000..66d42e581
--- /dev/null
+++ b/src/compat/object/cloneWith.ts
@@ -0,0 +1,59 @@
+import { clone } from './clone.ts';
+
+/**
+ * Creates a shallow clone of the given object with customization.
+ * This method is like `_.clone` except that it accepts a customizer which
+ * is invoked to produce the cloned value. If customizer returns undefined,
+ * cloning is handled by the method instead.
+ *
+ * If no customizer is provided, it behaves like `clone`.
+ *
+ * @template T - The type of the object.
+ * @param {T} value - The value to clone.
+ * @param {Function} [customizer] - The function to customize cloning.
+ * @returns {T} - A shallow clone of the given object.
+ *
+ * @example
+ * // Clone a primitive values
+ * const num = 29;
+ * const clonedNum = cloneWith(num);
+ * console.log(clonedNum); // 29
+ * console.log(clonedNum === num) ; // true
+ *
+ * @example
+ * // Clone an array
+ * const arr = [1, 2, 3];
+ * const clonedArr = cloneWith(arr);
+ * console.log(clonedArr); // [1, 2, 3]
+ * console.log(clonedArr === arr); // false
+ *
+ * @example
+ * // Clone an object
+ * const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };
+ * const clonedObj = cloneWith(obj);
+ * console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }
+ * console.log(clonedObj === obj); // false
+ *
+ * @example
+ * // Clone an object with a customizer
+ * const obj = { a: 1, b: 2 };
+ * const clonedObj = cloneWith(obj, (value) => {
+ *   if (typeof value === 'number') {
+ *     return value * 2; // Double the number
+ *   }
+ *   // Returning undefined uses the default cloning
+ * });
+ * console.log(clonedObj); // { a: 2, b: 4 }
+ */
+export function cloneWith<T>(value: T, customizer?: (value: any) => any): T {
+  if (!customizer) {
+    return clone(value);
+  }
+
+  const result = customizer(value);
+  if (result !== undefined) {
+    return result;
+  }
+
+  return clone(value);
+}


Reviews:
user: login: raon0211
body: Wow, great work! 👍  Thanks so much for this pull request!
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-04T08:29:05Z
user: login: raon0211
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-08T07:50:19Z
user: login: raon0211
body: Looks nice. Let me add some missing test cases from lodash. Thanks so much for your great contribution! It is a great help for us.
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-05-08T07:51:38Z
user: login: raon0211
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-08T07:52:18Z
user: login: raon0211
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-08T07:52:42Z

Review comments:
diff_hunk: @@ -0,0 +1,541 @@
+import { describe, expect, it } from 'vitest';
+import * as lodashStable from 'es-toolkit/compat';
+import { clone } from './clone';
+import { args } from '../_internal/args';
+import { typedArrays } from '../_internal/typedArrays';
+
+describe('clone', () => {
+  it('should perform a shallow clone', () => {
+    const array = [{ a: 0 }, { b: 1 }];
+    const actual = clone(array);
+
+    expect(actual).toEqual(array);
+    expect(actual).not.toBe(array);
+    expect(actual[0]).toBe(array[0]);
+  });
+
+  it('should clone arguments objects', () => {
+    const actual = clone(args);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('2', 3);
+    expect(actual).toHaveProperty('length', 3);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arguments objects with Symbol.iterator', () => {
+    const args = {
+      0: 1,
+      1: 2,
+      length: 2,
+      [Symbol.iterator]: Array.prototype[Symbol.iterator],
+    };
+
+    const actual = clone(args);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('length', 2);
+    expect(actual[Symbol.iterator]).toBe(Array.prototype[Symbol.iterator]);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arrays', () => {
+    const object = ['a', ''];
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone array-like objects', () => {
+    const object = { 0: 'a', length: 1 };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone booleans', () => {
+    const object = false;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone boolean objects', () => {
+    const object = Object(false);
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone date objects', () => {
+    const object = new Date();
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone Foo instances', () => {
+    class Foo {}
+
+    const object = new Foo();
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects', () => {
+    const object = { a: 0, b: 1, c: 2 };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects with object values', () => {
+    const object = { a: /a/, b: ['B'], c: { C: 1 } };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone maps', () => {
+    const map = new Map([
+      ['a', 1],
+      ['b', 2],
+    ]);
+
+    const actual = clone(map);
+
+    expect(actual).toEqual(map);
+    expect(actual).not.toBe(map);
+  });
+
+  it('should clone null values', () => {
+    const object = null;
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone numbers', () => {
+    const object = 0;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone number objects', () => {
+    const object = Object(0);
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone regexes', () => {
+    const object = /a/gim;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone sets', () => {
+    const set = new Set([1, 2]);
+
+    const actual = clone(set);
+
+    expect(actual).toEqual(set);
+    expect(actual).not.toBe(set);
+  });
+
+  it('should clone strings', () => {
+    const object = 'a';
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
path: src/compat/object/clone.spec.ts
user: login: raon0211
body: ```suggestion

  it('should clone string objects', () => {
    const object = Object('a');

    const actual = clone(object);

    expect(actual).toEqual(object);
    expect(actual).not.toBe(object);
  });
  
```
created_at: 2025-05-08T07:50:19Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,550 @@
+import { describe, expect, it } from 'vitest';
+import * as lodashStable from 'es-toolkit/compat';
+import { clone } from './clone';
+import { args } from '../_internal/args';
+import { typedArrays } from '../_internal/typedArrays';
+
+describe('clone', () => {
+  it('should perform a shallow clone', () => {
+    const array = [{ a: 0 }, { b: 1 }];
+    const actual = clone(array);
+
+    expect(actual).toEqual(array);
+    expect(actual).not.toBe(array);
+    expect(actual[0]).toBe(array[0]);
+  });
+
+  it('should clone arguments objects', () => {
+    const actual = clone(args);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('2', 3);
+    expect(actual).toHaveProperty('length', 3);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arguments objects with Symbol.iterator', () => {
+    const args = {
+      0: 1,
+      1: 2,
+      length: 2,
+      [Symbol.iterator]: Array.prototype[Symbol.iterator],
+    };
+
+    const actual = clone(args);
+
+    expect(actual).toHaveProperty('0', 1);
+    expect(actual).toHaveProperty('1', 2);
+    expect(actual).toHaveProperty('length', 2);
+    expect(actual[Symbol.iterator]).toBe(Array.prototype[Symbol.iterator]);
+    expect(actual).not.toBe(args);
+  });
+
+  it('should clone arrays', () => {
+    const object = ['a', ''];
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone array-like objects', () => {
+    const object = { 0: 'a', length: 1 };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone booleans', () => {
+    const object = false;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone boolean objects', () => {
+    const object = Object(false);
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone date objects', () => {
+    const object = new Date();
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone Foo instances', () => {
+    class Foo {}
+
+    const object = new Foo();
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects', () => {
+    const object = { a: 0, b: 1, c: 2 };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone objects with object values', () => {
+    const object = { a: /a/, b: ['B'], c: { C: 1 } };
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone maps', () => {
+    const map = new Map([
+      ['a', 1],
+      ['b', 2],
+    ]);
+
+    const actual = clone(map);
+
+    expect(actual).toEqual(map);
+    expect(actual).not.toBe(map);
+  });
+
+  it('should clone null values', () => {
+    const object = null;
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone numbers', () => {
+    const object = 0;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone number objects', () => {
+    const object = Object(0);
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone regexes', () => {
+    const object = /a/gim;
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+
+  it('should clone sets', () => {
+    const set = new Set([1, 2]);
+
+    const actual = clone(set);
+
+    expect(actual).toEqual(set);
+    expect(actual).not.toBe(set);
+  });
+
+  it('should clone strings', () => {
+    const object = 'a';
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).toBe(object);
+  });
+
+  it('should clone string objects', () => {
+    const object = Object('a');
+
+    const actual = clone(object);
+
+    expect(actual).toEqual(object);
+    expect(actual).not.toBe(object);
+  });
+  
path: src/compat/object/clone.spec.ts
user: login: raon0211
body: ```suggestion

```
created_at: 2025-05-08T07:52:18Z
author_association: COLLABORATOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-02T06:30:35Z
body: [vc]: #lTro+jg5E89B86Nv1GEOLTIogMq1NwJ2pu9sX5YEAWY=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJlcy10b29sa2l0Iiwicm9vdERpcmVjdG9yeSI6ImRvY3MiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdG9zcy0wNWE3NDlkNi9lcy10b29sa2l0Lzhla1VySGJCejdwd01abzNVU3RzR3JFWURhZ1EiLCJwcmV2aWV3VXJsIjoiZXMtdG9vbGtpdC1naXQtZm9yay1zaHJlbjIwNy1mZWF0LWNsb25lLXRvc3MtMDVhNzQ5ZDYudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJlcy10b29sa2l0LWdpdC1mb3JrLXNocmVuMjA3LWZlYXQtY2xvbmUtdG9zcy0wNWE3NDlkNi52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **es-toolkit** | ✅ Ready ([Inspect](https://vercel.com/toss-05a749d6/es-toolkit/8ekUrHbBz7pwMZo3UStsGrEYDagQ)) | [Visit Preview](https://es-toolkit-git-fork-shren207-feat-clone-toss-05a749d6.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/es-toolkit-git-fork-shren207-feat-clone-toss-05a749d6.vercel.app?via=pr-comment-feedback-link) | May 8, 2025 8:00am |


author_association: NONE
user: login: codecov-commenter
created_at: 2025-05-02T06:34:25Z
body: ## [Codecov](https://app.codecov.io/gh/toss/es-toolkit/pull/1160?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toss) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.56%. Comparing base [(`5f76be6`)](https://app.codecov.io/gh/toss/es-toolkit/commit/5f76be6accbb4b26174697e19cf0d2cf3eb9dab2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toss) to head [(`e9548eb`)](https://app.codecov.io/gh/toss/es-toolkit/commit/e9548eb9781e6b9285fc2caae6786779ab0def51?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toss).
> Report is 5 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/toss/es-toolkit/pull/1160/graphs/tree.svg?width=650&height=150&src=pr&token=8N5S3AR3C7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toss)](https://app.codecov.io/gh/toss/es-toolkit/pull/1160?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=toss)

```diff
@@            Coverage Diff             @@
##             main    #1160      +/-   ##
==========================================
+ Coverage   99.54%   99.56%   +0.01%     
==========================================
  Files         443      447       +4     
  Lines        3983     4136     +153     
  Branches     1171     1227      +56     
==========================================
+ Hits         3965     4118     +153     
  Misses         18       18              
```

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE