Xåp*_* - 25 javascript prototype class ecmascript-6
我从一个模块导出以下ES6类:
export class Thingy {
hello() {
console.log("A");
}
world() {
console.log("B");
}
}
Run Code Online (Sandbox Code Playgroud)
并从另一个模块导入它:
import {Thingy} from "thingy";
if (isClass(Thingy)) {
// Do something...
}
Run Code Online (Sandbox Code Playgroud)
如何检查变量是否为类?不是类实例,而是类声明?
换句话说,我将如何isClass在上面的例子中实现该功能?
Fel*_*ing 28
如果要确保该值不仅是一个函数,而且实际上是一个类的构造函数,您可以将该函数转换为字符串并检查其表示形式.规范规定了类构造函数的字符串表示形式.
function isClass(v) {
return typeof v === 'function' && /^\s*class\s+/.test(v.toString());
}
Run Code Online (Sandbox Code Playgroud)
另一种解决方案是尝试将值调用为正常函数.类构造函数不能作为普通函数调用,但错误消息可能因浏览器而异:
function isClass(v) {
if (typeof v !== 'function') {
return false;
}
try {
v();
return false;
} catch(error) {
if (/^Class constructor/.test(error.message)) {
return true;
}
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
缺点是调用函数可能会产生各种未知的副作用......
log*_*yth 15
我会在前面说清楚,任何任意函数都可以是构造函数.如果您区分"类"和"功能",那么您的API设计选择很差.如果你假设某些东西必须是class例如,那么使用Babel或Typescript的人将被检测为a,class因为他们的代码将被转换为函数.这意味着您要求使用您的代码库的任何人通常必须在ES6环境中运行,因此您的代码在旧环境中将无法使用.
您在此处的选项仅限于实现定义的行为.在ES6中,一旦解析了代码并处理了语法,就没有太多特定于类的行为.你所拥有的只是一个构造函数.你最好的选择就是做
if (typeof Thingy === 'function'){
// It's a function, so it definitely can't be an instance.
} else {
// It could be anything other than a constructor
}
Run Code Online (Sandbox Code Playgroud)
如果有人需要执行非构造函数,请为其公开单独的API.
显然,这不是您正在寻找的答案,但重要的是要明确这一点.
正如这里提到的另一个答案,你有一个选项,因为.toString()在函数上需要返回一个类声明,例如
class Foo {}
Foo.toString() === "class Foo {}" // true
Run Code Online (Sandbox Code Playgroud)
然而,关键是,只有在可行的情况下才适用.它是100%规范兼容的实现
class Foo{}
Foo.toString() === "throw SyntaxError();"
Run Code Online (Sandbox Code Playgroud)
目前没有任何浏览器可以做到这一点,但是有几个嵌入式系统专注于JS编程,为了保存程序本身的内存,它们一旦被解析就会丢弃源代码,这意味着它们没有源代码可以从.toString()这是允许的.
同样,通过使用,.toString()您可以对未来验证和一般API设计进行假设.说你做
const isClass = fn => /^\s*class/.test(fn.toString());
Run Code Online (Sandbox Code Playgroud)
因为这依赖于字符串表示,它很容易破坏.
以装饰者为例:
@decorator class Foo {}
Foo.toString() == ???
Run Code Online (Sandbox Code Playgroud)
请问.toString()这包括装饰?如果装饰器本身返回function而不是类,该怎么办?
Ian*_*ter 13
检查prototype及其可写性应该允许确定函数的类型,而无需字符串化、调用或实例化输入。
/**
* determine if a variable is a class definition or function (and what kind)
* @revised
*/
function isFunction(x) {
return typeof x === 'function'
? x.prototype
? Object.getOwnPropertyDescriptor(x, 'prototype').writable
? 'function'
: 'class'
: x.constructor.name === 'AsyncFunction'
? 'async'
: 'arrow'
: '';
}
console.log({
string: isFunction('foo'), // => ''
null: isFunction(null), // => ''
class: isFunction(class C {}), // => 'class'
function: isFunction(function f() {}), // => 'function'
arrow: isFunction(() => {}), // => 'arrow'
async: isFunction(async function () {}) // => 'async'
});Run Code Online (Sandbox Code Playgroud)
这是一个古老的问题,除了这个之外几乎没有答案是正确的,但有一个警告......
让我们从任何建议调用该函数的人开始......这是一种容易发生灾难的方法,应该在 ChatGPT 甚至将其视为建议代码之前从建议中删除......接下来......
在某些 JS 运行时,函数的字符串表示形式或其余代码在生产中不存在,可能在调试模式下,但不一定在产品中存在,恰恰相反。
这是因为某些 JS 运行时可以通过从几乎所有内容中删除源代码来节省大量最终“字节码”大小。
因此,建议任何字符串检查的每个人都不知道或考虑这些情况,而且任何函数toString方法也可以用其他方法替换,这使得大多数答案都不是万无一失的。
最接近的答案是检查任何函数的描述符writable:prototype
{method(){}}没有原型prototype存在且其writable值恰好是falseconst isESClass = fn => (
typeof fn === 'function' &&
Object.getOwnPropertyDescriptor(
fn,
'prototype'
)?.writable === false
);
Run Code Online (Sandbox Code Playgroud)
重要的是要明白,在陷入 ES5 转译(无论出于何种原因)的项目中,这将失败,但从根本上没有办法保证通用函数,在 ES2015 之前的世界中,是一个类,还是不是一个类,使用了jQuery(以及其他)类似以下的模式和所有情况都是允许的:
function jQuery(...args) {
if (!(this instanceof jQuery))
return new jQuery(...args);
// do everything jQuery does
}
Run Code Online (Sandbox Code Playgroud)
与 ES2015+ 类不同,该实用程序既可以作为常规函数也可以作为new function,因此基本上这个问题没有正确的答案,只是需要考虑的妥协和目标列表。
false函数和类之间存在细微的区别,我们可以利用这个优势来区分它们,以下是我的实现:
\n// is "class" or "function"?\nfunction isClass(obj) {\n\n // if not a function, return false.\n if (typeof obj !== \'function\') return false;\n\n // \xe2\xad\x90 is a function, has a `prototype`, and can\'t be deleted!\n\n // \xe2\xad\x90 although a function\'s prototype is writable (can be reassigned),\n // it\'s not configurable (can\'t update property flags), so it\n // will remain writable.\n //\n // \xe2\xad\x90 a class\'s prototype is non-writable.\n //\n // Table: property flags of function/class prototype\n // ---------------------------------\n // prototype write enum config\n // ---------------------------------\n // function v . .\n // class . . .\n // ---------------------------------\n const descriptor = Object.getOwnPropertyDescriptor(obj, \'prototype\');\n\n // \xe2\x9d\x97functions like `Promise.resolve` do have NO `prototype`.\n // (I have no idea why this is happening, sorry.)\n if (!descriptor) return false;\n\n return !descriptor.writable;\n}\nRun Code Online (Sandbox Code Playgroud)\n以下是一些测试用例:
\nclass A { }\nfunction F(name) { this.name = name; }\n\nisClass(F), // \xe2\x9d\x8c false\nisClass(3), // \xe2\x9d\x8c false\nisClass(Promise.resolve), // \xe2\x9d\x8c false\n\nisClass(A), // \xe2\x9c\x85 true\nisClass(Object), // \xe2\x9c\x85 true\nRun Code Online (Sandbox Code Playgroud)\n