Automatically adding inheritance as properties on constructor, javascript - javascript

I'm moving a (heavy) file-parser to a webworker. Webworkers can only return serialized json. Hence, I cant return function that gives me object that inherit from a specific class:
getCollection: function getCollection(cls) {
if (!cls) {
return this.myObjects;
}
let collection = cls.toUpperCase();
let arr = [];
for (let i = 0; i < this.nrOfObjects; i++) {
if(classes[collection]){
if (this.myObjects[i] instanceof classes[collection]) {
arr.push(this.myObjects[i]);
}
}
}
return arr;
}
So for example getCollection('product') would return all instances that inherits from the class product
When I move the parsers to the webworker, I can't return this function. Is there anyway I can add the inheritance to my constructor in my classes instead?
export class Column extends BuildingElment {
constructor(GlobalId, OwnerHistory, Name, Description) {
super(GlobalId, OwnerHistory, Name);
this.description = Description
this.nameOfClass = 'Column';
this.inheritance = ['Root', 'Product', 'BuildingElement'] // Can this be somehow automatically generated??
}
}
Or if there is any other way I can work with webworkers and still get the inheritance.

Related

Update Nested Object Property in JavaScript

I have the following piece of code below:
class Set {
constructor() {
console.log("New Set!");
}
get repsCompleted() {
return this._repsCompleted;
}
set repsCompleted(value) {
this._repsCompleted = value;
}
}
class Exercise {
constructor(name) {
console.log(name);
this.name = name;
}
get sets() {
return [new Set(), new Set(), new Set()]
}
}
let barbellSquat = new Exercise("Barbell Squat");
let updatedBarbellSquat = updateRepsCompleted(barbellSquat);
console.log(updatedBarbellSquat);
function updateRepsCompleted(exercise) {
for (var i = 0; i < exercise.sets.length; i++) {
exercise.sets[i].repsCompleted = 5;
}
return exercise;
}
I have a class exercises, which has a property sets, which is an array of objects of type Set.
So I create a new object off the Exercise class then pass the object to a function, whose purpose is to update the set objects and set the property "repsCompleted" to 5.
Then I console.log the exercise object, only to find that repsCompleted is undefined. I can't figure out why.
Here's a fiddle with the code:
https://jsfiddle.net/the_archer/a09tpr63/10/
You shouldn't use a getter for sets. A getter or setter is used every time the corresponding property is read or assigned, to allow you to customize the way that property works. The getter isn't only used the first time the property is accesssed.
So every time you use exercise.sets it creates a new array containing 3 empty Set objects. sets should be an ordinary property so you can retrieve the same value every time. You can initialize it in the constructor.
If you want to hide the sets property, like you did with repsCompleted in the Set class, you can use the same method: use a different name for the actual property, and define a getter and setter that accesses it. You can also initialize it lazily by checking whether the property is set yet.
class Set {
constructor() {
console.log("New Set!");
}
get repsCompleted() {
return this._repsCompleted;
}
set repsCompleted(value) {
this._repsCompleted = value;
}
}
class Exercise{
constructor(name) {
this.name = name;
this._sets = null;
}
get sets() {
if (!this._sets) {
this._sets = [new Set(), new Set(), new Set()];
}
return this._sets;
}
set sets(value) {
this._sets = value;
}
}
let barbellSquat = new Exercise("Barbell Squat");
let updatedBarbellSquat = updateRepsCompleted(barbellSquat);
console.log(updatedBarbellSquat);
function updateRepsCompleted(exercise) {
for (var i = 0; i < exercise.sets.length; i++) {
exercise.sets[i].repsCompleted = 5;
}
return exercise;
}

how to extends es6 class using Dojo

I have some es6 class
class Human {
constructor(){
this.age = 0;
}
}
I want to inherit from this class using dojo toolkit
define(["dojo/_base/declare"],
function (declare) {
return declare("Man", Human, {
});
});
I'm getting the error Class constructor Human cannot be invoked without 'new'.
Tried to inherit from Human.constructor and from Human.funcWrapper
class Human {
constructor(){
this.age = 0;
}
static funcWrapper(){
return new Human()
}
}
Nothing worked.
I know that I can use babel to transform my code to functions but I don't want because of some political reasons.
As usual, i'm posting banging my head for hours, and then i'm coming with a solution. So far i've tested it, and look like it is working good. including calling this.inherited(arguments, ["bla"]) (dojo way of calling super("bla"))
So, I've created this function to convert es6 class to function class
function funcClass(type) {
const FuncClass = function (...args) {
const _source = Reflect.construct(type, args, this.constructor);
const keys = Reflect.ownKeys(_source);
for (const key of keys) {
if (!key.match || !key.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) {
const desc = Object.getOwnPropertyDescriptor(_source, key);
!this[key] && Object.defineProperty(this, key, desc);
}
}
}
FuncClass.prototype = type.prototype;
return FuncClass;
}
And usage:
define(["dojo/_base/declare"],
function (declare) {
return declare("Man", funcClass(Human), {
});
});

