Explain this confusing dojo tutorial syntax for declare - javascript

I am reading the syntax for using dojo's declare for class creation. The description is confusing:
The declare function is defined in the dojo/_base/declare module. declare accepts three arguments: className, superClass, and properties.
ClassName
The className argument represents the name of the class, including the namespace, to be created. Named classes are placed within the global scope. The className can also represent the inheritance chain via the namespace.
Named Class
// Create a new class named "mynamespace.MyClass"
declare("mynamespace.MyClass", null, {
// Custom properties and methods here
});
A class named mynamespace.MyClass is now globally available within the application.
Named classes should only be created if they will be used with the Dojo parser. All other classes should omit the className parameter.
"Anonymous" Class
// Create a scoped, anonymous class
var MyClass = declare(null, {
// Custom properties and methods here
});
The MyClass is now only available within its given scope.
SuperClass(es)
The SuperClass argument can be null, one existing class, or an array of existing classes. If a new class inherits from more than one class, the first class in the list will be the base prototype, the rest will be considered "mixins".
Class with No Inheritance
var MyClass = declare(null, {
// Custom properties and methods here
});
null signifies that this class has no classes to inherit from.
Class Inheriting from Another Class
var MySubClass = declare(MyClass, {
// MySubClass now has all of MyClass's properties and methods
// These properties and methods override parent's
});
The syntax is exactly the same for creating a non-named class and a class with no superclass:
var MyClass = declare(null, {
// Custom properties and methods here
});
I expect the syntax for a class without any super class and without any name to be like this:
var MyClass = declare(null, null, {
// Custom properties and methods here
});
I am coming from a typed language background, so perhaps I've misunderstood how this works in JavaScript. I fail to understand how someone reading the code (without any comments) would know the difference between the two, if the tutorials syntax is correct.
I would have expected the syntax to be something like this:
/*class without a name:*/ declare(null, SuperClass, {})
/*class without a name or super class:*/ declare(null, null, {})
/*class with a name but no super class:*/ declare("ClassName", null, {})
Maybe this is verbose, but at least it is easy to tell what each parameter is for.

Well, consider it an overloaded constructor:
// class with a name
declare(className: String, superClass: Array, classDeclaration: Object);
// class without a name
declare(superClass: Array, classDeclaration: Object);
Use an empty array [] or null for no superClass.
N.B.: As of Dojo 1.8, there is no need for named classes, because dojo/parser can use a module id (mid, e.g. "mynamespace/MyClass") for instantiation. I consider named classes to be obsolete and against code maintainability.

Related

Can a child class overwrite a private field inherited from a superclass?

