JavaScript's class syntax, added in ES6, apparently makes it legal to extend null:
class foo extends null {}
Some Googling reveals that it was suggested on ES Discuss that such declarations be made an error; however, other commenters argued for them to be left legal on the basis that
someone might want to create a class that has a {__proto__: null} prototype
and that side of the argument ultimately prevailed.
I can't make much sense of this hypothetical use case. For one thing, while the declaration of such a class is legal, it seems that instantiating a class declared in this way isn't. Trying to instantiate the class foo from above in Node.js or Chrome gives me the wonderfully daft error
TypeError: function is not a function
while doing the same in Firefox gives me
TypeError: function () {
} is not a constructor
It doesn't help to define a constructor on the class, as shown in MDN's current example of the feature; if I try to instantiate this class:
class bar extends null {
constructor(){}
}
then Chrome/Node tell me:
ReferenceError: this is not defined
and Firefox tells me:
ReferenceError: |this| used uninitialized in bar class constructor
What is all this madness? Why are these null-extending classes not instantiable? And given that they're not instantiable, why was the possibility of creating them deliberately left in the spec, and why did some MDN author think that it was noteworthy enough to document? What possible use case is there for this feature?
EDIT (2021): TC39, which specifies JavaScript still hasn't resolved exactly how this is supposed to work. That needs to happen before browsers can consistently implement it. You can follow the latest efforts here.
Original answer:
Instantiating such classes is meant to work; Chrome and Firefox just have bugs. Here's Chrome's, here's Firefox's. It works fine in Safari (at least on master).
There used to be a bug in the spec which made them impossible to instantiate, but it's been fixed for a while. (There's still a related one, but that's not what you're seeing.)
The use case is roughly the same as that of Object.create(null). Sometimes you want something which doesn't inherit from Object.prototype.
To answer the second part:
I can't make much sense of this hypothetical use case.
That way, your object won't have Object.prototype in its prototype chain.
class Hash extends null {}
var o = {};
var hash = new Hash;
o["foo"] = 5;
hash["foo"] = 5;
// both are used as hash maps (or use `Map`).
hash["toString"]; // undefined
o["toString"]; // function
As we know, undefined in fact is not a function. In this case we can create objects without fearing a call on a field that shouldn't be there.
This is a common pattern through Object.create(null) and is common in a lot of code bases including big ones like NodeJS.
It is in fact possible to construct a class that extends null, as such
class bar extends null{
constructor(){
super() //required
}
}
//super is not a function
//change the super class (for static methods only)
Object.setPrototypeOf(bar, class{})
let instance = new bar()
//instances still have null prototype
console.log(instance.toString) //undefined
Related
I'm looking for a way to implement an abstract static method in TypeScript.
Here an example:
abstract class A {
abstract static b (): any // error
}
class B extends A {
static b () {}
}
This is allowed with public, private and protected methods but not with a static method. TypeScript returns the following error:
'static' modifier cannot be used with 'abstract' modifier.ts(1243)
Is there any workaround?
I think the reasoning is generally as follows:
Abstract methods are placeholders that a concrete subclass is supposed to implement.
The abstract base class will have a concrete method which uses this abstract placeholder, and thus requires it to be implemented; without this, abstract methods make little sense at all. E.g.:
abstract class Foo {
bar() {
return this.baz() + 1;
}
abstract baz(): int;
}
This can only be called on a concrete instance, e.g.:
function (foo: Foo) {
let val = foo.bar();
...
}
The result will be different here depending on what concrete subclass of Foo you get, but it's all guaranteed to work.
Now, what would this look like with static methods?
abstract class Foo {
static bar() {
return this.baz() + 1;
}
abstract static baz(): int;
}
function (F: typeof Foo) {
let val = F.bar();
...
}
This looks somewhat logical and like it should work, shouldn't it? But:
If you write this, there's basically no difference to passing instances of classes, except for the awkward typeof typing. So, why?
If you never instantiate the class, what's the point of a class?
If your static method does instantiate an object, if it acts as an alternative constructor—which is entirely legitimate—then calling F.bar() to get a new instance, you'd expect to get an instance of F, but you might be getting an instance of some subclass of it. And that's arguably not desired.
To rephrase the chain of argument here:
If you're going to use static methods at all, they should act as alternative constructors and return an instance, otherwise they have little business being part of the class.
Alternative constructors should return an instance of the class that you called them on. Calling F.bar() and not getting an instance of exactly F is… weird?
If you're going to call static alternative constructors on classes, you're going to want to call them on specific classes and not variables as shown above, because otherwise you really won't know what you're getting (see point above).
Therefore, there's no real use case for an abstract static method, either as direct alternative constructor nor as helper function for one, since that would lead to a violation of one of the above points one way or another.
As far as I see, this is pretty much the classical thinking that lead to abstract static methods not being a thing. Certain technical limitations in Java may have contributed to that and/or lead to said technical limitations in the first place.
In Javascript one can argue that this makes less sense, since even classes are objects and it would be entirely feasible to use them as described above, and in certain situations it may even make sense. But here we are.
How can I get name of object's class? I mean "Process" in this example
I see two ways to get it. First one is to write a getter in this class like
getClassName(){return "Process"}
But I suppose it will be an error if I try to call this method in object which doesn't belong to this class and hasn't got method like this.
And second one is using object instanceof Process. But maybe there is some way to make it better and more correctly?
You can get it from name on constructor:
console.log(object.constructor.name);
When you do ex = new Example, for instance, in the normal course of things that makes Example.prototype the prototype of the object that was created (ex), and the object inherits a constructor property from that object that refers back to the constructor (Example).
I say "in the normal course of things" because there are various ways those normal relationships can be changed. For instance, code could have overridden the constructor property with an own property on the object (ex.constructor = somethingElse;). To rule out that specific scenario, you could use:
console.log(Object.getPrototypeOf(object).constructor.name);
Live example:
class Example1 {
}
const e1 = new Example1();
console.log(e1.constructor.name); // "Example1"
class Example2 {
constructor() {
this.constructor = "I'm special";
}
}
const e2 = new Example2();
console.log(Object.getPrototypeOf(e2).constructor.name); // "Example2"
The TC39 committee members that specify JavaScript were happy enough to use the instance's constructor property in Promises when building the new promise that then and catch return (see Step 3 here which goes here and reads constructor from the instance) (and in some other places), so you wouldn't be out on your own if you also used it. They don't even go to the prototype of the instance.
But yes, just for completeness, even if you go to the prototype for it, it's still possible for that to lead you astray, since the prototype's constructor property can also be mucked with:
class Example {
}
Example.prototype.constructor = Object; // Why would anyone do this? People are weird.
const e = new Example();
console.log(Object.getPrototypeOf(e).constructor.name); // "Object"
It's also possible to redefine the name on a function:
class Example {
}
// Why would someone do this? People are weird.
Object.defineProperty(Example, "name", {
value: "flibberdeegibbit"
});
const e = new Example();
console.log(Object.getPrototypeOf(e).constructor.name); // "flibberdeegibbit"
So...caveat user.
Note that the function name property is new as of ES2015 (as is class syntax). If you're using class syntax via a transpiler, it may or may not set name correctly.
Generally object instanceof Process is desirable if it's known for sure that object originates from this class/function. There may be situations where this won't be so. The appearance of several Process can be caused by iframes, multiple package versions, etc.
There is name property that already exists in regular functions class constructors. A known pitfall is that it will be mangled in minified code, so it is generally useless in browser JS, and its use can be considered an antipattern. name cannot be reassigned (in some browsers), so a separate property is needed to identify the class.
The proper way is to avoid this problem
But I suppose it will be an error if I try to call this method in object which doesn't belong to this class and hasn't got method like this.
is to use a getter:
class Process {
get className() { return 'Process'; }
...
}
Or a property:
class Process {
...
}
Process.prototype.className = 'Process';
As a result, there may be several Process classes that have Process className identifier. This may be desirable or not. While instanceof associates class instance with one particular class.
Use .constructor.name on the object. Each object's constructor by default refers to his creation function, which has a name property. It returns the name of the function.
class SomeClass {
}
const obj = new SomeClass();
console.log(obj.constructor.name);
Use the name property as follows:
class Process {}
console.log(Process.name);
const process = new Process;
console.log(process.constructor.name);
This is the same way it works for normal prototypal inheritance using functions.
Given the class is extended from non-class (including, but not limited to, function),
function Fn() {}
class Class extends Fn {
constructor() {
super();
}
}
what are the the consequences? What do the specs say on that?
It looks like the current implementations of Babel, Google V8 and Mozilla Spidermonkey are ok with that, and TypeScript throws
Type '() => void' is not a constructor function type
If this is a valid ES2015 code, what's the proper way to handle it in TypeScript?
TypeScript Part
Up to now, the spec says a extends claus must be followed by a TypeReference. And a TypeReference must be in the form of A.B.C<TypeArgument>, like MyModule.MyContainer<MyItem>. So, syntactically your code is right. But it is not the typing case.
The spec says the BaseClass must be a valid typescript class. However, the spec is outdated, as said here. Now TypeScript allows expressions in extends clause, as long as expressions are computed to a constructor function. The definition is, well, implementation based. You can see it here. Simply put, a expression can be counted as constructor if it implements new() {} interface.
ES2015 Part
So, your problem is plain function is not recognized as constructor in TypeScript, which is arguable because ES2015 spec only requires the object has a [[construct]] internal method. While user-defined function object does have it.
ES2015 requires BaseClass is a constructor at runtime. An object isConstructor if it has [[construct]] internal methd. The spec says [[construct]] is an internal method for Function Object. User functions are instances of Function Objects, so naturally they are constructor. But builtin function and arrow function can have no [[construct]].
For example, the following code will throw runtime TypeError because parseInt is a builtin function and does not have [[construct]]
new parseInt()
// TypeError: parseInt is not a constructor
And from ECMAScript
Arrow functions are like built-in functions in that both lack .prototype and any [[Construct]] internal method. So new (() => {}) throws a TypeError but otherwise arrows are like functions:
As a rule of thumb, any function without prototype is not new-able.
Work Around
In short, not every function is constructor, TypeScript captures this by requiring new() {}. However, user-defined function is constructor.
To work around this, the easiest way is declare Fn as a variable, and cast it into constructor.
interface FnType {}
var Fn: {new(): FnType} = (function() {}) as any
class B extends Fn {}
Reasoning the incompatiblity
DISCALIMER: I'm not a TypeScript core contributor, but just a TS fan who has several side project related to TS. So this section is my personal guess.
TypeScript is a project originated in 2012, when ES2015 was still looming in dim dark. TypeScript didn't have a good reference for class semantics.
Back then, the main goal of TypeScript was to keep compatible with ES3/5. So, newing a function is legal in TypeScript, because it is also legal in ES3/5. At the same time, TypeScript also aims to capture programming errors. extends a function might be an error because the function might not be a sensible constructor (say, a function solely for side effect). extends did not even exist in ES3/5! So TypeScript could freely define its own usage of extends, making extends must pair with class variable. This made TypeScript more TypeSafe, while being compatible with JavaScript.
Now, ES2015 spec is finalized. JavaScript also has a extends keyword! Then incompatibility comes. There are efforts to resolve incompatibility. However, still problems exist. () => void or Function type should not be extendsable, as stated above due to builtin function. The following code will break
var a: (x: string) => void = eval
new a('booom')
On the other hand, if a ConstructorInterface was introduced into TypeScript and every function literal implemented it, then backward incompatibility would emerge. The following code compiles now but not when ConstructorInterface was introduced
var a = function (s) {}
a = parseInt // compile error because parseInt is not assignable to constructor
Of course, TS team can have a solution that balancing these two options. But this is not a high priority. Also, if salsa, the codename for TS powered JavaScript, is fully implemented. This problem will be solved naturally.
what are the the consequences? What do the specs say on that?
It is the same as extending a "EcmaScript 5" class. Your declare a constructor function and no prototype at all. You can extend it without any problem.
But for TypeScript, there is a big difference between function Fn() {} and class Fn {}. The both are not of the same type.
The first one is a just a function returning nothing (and TypeScript show it with the () => void). The second one is a constructor function. TypeScript refuse to do an extends on a non constructor function.
If one day javascript refuse to do that, it will break many javascript codes. Because at the moment, function Fn() {} is the most used way to declare a class in pure javascript. But from the TypeScript point of view, this is not "type safe".
I think the only way for TypeScript is to use a class :
class Fn {}
class Class extends Fn {
constructor() {
super();
}
}
I'm not sure I answer your question but I was very interested how to extend a JS function by a TypeScript class so I tried following:
fn.js (note the .js extension!)
function Fn() {
console.log("2");
}
c.ts
declare class Fn {}
class C extends Fn {
constructor() {
console.log("1");
super();
console.log("3");
}
}
let c = new C();
c.sayHello();
Then I ran:
$ tsc --target es5 c.ts | cat fn.js c.js | node # or es6
and the output is:
1
2
3
Hello!
Note, that this is not code for production use but rather a workaround for cases when you don't have time to convert some old JS file to TypeScript.
If I was in OP situation, I would try to convert Fn to a class because it makes code easier for others in a team.
This is indirectly related to TypeScript interface to describe class
Although the typeof a class is function, there is no Class (nor Interface) in Typescript which is the super class of all classes.
functions however are all interfaced by Function
I guess to be consistent with Javascript, Function should be probably be a class, all functions should be of that class and all classes should extend it...
Let's take the following example code:
var ns = {}; // Some namespace
ns.Test = function()
{
// Constructor of class Test
};
var inst = new ns.Test();
var className = hereIsTheMagic(inst); // Must return "ns.Test"
So I create a class named 'Test' in namespace 'ns' and an instance of this class named 'inst'. Now I want to find out the class name. How can I do this?
Up to now I solved this problem by giving each class a string property with the class name so I could use inst.constructor.className to access the class name. But if possible I would like to stop doing this because it is pretty error-prone when copy/pasting classes.
If there is no solution which works in all current browsers maybe there is at least some new feature in some future ECMAScript spec which provides access to class names?
JavaScript doesn't have classes, it has constructor functions and the prototype chain. Together, they can look a bit like classes, but they really aren't. :-)
No, there's no standard way that hereIsTheMagic can find the string "ns.Test" in your example, not even in ECMAScript 5. For one thing, ns.Test may not be the only property referring to that function (what if you added ns.OtherTest = ns.Test;, for instance).
JavaScript does have the concept of functions having proper names, but there's no standard way to access the proper name of a function, and even if you could, the function you're calling ns.Test is anonymous. The property you've assigned it to on ns has a name (Test), but the function does not.
You could give the function a name:
var ns = {}; // Some namespace
(function() {
ns.Test = ns_Test;
function ns_Test() {
// Constructor of class Test
}
})();
...which is a good idea, because it helps tools help you, but there's no standard way for JavaScript code itself to get at that name. (Don't do ns.Test = function ns_Test() { ... };, though. It should work, but there are various implementations of ECMAScript that have various bugs related to it. More about that in the article linked above.)
Update: I probably should have said: There are non-standard ways to get at the proper name of a function (ns_Test in my example above).
The easiest of these is the name property on function instances, which is supported by many implementations, including:
Firefox's SpiderMonkey
Chrome's V8
Opera's engine (I don't know its name)
Safari's engine (including Safari mobile)
...so alert(ns_Test.name); would alert "ns_Test". But it is non-standard and not supported by any version of JScript (Microsoft's engine), not even the version used by IE9 (now finally released!). Here's a quick tester page: http://jsbin.com/oxuva3
Note that, again that's the proper name of the function (see my example above), not the name of the property you've assigned it to.
If name isn't there, you can use toString() on the function object, which may (or may not) return a string representation of the function (e.g., "function ns_Test() { ... }"). That's completely non-standard, but common on desktop browsers; it doesn't work on several mobile browsers, and probably a bad idea in any case. :-)
I see a lot of code like this:
function Base() {}
function Sub() {}
Sub.prototype = new Base();
However, if you do:
s = new Sub();
print(s.constructor == Sub);
This is false. This seems confusing to me, since s's constructor is, indeed, Sub. Is it conventional/better to do this?
function Base() {}
function Sub() {}
Sub.prototype = new Base();
Sub.prototype.constructor = Sub;
or does it not really matter?
'constructor' doesn't do what it looks like it does. This, in addition to its non-standardness, is a good reason to avoid using it - stick with instanceof and prototype.
Technically: 'constructor' is not a property of the 's' instance, it is a property of the 'Sub' prototype object showing through. When you create the 'Sub' function in Mozilla, you get a newly-minted default Sub.prototype object which has a 'constructor' pointing back to the Sub function as a courtesy.
However you then replace that prototype with a new Base(). The original default prototype with the link back to Sub is lost; instead, Sub.prototype is an instance of Base without any overriding 'constructor' property. So:
new Sub().constructor===
Sub.prototype.constructor===
new Base().constructor===
Base.prototype.constructor===
Base
...all the way down to the most basic object whose prototype you didn't change.
Is it conventional/better to do this?
When dealing with JavaScript objects/classes there is no one convention; every library's metaclass system behaves slightly differently. I haven't seen one that writes 'constructor' to each derived class manually, but it seems as good a solution as any if you really want to have the real constructor available; it will also make the code compatible with browsers/engines that don't give you 'constructor'.
I'd consider giving it a different name, though, to avoid confusion with the existing and differently-behaving 'constructor' property.
If you want to test whether an object is exactly an instance of Sub use the instanceof operator:-
print(s instanceof Sub);
If you want to know whether an object is an instance of Sub or an instance of a sub-class of Sub use the isPrototypeOf method:-
print(Sub.prototype.isPrototypeOf(s));
Yeah,
Sub.prototype.constructor = Sub;
let's you use instanceof but there's a better solution. Look here: ,TDD JS Inheritance on GitHub ,and find the Parasitic Combination Inheritance pattern. The code is TDD'd so you should be able to grok it very quickly and then simply change the names to get you started. This is basically what YAHOO.lang.extend uses (source: yahoo employee and author Nicholas Zakas's Professional JavaScript for Web Developer's, 2nd ED, page 181). Good book by the way (not affiliated in any way!)
Why? Because the classical pattern you're working with has staticy reference vars (if you create var arr = [1,2] in the base object, ALL instances will have read/write and will "share state" of 'arr'! If you use constructor stealing you can get around this. See my examples.