javascript - Check if parent methods are used inside child methods

I'm writing some JS that extends a parent class and I wanted to know if there's a way to tell if a child class is using a parent method without having called it yet. Ideally I'd like to run a check in the constructor of the parent to see if any of the child methods are using the parent's methods in the method definition.
I've done a bit of research and have come across things like Object.getOwnPropertyNames() but I'm not sure if I'm headed in the right direction.
For instance:
class Path {
constructor (name) {
// how can I check if addRelationship have been used? If possible.
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (relationship) {
// do something
this.currentRelationship = relationship.path;
return this;
}
makePath () {
let path = [this.path];
if(this.currentRelationship) {
path.push(this.currentRelationship)
}
return path.join("/");
}
}
class OnePath extends Path {
// ...
someMethodFromThatRelationship () { }
}
class TwoPath extends Path {
// ...
}
var onePath = new OnePath('one');
var twoPath = new TwoPath('two-path');
class SomeOtherPath extends Path {
one () {
return this.addRelationship(onePath);
}
two () {
return this.addRelationship(twoPath);
}
}
The idea of the above example is I could check if addRelationship is referenced in any methods and if so, register a this.relationships.one and this.relationships.two before one() and two() are actually called. I hope I'm making sense. I'd love to know if this is even possible.
Updated
The end result of the above code would be the ability to do the following:
let someOtherPath = new SomeOtherPath('some-other-path');
// now I can call
someOtherPath.relationships.one.someMethodFromThatRelationship();
// and can also call the save method from the extended class
someOtherPath.one().makePath();
// some-other-path/one
// I can also just call
someOtherPath.makePath();
// some-other-path
Is there a way to tell if a child class is using a parent method without having called it yet?
No. Figuring out what programs do without calling them is equivalent to the unsolvable halting problem.
I think what you are actually looking for is a more declarative approach for creating the relationship and its accompanying method in one go. Don't use too much magic (which a parent constructor inspecting its child class code would certainly be) but be explicit.
class Path {
constructor (path) {
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (name, relationship) {
this.relationships[name] = relationship;
this[name] = function() {
// do something
this.currentRelationship = name;
return this.relationships[name];
}
return this;
}
makePath () {
let path = this.path;
if (this.currentRelationship) {
path += "/" + this.relationships[this.currentRelationship].makePath();
}
return path;
}
}
class SomeOtherPath extends Path {
constructor(name) {
super(name);
this.addRelationship("one", new OnePath('one'));
this.addRelationship("two", new TwoPath('two-path'));
}
}
or even
class Path {
constructor (path, relationships = {}) {
this.relationships = relationships;
this.currentRelationship = '';
this.path = path;
for (let const r in relationships)
this.addRelationship(r, relationships[r]);
}
…
}
class SomeOtherPath extends Path {
constructor(name) {
super(name, {
one: new OnePath('one'),
two: new TwoPath('two-path')
});
}
}
Maybe you don't even need these child classes any more if they don't have other methods or are only instantiated once (as singletons).
Notice that the above approach will create new methods and new subpaths on every instantiation of the constructor, if you don't want that you can of course also put the declaration on the class statically. Just make addRelationShip a static method that initialises the default relationships objects and puts the methods on the class' .prototype. The variations of the pattern are endless.
You even might want to experiment with the proposed decorators feature for classes.

Extend native JavaScript array

Is there any way to inherit a class from JS native function?
For example, I have a JS function like this:
function Xarray()
{
Array.apply(this, arguments);
//some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
I tried to convert it to Typescript but i failed!!
export class Xarray implements Array {
}
The compiler asks me to define all Array interface properties. I know if I need this Xarray.prototype = new Array();, I have to extend Array in TS.
How to extend the JS native object in TS?
Starting in TypeScript 1.6, you can extend the Array type, see What's new in TypeScript
Here's an example:
class MyNewArray<T> extends Array<T> {
getFirst() {
return this[0];
}
}
var myArray = new MyNewArray<string>();
myArray.push("First Element");
console.log(myArray.getFirst()); // "First Element"
If you are emitting to ES5 or below, then use the following code:
class MyNewArray<T> extends Array<T> {
constructor(...items: T[]) {
super(...items);
Object.setPrototypeOf(this, MyNewArray.prototype);
}
getFirst() {
return this[0];
}
}
Read more about why this is necessary here.
I don't think there is a way to inherit existing interfaces like Array,
export class Xarray implements Array {
}
You should create a function and inherit it with its prototype. Typescript also will accept it which is similar to javascript.
function Xarray(...args: any[]): void; // required in TS 0.9.5
function Xarray()
{
Array.apply(this, arguments);
// some stuff for insert, add and remove notification
}
Xarray.prototype = new Array();
UPDATE: This one is discussed well and provided the best solution for this at jqfaq.com.
//a dummy class it to inherite array.
class XArray {
constructor() {
Array.apply(this, arguments);
return new Array();
}
// we need this, or TS will show an error,
//XArray["prototype"] = new Array(); will replace with native js arrray function
pop(): any { return "" };
push(val): number { return 0; };
length: number;
}
//Adding Arrray to XArray prototype chain.
XArray["prototype"] = new Array();
//our Class
class YArray extends XArray {
///Some stuff
}
var arr = new YArray();
//we can use the array prop here.
arr.push("one");
arr.push("two");
document.writeln("First Elemet in array : " + arr[0]);
document.writeln("</br>Array Lenght : " + arr.length);
Hope, this might help you!!!
Yes it's possible to extend a native JS object in TS, however there is an issue extending built-in types (those included in lib.d.ts) like Array. Read this post for workaround: http://typescript.codeplex.com/workitem/4
So defining a type interface which extends a native type object at a later stage can be done in the following way:
/// <reference path="lib.d.ts"/>
interface Array {
sort: (input: Array) => Array;
}
Using on a concrete example, you can sort some elements on an array which define a sort function in an interface and later implements it on an object.
class Math implements Array {
sort : (x: Array) => Array {
// sorting the array
}
}
var x = new Math();
x.sort([2,3,32,3]);
While researching this, I came across Ben Nadel's excellent post on Extending JavaScript Arrays While Keeping Native Bracket-Notation Functionality. After some initial confusion on how to succesfully convert this into TypeScript, I created a fully working Collection class that can be subclassed.
It can do everything an Array can, including indexing by brackets,use in loop constructions (for, while, forEach), maps, etc.
The main implementation points are
Create an array in the constructor, add the methods to the array and return that from the constructor
Copy dummy declarations of Array methods to pass the implements Array bit
Example of usage:
var foo = new Foo({id : 1})
var c = new Collection();
c.add(foo)
c.length === 1; // => true
foo === c[0]; // => true
foo === c.find(1); // => true
I made it available as a gist, complete with tests and an example implementation of a subclass, but I present the full source here:
/*
* Utility "class" extending Array with lookup functions
*
* Typescript conversion of Ben Nadel's Collection class.
* https://gist.github.com/fatso83/3773d4cb5f39128b3732
*
* #author Carl-Erik Kopseng
* #author Ben Nadel (javascript original)
*/
export interface Identifiable {
getId : () => any;
}
export class Collection<T extends Identifiable> implements Array<T> {
constructor(...initialItems:any[]) {
var collection = Object.create(Array.prototype);
Collection.init(collection, initialItems, Collection.prototype);
return collection;
}
static init(collection, initialItems:any[], prototype) {
Object.getOwnPropertyNames(prototype)
.forEach((prop) => {
if (prop === 'constructor') return;
Object.defineProperty(collection, prop, { value: prototype[prop] })
});
// If we don't redefine the property, the length property is suddenly enumerable!
// Failing to do this, this would fail: Object.keys([]) === Object.keys(new Collection() )
Object.defineProperty(collection, 'length', {
value: collection.length,
writable: true,
enumerable: false
});
var itemsToPush = initialItems;
if (Array.isArray(initialItems[0]) && initialItems.length === 1) {
itemsToPush = initialItems[0];
}
Array.prototype.push.apply(collection, itemsToPush);
return collection;
}
// Find an element by checking each element's getId() method
public find(id:any):T;
// Find an element using a lookup function that
// returns true when given the right element
public find(lookupFn:(e:T) => boolean):T ;
find(x:any) {
var res, comparitor;
if (typeof x === 'function') {
comparitor = x;
} else {
comparitor = (e) => {
return e.getId() === x;
}
}
res = [].filter.call(this, comparitor);
if (res.length) return res[0];
else return null;
}
// Add an element
add(value:T);
// Adds all ements in the array (flattens it)
add(arr:T[]);
add(arr:Collection<T>);
add(value) {
// Check to see if the item is an array or a subtype thereof
if (value instanceof Array) {
// Add each sub-item using default push() method.
Array.prototype.push.apply(this, value);
} else {
// Use the default push() method.
Array.prototype.push.call(this, value);
}
// Return this object reference for method chaining.
return this;
}
remove(elem:T):boolean;
remove(lookupFn:(e:T) => boolean):boolean ;
remove(x:any):boolean {
return !!this._remove(x);
}
/**
* #return the removed element if found, else null
*/
_remove(x:any):T {
var arr = this;
var index = -1;
if (typeof x === 'function') {
for (var i = 0, len = arr.length; i < len; i++) {
if (x(this[i])) {
index = i;
break;
}
}
} else {
index = arr.indexOf(x);
}
if (index === -1) {
return null;
}
else {
var res = arr.splice(index, 1);
return res.length ? res[0] : null;
}
}
// dummy declarations
// "massaged" the Array interface definitions in lib.d.ts to fit here
toString:()=> string;
toLocaleString:()=> string;
concat:<U extends T[]>(...items:U[])=> T[];
join:(separator?:string)=> string;
pop:()=> T;
push:(...items:T[])=> number;
reverse:()=> T[];
shift:()=> T;
slice:(start?:number, end?:number)=> T[];
sort:(compareFn?:(a:T, b:T) => number)=> T[];
splice:(start?:number, deleteCount?:number, ...items:T[])=> T[];
unshift:(...items:T[])=> number;
indexOf:(searchElement:T, fromIndex?:number)=> number;
lastIndexOf:(searchElement:T, fromIndex?:number)=> number;
every:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
some:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> boolean;
forEach:(callbackfn:(value:T, index:number, array:T[]) => void, thisArg?:any)=> void;
map:<U>(callbackfn:(value:T, index:number, array:T[]) => U, thisArg?:any)=> U[];
filter:(callbackfn:(value:T, index:number, array:T[]) => boolean, thisArg?:any)=> T[];
reduce:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
reduceRight:<U>(callbackfn:(previousValue:U, currentValue:T, currentIndex:number, array:T[]) => U, initialValue:U)=> U;
length:number;
[n: number]: T;
}
Of course, the bits on Identifiable, the find and remove methods are not needed, but I supply them none the less as a full fledged example is a tad more usable than a bare-bones Collection without any methods of its own.
Constructors that return an object implicitly substitute the value of this for callers of super(). Generated constructor code has to capture whatever super() returns and replace it with this.
Built-in classes use ES6 new.target to do the fixup but there's no way for ES5 code to ensure that new.target has a value calling the constructor.
This is why your extra methods vanish - your object has the wrong prototype.
All you need to do is fix the prototype chain after calling super().
export class RoleSet extends Array {
constructor() {
super();
Object.setPrototypeOf(this, RoleSet.prototype);
}
private containsRoleset(roleset:RoleSet){
if (this.length < roleset.length) return false;
for (var i = 0; i < roleset.length; i++) {
if (this.indexOf(roleset[i]) === -1) return false;
}
return true;
}
public contains(item: string | RoleSet): boolean {
if (item) {
return typeof item === "string" ?
this.indexOf(item) !== -1 :
this.containsRoleset(item);
} else {
return true;
}
}
}
Be aware that this curse shall afflict thy children and thy children's children until the end of code; you have to do the fixup in every generation of an inheritance chain.
In your case, a good bet would be to use this pattern:
function XArray(array) {
array = array || [];
//add a new method
array.second = function second() {
return array[1];
};
//overwrite an existing method with a super type pattern
var _push = array.push;
array.push = function push() {
_push.apply(array, arguments);
console.log("pushed: ", arguments);
};
//The important line.
return array
}
Then you can do:
var list = XArray([3, 4]);
list.second() ; => 4
list[1] = 5;
list.second() ; => 5
note however that:
list.constructor ; => Array and not XArray
If you already have a working Xarray implementation, I don't see the point in recreating it in typescript, which eventually will compile back to JavaScript.
But I do see the point in being able to use the Xarray in TypeScript.
In order to accomplish this, you simply need an interface for your Xarray. You don't even need to have a concrete implementation of your interface since your existing js implementation will serve as one.
interface Xarray{
apply(...arguments : any[]) : void;
//some stuff for insert, add and ...
}
declare var Xarray: {
new (...items: any[]): Xarray;
(...items: any[]): Xarray;
prototype: Array; // This should expose all the Array stuff from ECMAScript
}
After doing this, should be able to use your custom defined type through the declared variable without actually implementing it in TypeScript.
var xArr = new Xarray();
xArr.apply("blah", "hehe", "LOL");
You might look for reference here to see how they typed the ECMAScript Array API:
http://typescript.codeplex.com/SourceControl/changeset/view/2bee84410e02#bin/lib.d.ts
Yes you can augment the Builtin types and do it in a way that doesn't require all the paraphernalia of an XArray as described in the other answers and is closer to how you would do it in javascript.
Typescript allows a number of ways to do this, but for the Builtin types like Array and Number you need to use "merging" and declare the global namespace to augment the types, see the docs
so for Array we can add an optional metadata object and a get first member
declare global {
interface Array<T> {
meta?: any|null ,
getFirst(): T
}
}
if(!Array.prototype.meta )
{
Array.prototype.meta = null
}
if(!Array.prototype.getFirst )
{
Array.prototype.getFirst = function() {
return this[0];
}
}
we can use this like so:
let myarray: number[] = [ 1,2,3 ]
myarray.meta = { desc: "first three ints" }
let first: number = myarray.getFirst()
The same goes for Number say I want to add a modulo function that isn't limited like the remainder %
declare global {
interface Number {
mod(n:number): number
}
}
if(!Number.prototype.mod )
{
Number.prototype.mod = function (n: number) {
return ((this % n) + n) % n;
}
}
and we can use it like so:
let foo = 9;
console.log("-9.mod(5) is "+ foo.mod(5))
For Functions that we may want to add an API to ie to make it behave like a function and an object we can use hybrid types (see docs)
// augment a (number) => string function with an API
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
//helper function to get my augmented counter function with preset values
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
use it like so:-
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
With purpose to overcome the problem of extension of the native Array class, I took advantage of a decorator.
function extendArray(constructor: Function) {
Object.getOwnPropertyNames(constructor.prototype)
.filter(name => name !== 'constructor')
.forEach(name => {
const attributes = Object.getOwnPropertyDescriptor(constructor.prototype, name);
Object.defineProperty(Array.prototype, name, attributes);
});
}
#extendArray
export class Collection<T> extends Array<T> {
constructor(...args: T[]) {
super(...args);
}
// my appended methods
}
BTW This decorator can be made more generic (for other native classes) if to use a decorator factory.
Don't know how frowned upon this is but for example I needed to create an array of BulletTypes so that I could cycle through them. What I did is the following:
interface BulletTypesArray extends Array<BulletType> {
DefaultBullet?: Bullet;
}
var BulletTypes: BulletTypesArray = [ GreenBullet, RedBullet ];
BulletTypes.DefaultBullet = GreenBullet;
Obviously you could could also make a generic interface, something like interface SuperArray<T> extends Array<T>.

Javascript - Reflecting encapsulated members

I created a javascript "class" as follows:
function MyClass(member1, member2) {
this.Member1 = member1;
this.Member2 = member2;
}
All members are Strings.
I want to take an instance of MyClass and "clean" the members by calling
function NoneBecomesNull(item) {
if (item === "[None]")
item = "";
return item;
}
for-each member of the class. Is there an effecient way to accomplish this task? (In the case where MyClass has 30 members).
I would like to avoid doing...
myClassInstance.Member1 = NoneBecomesNull(myClassInstance.Member1);
myClassInstance.Member2 = NoneBecomesNull(myClassInstance.Member2);
//...30+ times
Try the following
for (var name in theObject) {
if (theObject.hasOwnProperty(name) && theObject[name] === "[None]") {
theObject[name] = "";
}
}
I used hasOwnProperty to prevent the reseting of properties higher up in the prototype chain. Your example didn't show the use of a prototype chain here and hence it's likely not necessary for this example. But it's good practice.
Why not encapsulate this behaviour inside your object?
WORKING EXAMPLE
function MyClass(member1, member2) {
this.Member1 = member1;
this.Member2 = member2;
this.clean = function() {
for ( var member in this ) {
if (this.hasOwnProperty(member) && this[member] === "[None]") {
this[member] = "";
}
}
};
}
Then it only takes one line to accomplish...
var obj = new MyClass("[None]", "hello");
obj.clean();

Categories

Resources