I'm fairly new to javascript and now I learned how calling functions with a context works.
Here is a simple example that poses a question in my head. Lets say we have this example:
var myObj = {
bar: function() {
console.log("Lets got to the bar!");
}
}
/* how can this be protected from errors,
* if a passed object doesn't contain bar */
function foo()
{
this.bar();
}
foo.call(myObj);
Now, how can foo be protected of breaking? In some OOP language (lets say Java) this would be implemented lets say via an interface. So in that case if the object being instantiated hasn't implemented the interface method, the compiler would through an error so the compiler protects the code/program from being faulty (in this case of course).
public interface MyInterface
{
public void bar();
}
public class MyClass implements MyInterface
{
public void bar()
{
System.println("Lets go to the bar");
}
}
MyInterface m = new MyClass();
m.bar(); // if bar isn't implemented the compiler would warn/break
Note: I'm not that good in Java so sorry for any syntax or other errors, but I hope you get the point.
So to sum up, as I see that in both cases in both languages one can achieve polymorphism, right? Now if so for the Javascript example, how can one protect it from breaking, are there any patterns or tricks? Does typeof this.bar === function work? If so, who guarantees the SW quality if the programmer forgets this, I'm asking this kind of question because Java has the compiler to warn the programmer about the mistake, does JS have something similar, some quality check tool?
Javascript is a dynamic interpeted* language. There isn't a compiler step to check references. Some tools (jsline) and IDEs (VS, Webstorm) can perform some design-time checks for you, but there's no true type safety. This is largely seen as a feature, not a bug.
There's an array of tricks to work around this (.hasOwnProperty, typeof x === 'function', storing self references, context binding) but mostly, if you want a type safety, you want a different language.
My recommendation is Typescript. It has a Java/C-like syntax, with some familiar OOP features, like classes, interface (and thus, sane polymorphism) and generic types, and transpiles to javascript in moments.
If you use a constructor to create your object you can use Javascript's builtin class member ship checking features. An example is below.
class MyClass {
bar() { console.log("Lets got to the bar!")}
}
function foo() {
if ( this instanceof MyClass ) {
this.bar();
}
else {
console.log('this is not a member of the MyClass');
}
}
foo.call(new MyClass);
Be warned that Javascript's type checking is horribly unreliable and you probably should not use it. If your object contains the same prototype anywhere in it's prototype chain as the class you are testing it for membership in, instanceof will return true.
Quick and dirty duck typing example
This will throw if you give it an object without the properties you are checking for, but you get the idea.
class MyClass {
constructor() {
this.feathers = 'white';
this.feet = 'webbed';
}
bar() { console.log("Lets got to the bar!")}
}
function foo() {
if (
this.hasOwnProperty('feathers') &&
this.hasOwnProperty('feet') &&
this.feathers === 'white' &&
this.feet === 'webbed'
)
{
this.bar();
}
else {
console.log('this is not a member of the MyClass');
}
}
foo.call(new MyClass);
Related
I know that the super keyword can be used to reference inherited properties and methods in a class. It can also be used in static methods and call super.staticMethod(). Is it okay to use the super keyword on static properties? Rollup says that it isn't okay, and I came across this problem while compiling ESModules to a CommonJs file.
Rollup is fine with this
class A {
static m() {
return 1;
}
}
class B extends A {
static m() {
return super.m() + 1;
}
}
console.log("A", A.m());
console.log("B", B.m());
Here there are static methods on classes A and B. B calls super.m(), which is like calling A.m().
Rollup is NOT fine with this
class A {
static score = 5;
static info = {
prop1: "hi",
prop2: "hello"
}
}
class B extends A {
static score = super.score + 5;
static info = {
...super.info,
prop3: "welcome",
prop4: "good morning"
}
}
console.log("A", A.score, A.info);
console.log("B", B.score, B.info);
Still, I don't see what's wrong with the second example. When I run it, it works without any errors. Is it bad or invalid javascript, or is rollup just complaining even though it's valid javascript? When I replace super with A (for example, A.score instead of super.score), it works.
Rollup Error
[!] Error: 'super' keyword outside a method
My rollup.config.js file
export default {
input: "index.js",
output: {
file: "commonjs/index.js",
format: "cjs"
}
}
Static class fields are still an experimental proposal.
Is it okay to use the super keyword on static properties?
Yes, probably.
If I interpret the current specification draft correctly, there is no early error from using super.… or super[…] in a class field, only super(…) and arguments are syntax errors - and there are no early errors specifically for static fields either. The field initialisers are evaluated as if their code was in an anonymous method, where they have access to a receiver (this value) and home object (super base).
However, there appears to be an editorial issue about how the class value is passed to the definition algorithm, and it's not inconceivable that the decision to allow super is reevaluated. That the parser used by Rollup disagrees with Chrome here is a bug, but we don't know in which of the two implementations.
So I'm writing a compiler, and different "Statement" types have different classes. A Block Statement has a BlockStatement class, an If Statement has an IfStatement class, etc.
I need to be able to tell what type of object I'm working with at runtime, eg
class BlockStatement extends Statement {
constructor(...children: Statement[]) {
super()
this.rep.assemble(
new BlockLabel(),
children.map(child => Statement.extractRep(child))
)
}
private addToBlock(pos: number, s: Statement): void {
this.rep.addChild(pos, Statement.extractRep(s))
}
}
function prettyPrint(s: Statement) {
switch () {
case 'BlockStatement': {
}
}
}
How can I tell what type of Statement I am working with? And even if I can tell, will it not matter since it may slice any inherited functionality since the parameter is a statement?
###object###.constructor.name does the trick, however, ive heard minifying may cause issues with this. Then cast itself as whatever it is so intellisense works
I have a class with two constructors:
#JsType
public class Dog implements Animal {
String name;
public Dog() {
this.name = "Scooby Doo";
}
public Dog(String name) {
this.name = name;
}
}
I get the following error when I run gwt compile [With GWT-dev 2.8]
[ERROR] Constructor 'Dog(String)' can be a JsConstructor only if all constructors in the class are delegating to it.
I have been trying to work through this error, with not much success. I am not sure how to delegate to the other constructor.
Any help is very much appreciated!
Thanks!
Alas, JavaScript can't handle multiple constructors! You get one and only one.
There are some things you can do to pretend to have more than one - you can check the incoming data, and assign sane defaults:
#JsConstructor
public Dog(#JsOptional String name) {
if (name == null) {
name = "Scooby Doo";
}
this.name = name;
}
You have to be careful of types here if your two constructors do not use the same types in the same position - judicious use of instanceof might work (just be aware that for JS objects you are using JS instanceof, not java!). Another option lets you be more flexible, but again, no overloaded methods - factory methods:
public static Dog withName(String name) {
return new Dog(name);
}
public static Doc defaultImpl() {
return new Dog();
}
#JsIgnore
public Dog() {/*...*/}
#JsIgnore
public Dog(String name) {/*...*/}
Another idea on the same theme would be to create a JsType builder. This is a bit less idiomatic for JS.
Finally, you could consider the dreaded "options object", where your one constructor takes a single Object with properties, or a JsPropertyMap<Any> holding all the possible values, then check nullness and types inside the giant constructor. I avoid this personally - this is one of the terrible things I'm hoping to avoid by writing Java in the first place.
Having a problem with Typescript's Generics where the type is undefined in the scope of the generic function or class. I can't find any documentation on this though I would assume it is by design. Is there a way to achieve what I am trying to, type-safely?
function test<T>() {
return new T();
}
class TestClass<T> {
public build(): T {
return new T();
}
}
Link to Play:
http://www.typescriptlang.org/Playground/#src=function%20test%3CT%3E()%20%7B%0A%09return%20new%20T()%3B%0A%7D%0A%0Aclass%20TestClass%3CT%3E%20%7B%0A%09public%20build()%3A%20T%20%7B%0A%09%09return%20new%20T()%3B%0A%09%7D%0A%7D%0A
TypeScript generics (unlike other languages like C#) are compile time only. So you cannot use them in runtime positions e.g. new T.
Is there a way to achieve what I am trying to, type-safely
Pass the constructor explicitly. e.g.
class TestClass<T> {
public build(x:{new ():T}): T {
return new x();
}
}
Here x:{new ():T} I am saying that x is something that when called with new gives an instance of T.
In Javascript, in contrast to other languages which OOP like Java, do not provide interfaces. There is some solutions on the internet which are more complex than mine, but I want to share with you my way how to resolve this problem, to get some constructive critisism from you and check if I choose the right way of thinking. I choose Answer your own question – share your knowledge, Q&A-style and you will see my answer below.
Here is an example that uses a timeout to check if the needed functions are implemented, you can implement multiple Interfaces.
Since JS is not a compile time type checked language you can't really have a good solution for this. Maybe you can have a look at mix ins (under mix ins), leave the default implementation or override.
function PersonInterface(proto,fnName){
//after running the implements function it depends how quickly you're
// creating instances and how quickly you implement the functions
setTimeout(function(){
if(typeof proto.getSurName !== 'function'){
throw new Error(fnName + ' has to implement getSurName');
}
//and others if needed
},100);
}
function implements(fn, implements,fnName){
implements(fn.prototype,fnName);
}
function Employer(){};
implements(Employer, PersonInterface,'Employer');
//depends how quickly you set the getSurName
// and how quickly you will be creating Emplyer instances
// trying to call getSurName
// comment out the next line and you'll get
// Employer has to implement getSurName
Employer.prototype.getSurName=function(){};
My solution based on Javascript Prototype. I create a class which acts like interface by throwing Error object in methods which should be implemented, when child inherits from interface. Let's create PersonInterface:
function PersonInterface(){
}
PersonInterface.prototype.getName = function(){
throw typeof(Error) !== 'undefined'?
new Error(" Interface Person: method getName() unimplemented!")
: " Interface Person: method getName() unimplemented!";
};
PersonInterface.prototype.getSurname = function(){
throw typeof(Error) !== 'undefined'?
new Error(" Interface Person: method getSurname() unimplemented!")
: " Interface Person: method getSurname() unimplemented!";
};
Then let's create a Customer, which implements PersonInterface interface:
function Customer(name, surname){
PersonInterface.call(this);
this.__name = name;
this.__surname = surname;
}
Customer.prototype = Object.create(PersonInterface.prototype);
Customer.prototype.getName = function(){
return this.__name;
};
Customer.prototype.getSurname = function(){
return this.__surname;
};
When getName() or getSurname() function are not implemented, it throws an Error object with corresponding message. I create a JSFiddle which demonstrates how it works: Javascript Interface with Prototype (click)
When you, for example, remove getSurname() method from Customer's prototype, it throws an Error - which you find in your browser console. I know that is simple solution and on the net we can find many solutions, but I think it can resolve problem when we don't need something more complex than simple interface.