diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 3f23ed753..fef4c2f21 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -479,6 +479,13 @@ export class SwiftRuntime {
                 return obj instanceof constructor;
             },
 
+            swjs_value_equals: (lhs_ref: ref, rhs_ref: ref) => {
+                const memory = this.memory;
+                const lhs = memory.getObject(lhs_ref);
+                const rhs = memory.getObject(rhs_ref);
+                return lhs == rhs;
+            },
+
             swjs_create_function: (
                 host_func_id: number,
                 line: number,
diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts
index 587b60770..b81818ade 100644
--- a/Runtime/src/types.ts
+++ b/Runtime/src/types.ts
@@ -96,6 +96,7 @@ export interface ImportedFunctions {
         exception_payload2_ptr: pointer
     ): number;
     swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean;
+    swjs_value_equals(lhs_ref: ref, rhs_ref: ref): boolean;
     swjs_create_function(host_func_id: number, line: number, file: ref): number;
     swjs_create_typed_array(
         constructor_ref: ref,
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
index cd88a5302..b4ad10237 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSString.swift
@@ -77,7 +77,7 @@ public struct JSString: LosslessStringConvertible, Equatable {
     ///   - lhs: A string to compare.
     ///   - rhs: Another string to compare.
     public static func == (lhs: JSString, rhs: JSString) -> Bool {
-        return lhs.guts.buffer == rhs.guts.buffer
+        return swjs_value_equals(lhs.guts.jsRef, rhs.guts.jsRef)
     }
 }
 
diff --git a/Sources/JavaScriptKit/Runtime/index.d.ts b/Sources/JavaScriptKit/Runtime/index.d.ts
index 5bfa4c242..adfa8161d 100644
--- a/Sources/JavaScriptKit/Runtime/index.d.ts
+++ b/Sources/JavaScriptKit/Runtime/index.d.ts
@@ -50,6 +50,7 @@ interface ImportedFunctions {
     swjs_call_new(ref: number, argv: pointer, argc: number): number;
     swjs_call_throwing_new(ref: number, argv: pointer, argc: number, exception_kind_ptr: pointer, exception_payload1_ptr: pointer, exception_payload2_ptr: pointer): number;
     swjs_instanceof(obj_ref: ref, constructor_ref: ref): boolean;
+    swjs_value_equals(lhs_ref: ref, rhs_ref: ref): boolean;
     swjs_create_function(host_func_id: number, line: number, file: ref): number;
     swjs_create_typed_array(constructor_ref: ref, elementsPtr: pointer, length: number): number;
     swjs_load_typed_array(ref: ref, buffer: pointer): void;
diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js
index a3bc31397..840d843b5 100644
--- a/Sources/JavaScriptKit/Runtime/index.js
+++ b/Sources/JavaScriptKit/Runtime/index.js
@@ -604,6 +604,12 @@
                     const constructor = memory.getObject(constructor_ref);
                     return obj instanceof constructor;
                 },
+                swjs_value_equals: (lhs_ref, rhs_ref) => {
+                    const memory = this.memory;
+                    const lhs = memory.getObject(lhs_ref);
+                    const rhs = memory.getObject(rhs_ref);
+                    return lhs == rhs;
+                },
                 swjs_create_function: (host_func_id, line, file) => {
                     var _a;
                     const fileString = this.memory.getObject(file);
diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs
index ba1b6beaf..50799f2c1 100644
--- a/Sources/JavaScriptKit/Runtime/index.mjs
+++ b/Sources/JavaScriptKit/Runtime/index.mjs
@@ -598,6 +598,12 @@ class SwiftRuntime {
                 const constructor = memory.getObject(constructor_ref);
                 return obj instanceof constructor;
             },
+            swjs_value_equals: (lhs_ref, rhs_ref) => {
+                const memory = this.memory;
+                const lhs = memory.getObject(lhs_ref);
+                const rhs = memory.getObject(rhs_ref);
+                return lhs == rhs;
+            },
             swjs_create_function: (host_func_id, line, file) => {
                 var _a;
                 const fileString = this.memory.getObject(file);
diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index 2b96a81ea..05f4ed0f8 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -257,6 +257,16 @@ IMPORT_JS_FUNCTION(swjs_call_throwing_new, JavaScriptObjectRef, (const JavaScrip
 IMPORT_JS_FUNCTION(swjs_instanceof, bool, (const JavaScriptObjectRef obj,
                                            const JavaScriptObjectRef constructor))
 
+/// Acts like JavaScript `==` operator.
+/// Performs "==" comparison, a.k.a the "Abstract Equality Comparison"
+/// algorithm defined in the ECMAScript.
+/// https://262.ecma-international.org/11.0/#sec-abstract-equality-comparison
+///
+/// @param lhs The left-hand side value to compare.
+/// @param rhs The right-hand side value to compare.
+/// @result Return `true` if `lhs` is `==` to `rhs`. Return `false` if not.
+IMPORT_JS_FUNCTION(swjs_value_equals, bool, (const JavaScriptObjectRef lhs, const JavaScriptObjectRef rhs))
+
 /// Creates a JavaScript thunk function that calls Swift side closure.
 /// See also comments on JSFunction.swift
 ///
diff --git a/Tests/JavaScriptKitTests/JSStringTests.swift b/Tests/JavaScriptKitTests/JSStringTests.swift
new file mode 100644
index 000000000..456c24147
--- /dev/null
+++ b/Tests/JavaScriptKitTests/JSStringTests.swift
@@ -0,0 +1,13 @@
+import JavaScriptKit
+import XCTest
+
+final class JSStringTests: XCTestCase {
+    func testEquatable() {
+        let string1 = JSString("Hello, world!")
+        let string2 = JSString("Hello, world!")
+        let string3 = JSString("Hello, world")
+        XCTAssertEqual(string1, string1)
+        XCTAssertEqual(string1, string2)
+        XCTAssertNotEqual(string1, string3)
+    }
+}