From c8c8ce0bcfdc1a016c080fefa0f8c6b629c98896 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Mon, 25 Nov 2024 13:00:06 +0100
Subject: [PATCH] fix: optimize instanceof operator

---
 source/types/is.mjs     | 10 ++++++-
 test/cases/types/is.mjs | 60 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/source/types/is.mjs b/source/types/is.mjs
index 1fc67ae6e..de9754d7c 100644
--- a/source/types/is.mjs
+++ b/source/types/is.mjs
@@ -173,7 +173,15 @@ function isInstance(value, instance) {
 	if (!isObject(value)) return false;
 	if (!isFunction(instance)) return false;
 	if (!instance.hasOwnProperty("prototype")) return false;
-	return value instanceof instance ? true : false;
+	if(value instanceof instance) return true;
+
+	let proto = Object.getPrototypeOf(value);
+	while (proto != null) {
+		if (proto === instance.prototype) return true;
+		proto = Object.getPrototypeOf(proto);
+	}
+	
+	return false;
 }
 
 /**
diff --git a/test/cases/types/is.mjs b/test/cases/types/is.mjs
index 8be3a39ae..852ee3109 100644
--- a/test/cases/types/is.mjs
+++ b/test/cases/types/is.mjs
@@ -238,7 +238,65 @@ describe('Is', function () {
             });
         });
     });
-    
+
+    describe('.isInstanceExtended()', function () {
+        class Base {}
+        class Derived extends Base {}
+        function AnotherClass() {}
+
+        let baseInstance = new Base();
+        let derivedInstance = new Derived();
+
+        // Test cases
+        const cases = [
+            [() => {}, undefined, false,"function vs undefined"],
+            [new ID(), ()=>{}, false, "ID instance vs function"],
+            [new ID(), ID, true, "ID instance vs ID"],
+            ['test1', undefined, false, "string vs undefined"],
+            [undefined, undefined, false, "undefined vs undefined"],
+            [null, undefined, false, "null vs undefined"],
+            [2, undefined, false, "number vs undefined"],
+            [false, undefined, false, "false vs undefined"],
+            [parseInt("a"), undefined, false, "NaN vs undefined"],
+            [true, undefined, false, "true vs undefined"],
+            [4.5, undefined, false, "float vs undefined"],
+            [{}, undefined, false, "object vs undefined"],
+            [[1, 2, 3], undefined, false, "array vs undefined"],
+            [Symbol("foo"), undefined, false, "symbol vs undefined"],
+            [baseInstance, Base, true, "Base instance vs Base"],
+            [derivedInstance, Base, true, "Derived instance vs Base"],
+            [derivedInstance, Derived, true, "Derived instance vs Derived"],
+            [baseInstance, Derived, false, "Base instance vs Derived"],
+            [baseInstance, AnotherClass, false, "Base instance vs AnotherClass"],
+            [derivedInstance, ()=>{}, false, "Derived instance vs function"],
+            [new AnotherClass(), AnotherClass, true, "AnotherClass instance vs AnotherClass"],
+        ];
+
+        // Adding prototype modification test
+        let protoModifiedInstance = new Base();
+        Object.setPrototypeOf(protoModifiedInstance, Derived.prototype);
+        cases.push(
+            [protoModifiedInstance, Base, true, "Proto modified Base instance vs Base"], 
+            [protoModifiedInstance, Derived, true, "Proto modified Base instance vs Derived"]
+        );
+
+        // Running the tests
+        cases.forEach(function (data) {
+            const a = data.shift();
+            const b = data.shift();
+            const c = data.shift();
+            const d = data.shift();
+
+            it('isInstance(' + JSON.stringify(a) + ', [Function]) should return ' + c, function () {
+                
+                if (isInstance(a, b)!==c) {
+                    console.log(d)
+                }
+                
+                expect(isInstance(a, b)).to.equal(c);
+            });
+        });
+    });
     
     describe('.isObject()', function () {
 
-- 
GitLab