I'm playing around with ES6 classes in JavaScript and I'm wondering if it's possible for a child class to inherit private properties/methods from a superclass, while allowing the subclass to mutate this private property without using any "set" methods (to keep it read-only).
For example, say I want to create a static private property called #className in my superclass that is read-only, meaning you can only read from it using a method called getClassName() and you cannot access this property by doing class.className. Now, I make a new child class that extends this superclass. The child class will inherit #className and getClassName(), but, I would like #className to be initialized with a different value in the child class than in the superclass. So, in the superclass you could have: #className = 'Parent' but in the child class you would have #className = 'Child'.
My only problem is, it seems like you can't really do this. If I try declaring a new #className in the child class, the child class's getClassName() method still refers to the #className from the superclass. Here's the code I was playing with:
class Parent {
#className = 'Parent' // create private className
constructor() {}
getClassName() {
return this.#className; // return className from object
}
}
class Child extends Parent {
#className = 'Child' // re-define className for this child class
constructor() { super(); } // inherit from Parent class
}
new Child().getClassName() // --> this prints 'Parent' when I want it to print 'Child'
Does anyone have a solution to this? Or an alternative that achieves a similar affect?
JavaScript does not support directly accessing private properties inherited from another class, which is how private members are supposed to work. You seem to want the functionality of protected properties. As of 2022, JavaScript does not support protected properties or members of any kind. Why that is, I can't imagine, since other OOP languages have allowed said functionality since time immemorial.
If you have control over the code of the parent class, you can simulate protected properties by using symbols.
const className = Symbol();
class Parent {
[className] = 'Parent'; // create protected [className]
getClassName() {
return this[className]; // return [className] from object
}
}
class Child extends Parent {
[className] = 'Child'; // re-define [className] for this child class
}
console.log(new Child().getClassName()); // --> this prints 'Child'
I'm not sure why this snippet fails in the preview even with Babel. That exact code appears works in the console of every major browser I've tried.
The reason this works is that a Symbol in JavaScript is a primitive type that's guaranteed to be unique. Unlike other primitives, when used as a key in an object, it cannot be [easily] accessed or iterated over, effectively making it protected.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
Whether a property is truly "protected" is primarily determined by the scope within which you define the symbol (or whether you pass it around).
For instance, if the above code is module-scoped then that symbol will only be accessible to anything that's within that scope. As with any variable in JavaScript, you can scope a symbol within a function definition, or an if-statement, or any block. It's up to you.
There are a few cases where those symbols can be accessed. This includes the use of Object.getOwnPropertySymbols, Object.getOwnPropertyDescriptors, to name a few. Thus, it's not a perfect system, but it's a step above the old-school way of creating "private" members in JavaScript by prefixing names with an underscore.
In my own work, I use this technique to avoid using private class member syntax because of the gotcha you describe. But that's because I have never cared about preventing that capability in subclasses. The only drawback to using symbols is it requires a bit more code. Since a symbol is still a value that can be passed around, that makes it possible to create "loopholes" in your code for testing purposes or the occasional edge-case.
To truly eliminate leakage of conceptually protected properties, a WeakMap within the scope of the class definitions can be used.
const protectedClassNames = new WeakMap();
class Parent {
constructor() {
protectedClassNames.set(this, 'Parent');
}
getClassName() {
return protectedClassNames.get(this);
}
}
class Child extends Parent {
constructor() {
super();
protectedClassNames.set(this, 'Child'); // re-define className for this child class
}
}
console.log(new Child().getClassName()); // --> this prints 'Child'
A WeakMap is a key-value store that takes an object as a key and will dereference the object when said object has been garbage collected.
As long as the protectedClassNames WeakMap is only scoped to the class definitions that need it, then its values won't be possibly leaked elsewhere. However, the downside is that you run into a similar problem to the original issue if a class that's out of scope of the weak map tries to inherit from one of the classes that uses it.
WeakMaps aren't strictly necessary for this, but its function is important for managing memory.
Unfortunately, there appears to be no proposal in progress for adding protected members to the JavaScript standard. However, decorator syntax combined with either of the approaches described here may be a convenient way of implementing protected members for those willing to use a JavaScript transpiler.

Prototype property inside class constructor [duplicate]

