Created
February 25, 2023 21:09
-
-
Save dfkaye/06f7078b157117545fa70faa02957299 to your computer and use it in GitHub Desktop.
detect whether something is a class in JavaScript, not just a function; warning: this is not tamper-proof
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 25 February 2023 | |
// detect whether something is a class in JavaScript, not just a function. | |
// warning: this is not tamper-proof. | |
// @webreflection figured this out | |
// https://stackoverflow.com/questions/30758961/how-to-check-if-a-variable-is-an-es6-class-declaration/75567955#75567955 | |
// The test? | |
// The variable is typeof function and its prototype field is not writable. | |
// Example with a function | |
var F = function () {} | |
console.log(typeof F); | |
// "function" | |
console.log(Object.getOwnPropertyDescriptor(F, "prototype").writable); | |
// true | |
// Example with a class | |
var A = class {} | |
console.log(typeof A); | |
// "function" | |
console.log(Object.getOwnPropertyDescriptor(A, "prototype").writable); | |
// false | |
/////////////////////////////////////// | |
// So let's make a function to do that. | |
/////////////////////////////////////// | |
function isClass(Class) { | |
return ( | |
typeof Class == 'function' | |
&& !Object.getOwnPropertyDescriptor(Class, "prototype").writable | |
); | |
} | |
/* test it out */ | |
console.log("class A is a class:", isClass(A)); | |
// class A is a class: true | |
console.log("function F is a class:", isClass(F)); | |
// function F is a class: false | |
console.log("NaN is a class:", isClass(NaN)); | |
// NaN is a class: false | |
// However, this isn't tamper-proof. | |
// We can spoof this behavior by overwriting the prototype property descriptor... | |
var fake = function () {}; | |
Object.defineProperty(fake, "prototype", { | |
value: fake.prototype, | |
writable: false | |
}); | |
console.log("fake is a class:", isClass(fake)); | |
// fake is a class: true | |
// ...or via freezing: | |
var frozen = Object.freeze(function () {}); | |
console.log("frozen is a class:", isClass(frozen)); | |
// frozen is a class: true | |
also, form my SO answer, this is not there by accident:
basically there is no correct answer to this question, just a list of compromises and targets to consider.
enjoy your Sunday 👋
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
you don't need
value
there,Object.defineProperty(fake, "prototype", {writable: false});
is enough, asdefineProperty
anddefineProperties
change descriptors values already present, sovalue
is unnecessary, as it won't be changed.that being said, you basically repeated what Babel does when targeting ES5 to indeed hints and represent ES2015 classes as dictated by specs ... if you taint functions in the wild your code will just break, if you use my suggested function it will work even with transpiled classes so ... I am not sure where you are going with this argument 🤷