why they behave differently?
They do not behave differently. The two declarations are essentially equivalent. The differences are not worth considering for these two pieces of code.
class HelloClass {
hi() {
return 'hi'
}
}
var HelloFunction = /** @class */ (function () {
function Hello() {
}
Hello.prototype.hi = function () {
return 'hi';
};
return Hello;
}());
const hello1 = new HelloClass()
var hello2 = new HelloFunction();
const proto1 = Object.getPrototypeOf(hello1);
const proto2 = Object.getPrototypeOf(hello2);
console.log("hi" in proto1);
console.log("hi" in proto2);
console.log(proto1.hasOwnProperty("hi"));
console.log(proto2.hasOwnProperty("hi"));
console.log(hello1.hi());
console.log(hello2.hi());
The only reason the class prototype will not show the hi
method is because class methods are not set as enumerable:
class HelloClass {
hi() {
return 'hi'
}
}
var HelloFunction = /** @class */ (function () {
function Hello() {
}
Hello.prototype.hi = function () {
return 'hi';
};
return Hello;
}());
const hello1 = new HelloClass()
var hello2 = new HelloFunction();
const proto1 = Object.getPrototypeOf(hello1);
const proto2 = Object.getPrototypeOf(hello2);
const hi1 = Object.getOwnPropertyDescriptor(proto1, "hi");
const hi2 = Object.getOwnPropertyDescriptor(proto2, "hi");
console.log(hi1);
console.log(hi2);
console.log(proto1);
console.log(proto2);
Which should make no actual difference in practice. Especially in TypeScript where it would not be type-safe to do something like for(const prop in obj)
while in JavaScript this is almost entirely useless to invoke on a class instance. It would probably be classified as "uncommon practice", yet I have yet to see it practised. There is probably some code somewhere that tries it hence the "uncommon" part.
For "fuller equivalency" the ES5 code can be changed to use Object.defineProperty
:
class HelloClass {
hi() {
return 'hi'
}
}
var HelloFunction = /** @class */ (function () {
function Hello() {
}
Object.defineProperty(Hello.prototype, "hi", {
value: function hi() { //name the function
return 'hi';
},
enumerable: false, //make it non-enumerable
writable: true,
configurable: true
});
return Hello;
}());
const hello1 = new HelloClass()
var hello2 = new HelloFunction();
const proto1 = Object.getPrototypeOf(hello1);
const proto2 = Object.getPrototypeOf(hello2);
const hi1 = Object.getOwnPropertyDescriptor(proto1, "hi");
const hi2 = Object.getOwnPropertyDescriptor(proto2, "hi");
console.log(hi1);
console.log(hi2);
console.log(proto1);
console.log(proto2);
However, chances of this having any impact is minimal. Again, especially in the context of TypeScript where the compiler will keep you from trying to do type-unsafe property enumeration of class instances:
class Hello {
hi() {
return 'hi'
}
}
const hello = new Hello()
for (const prop in hello) {
hello[prop]; //compilation error for indexing with string property
}
Playground Link
By the time you go around this compilation error and manage to produce code which might be influenced by whether a method is enumerable or not, you would actually end up with code you should do use anyway as there are better alternatives.