TypeScript是包含类型的ES6 Javascript的超集.可以使用class关键字声明一个类,并使用关键字实例化new它们,就像它们在Java中一样.
我想知道在TypeScript中是否有任何用例可以在不使用new关键字的情况下实例化类.
我问的原因是因为我想知道是否假设我有一个被调用的类Bob,我可以假设任何实例都Bob被实例化了new Bob().
Typescript默认保护这一点,所以如果你这样做:
class A {}
let a = A();
Run Code Online (Sandbox Code Playgroud)
你会收到一个错误:
类型的
typeof A值不可调用.你的意思是包括'新'吗?
但是,有些对象可以在不使用new关键字的情况下创建,基本上都是本机类型.
如果查看lib.d.ts,您可以看到不同构造函数的签名,例如:
interface StringConstructor {
new (value?: any): String;
(value?: any): string;
...
}
Run Code Online (Sandbox Code Playgroud)
interface ArrayConstructor {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
...
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的那样,无论有没有new关键字,总会有相同的ctors .
如果你愿意,你当然可以模仿这种行为.
重要的是要理解的是,虽然打字稿检查以确保不会发生这种情况,但javascript不会检查,因此如果有人编写将使用您的代码的js代码,他可能会忘记使用new,所以这种情况仍有可能.
很容易检测到这是否在运行时发生,然后按照您认为合适的方式处理它(抛出错误,通过返回实例new并使用它来修复它).
这是一个讨论它的帖子:创建没有新(普通js)的实例,但是tl; dr是:
class A {
constructor() {
if (!(this instanceof A)) {
// throw new Error("A was instantiated without using the 'new' keyword");
// console.log("A was instantiated without using the 'new' keyword");
return new A();
}
}
}
let a1 = new A(); // A {}
let a2 = (A as any)(); // A {}
Run Code Online (Sandbox Code Playgroud)
(游乐场代码)
据我所知,不可能让编译器理解A可以在没有new关键字的情况下调用它而不进行调用.
我们可以做得比把它投射到更好any:
interface AConstructor {
new(): A;
(): A;
}
let a2 = (A as AConstructor)(); // A {}
Run Code Online (Sandbox Code Playgroud)
我们无法做到(即)Arrayin中所做的诀窍的原因lib.d.ts:
interface Array<T> {
...
}
interface ArrayConstructor {
...
}
declare const Array: ArrayConstructor;
Run Code Online (Sandbox Code Playgroud)
在这里,他们使用Array一次作为一个类型,一次作为一个值,但一个类是一个类型和一个值,所以试图做这个技巧将结束:
重复的标识符'A'
这是相当棘手的,但可以通过多种方式完成,具体取决于您希望它与像Array这样工作的内置构造函数的行为有多接近。
这不是 TypeScript 特有的,这是 JavaScript 的问题。
class在 JavaScript 中,如果没有关键字,则无法调用使用关键字创建的构造函数new:
> class A {}
undefined
> new A()
A {}
> A()
TypeError: Class constructor A cannot be invoked without 'new'
Run Code Online (Sandbox Code Playgroud)
就像箭头函数不能用以下方式调用一样new:
> B = () => {}
[Function: B]
> B()
undefined
> new B()
TypeError: B is not a constructor
Run Code Online (Sandbox Code Playgroud)
只有使用关键字创建的函数才function可以使用和不使用 来调用new:
> function C() {}
undefined
> C()
undefined
> new C()
C {}
Run Code Online (Sandbox Code Playgroud)
(有趣的是,如果你将箭头函数和class关键字构造函数都转译为早于 ES6 的 JS A(),那么 allB()和C()以上都将在有或没有的情况下工作new,因为它们都会被转译为带有function关键字的旧式函数,并且即使在当前的发动机。)
一旦克服了调用构造函数时出错的问题,您需要确保构造函数实际上获得了一个新对象。
在 JavaScript(和 TypeScript)中,new关键字创建一个新对象并将其绑定到this构造函数中,然后返回它。所以如果你有一个函数:
> class A {}
undefined
> new A()
A {}
> A()
TypeError: Class constructor A cannot be invoked without 'new'
Run Code Online (Sandbox Code Playgroud)
那么如果你调用它,new f()它将打印并清空对象并返回它。
如果您将其称为f()“没有” new,那么它将打印全局对象(window在浏览器中或global in Node or自我in web worker - see my module on npm [the-global-object](https://www.npmjs.com/package/the-global-object) for more info) and it will return未定义)。
这个问题是 TypeScript 特有的。您需要确保所有类型都按预期工作并且以有用的方式工作。将所有内容声明为很容易any,但随后您将丢失编辑器中的所有提示,并且 TypeScript 编译器在编译期间不会检测到类型错误。
这个问题也不是 TypeScript 特有的,而是 JavaScript 普遍存在的。您希望一切都按预期工作 - 使用旧式函数和显式原型的继承以及使用class和extends关键字进行继承以及更多。
换句话说,该对象应该与使用其他对象声明class和实例化的对象一样工作,new没有任何花哨的东西。
我的经验法则:如果您可以使用内置函数Array(例如,可以使用或不使用new)来执行某些操作,那么您也应该使用我们的构造函数来执行此操作。
再次普遍适用于 JavaScript。您想要的不仅是在调用时获得一个像您想要的那样工作的对象,A()而且new您实际上希望按x instanceof A预期工作,console.log()当您想要打印对象时,您想写出正确的名称等。
这可能不是每个人的问题,但需要考虑。
function它应该支持class语法,而不是回到function构造函数和原型,否则你将失去许多有用的 TypeScript 功能。
这与上面的问题 #6有关- 如果转译目标是 ES6 之前的版本,那么结果将使用function不会给出错误的旧式构造函数:
TypeError: Class constructor A cannot be invoked without 'new'
Run Code Online (Sandbox Code Playgroud)
(参见上面的问题#1)
这对您来说可能是问题,也可能不是问题。如果您无论如何都在为遗留引擎进行转译,那么您不会看到这个问题,但是当您更改转译目标时(例如,为了避免async/ awaitpolyfills 等的高运行时成本),那么您的代码将会崩溃。如果它是库代码,那么它并不适合所有人。如果它仅供您自己使用,那么至少请记住这一点。
以下是我前段时间思考这个问题时想到的一些解决方案。我对它们并不是 100% 满意,我想避免使用代理,但这些是目前我发现的唯一可以解决上述所有问题的解决方案。
我的第一次尝试之一(对于更一般的类型,请参阅后面的示例):
> B = () => {}
[Function: B]
> B()
undefined
> new B()
TypeError: B is not a constructor
Run Code Online (Sandbox Code Playgroud)
这样做的一个缺点是,虽然构造函数名称没问题并且打印得很好,但 constructor 属性本身指向作为nonew()函数参数的原始构造函数,而不是函数返回的内容(这可能是也可能不是问题,具体取决于关于你如何掠夺它)。
另一个缺点是需要声明接口来公开类型。
另一个解决方案:
> function C() {}
undefined
> C()
undefined
> new C()
C {}
Run Code Online (Sandbox Code Playgroud)
在这里,您不需要在冗余接口中复制类型定义,而是获得带有$前缀的原始构造函数。在这里,您还可以获得继承和instanceof工作,构造函数名称和打印都可以,但 constructor 属性指向$- 前缀的构造函数。
另一种方法:
function f() {
console.log(this);
}
Run Code Online (Sandbox Code Playgroud)
该解决方案的 constructor 实例属性指向公开的(不是$- 前缀的构造函数),但使属性 constructor 返回- but for所以这应该不是问题。truehasOwnProperty()falsepropertyIsEnumerable()
我把我所有的尝试和更多解释放在 GitHub 上:
我对他们中的任何一个都不是完全满意,但他们都在做自己的事情。
另请参阅我对Call constructor on TypeScript class without new 的回答
| 归档时间: |
|
| 查看次数: |
4142 次 |
| 最近记录: |