I was trying ES6 syntax and find I cannot define prototype property or instance property within class defination, why forbids it?
I was using MyClass.prototype.prop=1 before, try ES7 by babel compiler as below, still cannot define prototype property.
class MyClass{
prop=1;
static sProp=1;
}
I don't think define instance property is any dangerous, there's 2 cases in my own browser game need prototype property:
Subclass instances need to inherit same property value from base class:
var Building=function(){...}
Building.prototype.sight=350;
TerranBuilding.CommandCenter=...(CommandCenter extends Building)
TerranBuilding.Barracks=...(Barracks extends Building)
So CommandCenter and Barracks will both have same building sight as 350.
new CommandCenter().sight===new Barracks().sight//All buildings have same sight
Buffer effect override original property and remove buffer
Marine.prototype.speed=20
var unit=new Marine()
unit.speed===20//get unit.__proto__.speed 20
unit.speed=5//Buffer:slow down speed, unit.speed will override unit.__proto__.speed
delete unit.speed//Remove buffer
unit.speed===20//true, speed restore
So I think it should add a way to set prototype property instead of forbid it completely, or can you give some other solutions to deal with above 2 cases?
Updated Answer (April, 2022)
Just two months after my previous answer, in August of 2021, the static block proposal was moved to stage 4 by the TC-39 committee. See the whole informal list of finished proposals here.
For those looking to get a use case summary of static blocks in Javascript, read the initial publication from the V8 blog from March 2021, after their implementation.
Also, see the MDN documentation for static initialization blocks.
Though most all updated browsers now support this, read below if you really like to support Internet Explorer.
Original Answer
Below is the typical pattern I follow in javascript. Native, no babel, etc..
It mirrors the static-block style that java uses. There is a Stage 3 Proposal open for this right now, and I expect therefor, that it will be standardized in the near future (as is consistent with the stage 3 proposal expectations of the TC-39 committee).
What the proposal will look like
class MyClass {
static {
// Any code here is executed directly after the initialization
// of MyClass. You can add prototype stuff here. The function
// is called bound to `MyClass`.
}
}
This can be done today using a static iife
These will function exactly the same way.
class MyClass {
// Using private properties is not required, it is just an option. Make
// sure to use an arrow function so that `this` refers to `MyClass`,
// Note that `MyClass` will still be in the functions closure.
static #_ = (() => {
// 'Almost' how functions are typically added. ES6 style
// is always recommended over this.
this.prototype.myFunc = function myFunc() {
console.log(":D");
};
// ES6 would actually do this (approximately) so that the function is
// non-enumerable in the prototype.
Reflect.defineProperty(this.prototype, "myFunc", {
// enumerable: false, // defaults 'false'
writable: true,
configurable: true,
// I'm intentionally not using the shorthand for the function
// so that it is named 'myFunc'.
value: function myFunc() {
console.log(":D");
}
});
// Note that all children of MyClass will refer to this exact
// object if put in the prototype, i.e. not a copy of it.
// Also, this property will be non-enumerable on the children
// (but enumerable on the prototype itself unless you
// use `defineProperty` as above).
this.prototype.sharedProperty = { name: "Gerald" };
})();
}
Neither of those will be on the class prototype.
The class Foo { bar = 1; } syntax will assign a value to the class instance, to be accessed with this.bar.
The class Foo { static bar = 1; } syntax will assign a value to the class constructor, to be accessed with Foo.bar.
There isn't much reason to use the prototype in this case. It will only complicate who actually owns the property and assigning a number in a few different classes will have very little overhead.
I would suggest the class instance property and just use this.sight everywhere you need it.
The simplest way to add a property to the prototype inside the class body is by using the prototype assignment as a "value" for a dummy static property:
class MyClass {
static _dummy = MyClass.prototype.prop1 = <expression1>
static _dummy = MyClass.prototype.prop2 = <expression2>
// or
static _dummy = this.prototype.prop2 = <expression2>
}
(it works without parentheses because = is right-associative, and it's fine to re-use the same dummy property for each prototype assignment)
If you want to do more interesting (multi-line) computation for the values, an initializer can be an immediately-executed function expression, in which case you've basically created a static constructor and you can put all the initializations for the prototype and class object in that.
I think the other answer didn't get the point of this question. The whole point of having inheritance is that you can decide when and where to override something. and if anyone think that's an overhead, why does ya use an OOP language at all?
I don't know why it's "forbidden" after all, but I could share some ideas.
I'm pretty sure there is no way to define a Prototype Property with 'class' keyword. Any definition will be install on "hasOwnProperty". A huge setback is that in a constructor, there is no way to have any parents' constructors to interact with an overridden property.
To the point of reasoning, it's actually expelled by an other feature: you can use expressions to assign properties to this.
class A extends B { sight = this.getSight() * 3 }
When an expression excuses, it is either run with instance - created with constructor, or run at class declaration - when the prototype is created.
Accessors and methods don't have this problem. They are defined at prototype definition time and called at instance run time.
Property defined with expression with "=" is the return value of the expression. It is excused right after definition - should be the instance creation time, otherwise this could not be available.
So it's nothing about patterns. It's about having expressions or having inheritance. I definitely prefer inheritance, expressions are so pointless when you can write them right into the constructor.
class A extends B { constructor() { this.sight = this.getSight() * 3 }
Using decorators are a nice work around. You can always do something with the prototype in javascript:
#B({sight:2}) class A {};
decorator B is:
function(option) {return function(clazz) {clazz.prototype.sight = option.sight; return clazz}}
Using Class static initialization blocks1:
class MyClass {
static {
this.prototype.prop = 1;
}
}
console.log(MyClass.prototype.prop); // 1
const instance = new MyClass();
console.log(instance.__proto__.prop); // 1
this in the static block differs as1:
The this inside a static block refers to the constructor object of the class.
The code inside the static block is executed only once when the class initialization gets evaluated.
Note: Safari doesn't support this feature as of June 2022. You can check the latest info on, for example, mdn web docs1.
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_static_initialization_blocks
class MyClass {
constructor() {
MyClass.prototype.prop2 = "Marry";
}
}
const mc = new MyClass()
mc.__proto__ // { prop2: "Marry" }

What is a difference between an object literal and a class with values in constructor in javascript?

I've been working on end to end test in testcafe and in their documentation I found following solution for Page Model:
class Page {
constructor () {
this.nameInput = Selector('#developer-name');
}
}
export default new Page();
I've been doing some research and I cannot get my head around why it is not resolved with an object literal:
export const Page = {
nameInput: Selector('#developer-name');
}
What are consequences of using each of them?
The difference is significant but fundamentally both are JavaScript objects, albeit with different properties and values. Listing all differences on the level of the language would be a long story and you'd be wise to read and understand on JavaScript prototype-based inheritance, but the most important differences are:
Page is a prototype object: whenever you create an object of class Page with new Page(), the constructor function is called with this referring to the object being created, not Page itself. Any property you access on the object is searched along the so-called "prototype chain", including the so-called prototype object. This prototype object can be accessed with Page.prototype and in fact, all methods you define in the class Page are also properties of this prototype object. Unlike own properties designed to refer to unique objects or primitives specific to an object, functions in JavaScript don't have to be bound to an object during object or function creation and can be shared between objects (belonging to the same class, for instance) and are called on the actual instance, not the prototype to which they may belong. In other words, this.nameInput in your constructor actually adds a property named nameInput to the object being created with new Page(), not the prototype, while the constructor itself (constructor) and any non-static methods you might add to Page will be added as properties of Page.prototype. The constructor is accessed as Page.prototype.constructor, by the way, as you'd naturally expect. Page.prototype.constructor === Page evaluates to true, by the way.
An expression of the form like { nameInput: ... } creates an object which prototype is Object.prototype, in practice the most basic form of object with "no prototype" and thus no superclass or any traits beyond what the fundamental object prototype object could provide. Any properties any such { ... } object may seem to have through its prototype chain, including methods, are properties of Object.prototype. This is why you can do ({}).toString() or ({}).hasOwnProperty("foobar") without actually having toString or hasOwnProperty properties in your object -- toString and hasOwnProperty are properties of Object.prototype referring to two distinct methods called toString and hasOwnProperty, respectively, and JavaScript creates a special property on your object called __proto__ referring to Object.prototype. This is how it knows how to "walk the prototype chain". The names of functions themselves do not matter like that, by the way -- I may add a property on an object referring to an anonymous function: var foo = ({}); foo.bar = function() { }; and call said unnamed function with foo.bar().
One mistake you appear to be making is confusing an object of a class with the class, otherwise you wouldn't compare export default class Page { ... } to export const Page = { nameInput: Selector(...) } -- the former creates a class accessible as Page which is used as the prototype object whenever objects of the class are created, while the latter creates an object accessible as Page which contains nameInput referring to result of evaluating expression Selector("#developer-name") (calling Selector with the sole argument "#developer-name"). Not the same thing at all, not to mention that former has Page refer to a class (invariably a prototype in JavaScript), while latter has Page refer to an object that does not seem to fit the pattern of a class.
The interesting things start when you realize that since a class is an object like any other in JavaScript, any object can be used as a class if you know how prototype-based inheritance works:
new (function() { this.nameInput = Selector("#developer-name"); })();
What happens here? You create a new object with an unnamed function as the object constructor. The effect is absolutely equivalent to otherwise creating the object with new Page with Page being your original ES6 class (ECMAScript 6 is the language specification that adds class syntax to JavaScript).
You can also do this, again equivalent to if you defined Page with class Page ...:
function Page() {
this.nameInput = Selector("#developer-name");
}
var foo = new Page();
Page.prototype will be the prototype object for foo, accessible as foo.__proto__ and otherwise making it possible for you to call instance methods on foo like foo.bar(), provided you define bar property on at least Page.prototype:
function Page() {
this.nameInput = Selector("#developer-name");
}
Page.prototype.bar = function() {
console.log(this.nameInput);
}
var foo = new Page();
foo.bar();
In fact, the above is what browser would do internally if it had to interpret the following code:
class Page {
constructor() {
this.nameInput = Selector("#developer-name");
}
bar() {
console.log(this.nameInput);
}
}
It is beyond the scope of my answer to list differences between the two last approaches (isn't the same thing as the two approaches you proposed), but one difference is that with class Page ..., Page is not a property of window in some user agents while with function Page ... it is. It's partly historical reasons, but rest assured that so far defining constructors and prototype using either approach is pretty much the same, although I can imagine smarter JavaScript runtimes will be able to optimize the latter form better (because it's an atomic declaration, and not just a sequence of expressions and statements).
If you understand prototype-based inheritance at the heart of all of this, all your questions about this will fall away by themselves as very few fundamental mechanisms of JavaScript support 99% of its idiosyncrasies. You'll also be able to optimize your object design and access patterns, knowing when to choose ES6 classes, when not to, when using object literals ({ prop: value, ... }) and when not to, and how to share fewer objects between properties.
Classes can be thought of as a blueprint, they both provide an object in the end. But as the object literals name implies, you literally create it there and then with this 'literal' syntax. A class however, we would use to instantiate new instances from 1 base blueprint.
let x = { myProp: undefined }
let y = { myProp: undefined }
x.myProp = "test";
y.myProp // undefined
Here we see we make two separate instances, but we will have to repeat code.
class X { }
let x = new X();
let y = new X();
A class does not need to repeat the code, as it is all encapsulated in the idea of what X should be, a blueprint.
Similar to above [in the literal] we have two separate instances but it's cleaner, more readable, and any change we wish to make to every instance of this 'X' object can now be changed simply in the class.
There's a plethora of other benefits and even a paradigm dedicated to Object-Oriented Programming, read here for more:
https://www.internalpointers.com/post/object-literals-vs-constructors-javascript
To go further into the constructor question... In other languages we have fields.
I believe when you assign a field in the constructor, it just creates an underthehood like field (I say underthehood like because JavaScript is prototype based, and the class syntax is syntactical sugar to help write prototypes easier for programmers familiar with class syntax in other languages).
Here is an example in C#.
public class X{
private int y;
X() {
this.y = 5;
}
}
It's more a convention to assign fields in the constructor in other languages, so I assume this has something to do with it in JavaScript.
Hope this helps.
By declaring it as a Class you can later identify what type of object it is with .constructor.name:
class Page {
constructor () {
this.nameInput = "something";
}
// No comma
anotherMethod() {
}
}
const pageClass = new Page();
const pageLiteral = {
nameInput: "something"
, // must have a comma
anotherMethod() {
}
}
console.log("Name of constructor for class: ", pageClass.constructor.name); // Page
console.log("Name of constructor for literal: ", pageLiteral.constructor.name); // Object

What's the difference between static and instance members?

I want to know what the difference between these two functions at this sample class in javascript?
class Sample{
constructor(){}
sampleOne(){
console.log('this is test')
}
static sampleTwo(){
console.log('this is test too')
}
}
The first one is an instance method. This means, you have to create a new instance of the Sample class to call it:
let instance = new Sample();
instance.sampleOne();
The second one is a static method, so you don't have to have an instance of the class to call it:
Sample.sampleTwo();
Javascript doesn't have classes, therefore explanations in terms of "class" and "instance" are inaccurate. In javascript, there are only objects, which have constructors (functions) and prototypes (other objects). The ES6 "class" syntax is just a decorator, which simply translates java-like class declarations into prototypes, namely:
classes are turned into constructors (functions):
class Sample => function Sample()
ordinary methods are attached to the prototype property of the constructor, and copied to the object.[[Prototype]] later on when you do new Sample():
sampleOne => Sample.prototype.sampleOne
"static" methods are attached directly to the constructor:
sampleTwo => Sample.sampleTwo
Illustration:
static is a declaration to say this method sampleTwo can be called on the class level and not the instance level. sampleOne however, is an instance method and so you would need to instantiate an instance of your sample class.
const test = Sample();
test.sampleOne(); //works
test.sampleTwo(); //won't work cause it is defined on class level
Sample.sampleOne(); //won't work cause not static defined
Sample.sampleTwo(); //works because it is static.
Static methods are not bound to a class instance. But to the class itself. So while convenient you will not have access to this as there is no instance to point to.
Static methods are called on the class itself.
For example, the following would work perfectly
Sample.sampleOne()
// undefined
while
Sample.sampleTwo()
// this is a test two

How to make a JS object *become* a TypeScript class instance

Say I have the following class:
export class MyClass {
str: string = '';
foo() {
console.log(this.str);
}
}
Then, in some other code:
var myObj = {
str: 'Hello World';
}
How can I convert myObj into a MyClass instance, so the following line works:
myObj.foo();
// writes 'Hello World' to the console
(Please note that I cannot change the creation of myObj, because it's created in another library)
***** EDIT: *****
I'm still looking for a solution for this. The main problem is that MyClass has references to other classes, which maybe have references to MyClass. It's a whole object graph that I'm trying to convert to TypeScript classes.
Of every class and every property I know its type, and it matches perfectly with the classes and properties defined on MyClass.
Maybe something like this:(I've added the MyClass as JS code).
function becomeMyClass(o){
var fn1=function(){
var thing;
MyClass.apply(arguments);
//note: shallow copy only
for(thing in o){
if(Object.prototype.hasOwnProperty
.call(o,thing)){
this[thing]=o[thing];
}
}
};
fn1.prototype=Object.create(MyClass.prototype);
return new fn1([].slice.call(arguments,1));
}
function MyClass(){
this.str = "from myclass";
}
MyClass.prototype.foo=function(){
console.log(this.str);
};
var myObj = {
str:"from myObj"
}
myC = becomeMyClass(myObj);
console.log(myC.foo());//from myObj
console.log(myC instanceof MyClass);//true
Is this other library that creates the object always going to return that object from the function? You could create a .d.ts definitions file that defines that the specific function returns MyClass.
I think the simplest solution, the most readable solution and the one with the fewest lines of code would be to just map it:
var myClass = new MyClass();
myClass.str = myObj.str;
myClass.foo();
If the str property is mandatory, you could make it a constructor parameter and reduce this by one line...
var myClass = new MyClass(myObj.str);
myClass.foo();
Update
If you have many classes in your program with the same properties, perhaps you could encapsulate those properties within a class. For example, if all the classes had properties name, nickname, avatar you might wrap them into a Profile class. This gives you one property to map should you need to take the profile from one class and add it to another.
However, you know your use case best, so you might be interested in this JavaScript port of auto-mapper, which should easily map your stuff if the properties all have the same names:
Found it out, now I'm using the following utility method:
export class Util {
static become(obj: any, newClass: any) {
obj.__proto__ = (<any>(new newClass())).__proto__;
}
}
The following call converts myObj into a MyClass instance by assigning the right prototype:
Util.become(myObj, MyClass);
Maybe there's another, more elegant way that doesn't involve the use of __proto__.
Since you say you have a lot of objects with an unknown list of properties, I take it you can't write a constructor for MyClass to take in an arbitrary object and create a MyClass instance from its properties. Therefore you can't meaningfully "convert" myObj to an instance of MyClass.
Fiddling with myObj's prototype is not a very nice thing to do. See the warning at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
That leaves you with using duck typing: MyClass.foo.call(myObj); This will only work if MyClass's methods only refer to properties available on myObj, i.e., you don't expect the constructor of MyClass to have set any other properties to default values, etc. (since in this case the MyClass constructor has effectively never run).

Categories

Resources