In javascript, I've created a class Group that creates an empty array named items and returns it.
But I'm trying to create a static method from(){} that takes an iterable object and creates a new Group from that iterable obj.
I'm trying this.items.push() but its not working how I'd expect it to
class Group(){
constructor(){
this.items = []
}
from(obj){
for(let item of obj){this.items.push(item)}
}
}
i'd implement it like so:
let group = Group.from([10, 11))//random numbers
it unfortunately returns the following error:
TypeError: Cannot read property 'push' of undefined (line 37 in function
Function.from)
You have a syntax error:
class Group(){
should be:
class Group { // no parameter list here
as in the class syntax, the parameters are defined in the constructor function.
Where a constructor adds methods to "class" instances as below, the method is available to instances, not the constructor itself. You you have to create an instance, then call the method:
class Group {
constructor () {
this.items = [];
}
from (obj) {
for (let item of obj) {
this.items.push(item);
}
}
}
var group = new Group();
group.from([1,2]);
console.log(group.items); // 1, 2
console.log(typeof Group.from); // undefined
Although there are drafts for new static syntax on JS classes, you'll currently need to use the prototype for static methods / properties.
class Group {
constructor(){
this.items = []
}
}
Group.prototype.from = function(obj){
var group = new Group;
for(let item of obj) group.items.push(item);
return group;
}
let group = aGroudInstance.from([10, 11]);
You probably want to simply add the method onto the Group class (object) like so:
Group.from = function(...) ...;
As it's not actually making use of being a static method, and would unnecesarily require an instance of a Group -- unless you used it like so: Group.prototype.from(...).
It's a factory function.
Related
Code example:
ClassA.js
var classB = require('./ClassB');
function ClassA() {
this.ID = undefined;
this.Type = undefined;
....
}
ClassA.prototype.init = function init(id){
this.ID = id;
this.get();
if (this.Type === 'C' && Object.getPrototypeOf(this) === ClassA.prototype) {
return new classB().init(this.ID);
}
}
ClassB.js
function ClassB() {
ClassA.call(this);
this.additionalProp = undefined;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
I have implemented two classes ClassA and ClassB.
ClassB is a child of CLassA and has some additional properties.
The prototype chain for ClassB is setup like this:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
The information for the objects is retrieved from an API via an ID.
At the time of the API call I do not know if the object needs to be an instance of ClassA or ClassB. So I always start with an object of ClassA to call the API.
When the API returns a specific Type I want to convert my object from ClassA to more specific ClassB.
I tried to call the constructor of ClassB out of ClassA - this throws an error:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
I don't think I should reference ClassB in ClassA at all, but it was worth a try...
Thank you for helping out a novice! :)
At the time of the API call I do not know if the object needs to be an instance of ClassA or ClassB. So I always start with an object of one class, then when the API returns a specific type I want to convert my object.
Just don't do that. Construct your object after the API returns, and construct it with the correct type in the first place.
I always start with an object of the class to call the API
This is the point where your design has gone wrong. The class should not be responsible for calling the API, it should be responsible only for representing the data and having methods act on the data. Don't start with an empty object that can somehow hydrate itself, have the constructor completely initialise the object from the data passed as parameters.
Put the API call in a separate function, outside of the class. Create a separate class to represent the API endpoint maybe. If absolutely necessary, make it a static method of your base class, it still can create new ClassA or new ClassB instances then.
In a comment you've mentioned this error:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
You get that because ClassA.js and ClassB.js have a circular relationship: each tries to import something the other exports. That can be fine in some cases, but here you have code in ClassB.js trying to use ClassA.prototype in top-level code, and ClassA.js importing ClassB from ClassB.js. You end up with a placeholder for the ClassA import and the Object.create(ClassA.prototype) call doesn't work.
It's going to be much easier if you define both of them in the same file, thanks to function declaration hoisting.
I'd also modify init so that it always returns the instance, since you need to return a new object sometimes but not other times. So have it always return the instance simplifies the calling code.
Here's a minimal-changes example:
function ClassA() {
this.ID = undefined;
this.Type = undefined;
// ....
}
ClassA.prototype.get = function () {
// *** Just for debugging, ID 2 is `ClassB`, any other is `ClassA`
this.Type = this.ID === 2 ? "C" : "X";
};
ClassA.prototype.init = function init(id) {
this.ID = id;
this.get();
if (this.Type === "C" && Object.getPrototypeOf(this) === ClassA.prototype) {
return new ClassB().init(id); // *** Use `ClassB`, not `ClassA`
}
return this; // *** So the caller always gets an instance they can use
};
function ClassB() {
ClassA.call(this);
this.additionalProp = undefined;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
module.exports.ClassA = ClassA;
module.exports.ClassB = ClassB;
Then using it (just for example):
var both = require("./both");
var ClassA = both.ClassA;
var ClassB = both.ClassB;
var obj1 = new ClassA();
obj1 = obj1.init(1);
console.log(obj1 instanceof ClassA); // true
console.log(obj1 instanceof ClassB); // false
var obj2 = new ClassA();
obj2 = obj2.init(2);
console.log(obj2 instanceof ClassA); // true
console.log(obj2 instanceof ClassB); // true
That said, I think I'd refactor this. You've said that there's a separate init method because sometimes you want to use methods on the objects before you have an id. That makes me think ClassA (at least) is trying to do too much, both things that it can do when it doesn't know what it is (no id) and things it can do when it does. The instance returned by a constructor should be fully baked and ready to go. So probably better to split the parts of ClassA that don't need an id off into something else. That would also mean that ClassA didn't have to refer to ClassB, which isn't best practice.
I think I'd probably also split get off to be separate from the classes, and have it return the appropriate instance.
For example:
ClassA.js:
function ClassA(data) {
this.ID = data.id;
this.Type = data.type;
// ....
}
// ...other `ClassA` methods...
module.exports = ClassA;
ClassB.js:
var ClassA = require("./ClassA");
function ClassB(data) {
ClassA.call(this, data);
this.additionalProp = data.additionalProp;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
// ...other `ClassB` methods...
module.exports = ClassB;
get.js (or whatever):
var ClassA = require("./ClassA");
var ClassB = require("./ClassB");
function get(id) {
var data = /*...get from API...*/;
var cls = "additionalData" in data ? ClassB : ClassA;
return new cls(data);
}
That provides a much better separation of concerns.
I am making a mini state manager library for objects (because. don't ask.) and I want to use an approach like this (pseudo-code)
states = {}
when object is initialized {
if object.keys.states {
states[object.name] = object.keys.states;
}
}
/*
When object is initialized:
if object.keys.states exists:
states[object.name] = object.keys.states
*/
Is there a way to achieve this in typecript/javascript
is this typescript? if this is typescript you could use a class and an interface to execute that code or a constructor inside a class
class states {
states: State[] = [];
constructor(statesc?: State[])
{
if (statesc) {
for(var state in statesc)
{
//no need to do objects keys if you can rotate trough the given parameter
//btw only a class constructor can be called when an object is instantiated otherwise you need to do the
//logic after it has been created
this.states.push(statesc[state]);
}
}
}
}
interface State{
id: number;
name: string;
}
//then you can do this
let states = new States(states); and the constructor gets called
I have been looking for an answer on here but i couldn't find any, but anyway my question is how to get an array in a method that has been declared and initialised in an other method but in the same class. I'll make it a bit more clear by demonstrating what i want to achieve and what i have tried so far.
Javascript:
class SomeClass {
method1() {
var array = new array();
//its actually a 2d array and it is being initialised here but for simplicity this isn't
//necessary in the example.
}
method2() {
// --> Here i want to access the array and it's contents.
//I have tried this:
this.array;
//and
array;
}
}
but i got "cannot ready property of undefined" when i tried this.array;
You have to declare the array as an element of the Class, not inside a method, for that, you can use a constructor.
In this link you can see more information.
Here is an example:
class SomeClass {
constructor(someValue) {
// Initialize array or any other atribute
this.arr = new Array(someValue);
}
method1() {
console.log(this.arr);
}
method2() {
console.log(this.arr);
}
}
var instance = new SomeClass('data');
instance.method1();
instance.method2();
The array which is declared in method1 will only be available in that function. There is no way to access local variables of a function in some other function.
The solution could be to use the array as property of instance of class
class SomeClass {
constructor(){
this.array = []
}
method1() {
console.log(this.array);
}
method2() {
console.log(this.array)
}
}
const obj = new SomeClass();
obj.method1();
obj.method2();
Okay so you are making a major mistake, Your concepts of OOP are at stake.
To access array as a property/ instance of a class , You need to declare a constructor it within the class. Somewhat like this
class SomeClass {
constructor(){
this.array = new Array();
}
yourMethod1(){
console.log(this.array); /// You cann access it here and manipulate
}
yourMethod2(){
console.log(this.array); // You can accesss here too and do the same
}
}
Later on you can create an instance of your class like this and access the methods and do whatsoever
let a = new SomeClass();
a.yourMethod1();
a.yourMethod2();
I have a class instance whose constructor takes an object or a JSON string representing that object. *
Because that JSON comes from a remote source, I like to ensure that the properties all have a lowercase first letter. However I've noticed that the method I'm using to do so (Object.keys()) ignores prototype methods. This makes sense after reading the documentation. I considered Object.getOwnPropertyNames() instead but given that I'm not dealing with enumerable vs non-enumerable (as far as I know), that wouldn't make a difference.
Is there an alternative method I can use to lowercase the first letter of all keys and prototype methods?
Thanks. Demonstration below.
class B {
constructor() {
this.MyProperty = "Hello world!";
}
MyMethod() {}
}
class A {
constructor(b) {
console.log(`MyMethod exists before lowercasing: ${!!b.MyMethod}`);
b = Object.keys(b).reduce((copy,key) => ({...copy, [`${key[0].toLowerCase()}${key.slice(1)}`]: b[key]}),{});
console.log(`MyMethod exists after lowercasing: ${!!b.myMethod || !!b.MyMethod}`);
console.log(`MyProperty has been lowercased: ${!!b.myProperty}`);
}
}
let b = new B();
let a = new A(b);
* I've removed this for sake of brevity.
You could get the prototype from the instance using Object.getPrototypeOf(instance) and then invoke Object.getOwnPropertyNames() on the prototype to get the list of prototype methods.
Using Array.prototype.reduce() you can combine the prototype methods as well as the keys of the object instance you have given into a single object and apply your lower-casing logic subsequently:
class B {
MyMethod() { return true }
}
class A {
constructor(b) {
console.log(`MyMethod exists before lowercasing: ${!!b.MyMethod}`);
var proto = Object.getPrototypeOf(b);
var comp = Object.getOwnPropertyNames(proto).reduce((obj, key) => {obj[key] = proto[key]; return obj}, {...b})
b = Object.keys(comp).reduce((copy,key) => ({...copy, [`${key[0].toLowerCase()}${key.slice(1)}`]: b[key]}),[]);
console.log(`MyMethod exists after lowercasing: ${!!b.myMethod || !!b.MyMethod}`);
}
}
let b = new B();
let a = new A(b);
/**
* adds an entry to the list
*
* #param data {Object}
* #return this
*/
ElementList.prototype.addEntry = function(data){
if (!data) return this;
data['type'] = this.children_type;
// add the entry to the current elements
this.element_list.push(new FavoriteEntry(data));
this.refresh();
return this;
};
/**
* favorites extend ElementList
*
* #param setting_list
* #constructor
*/
function FavoriteList(setting_list){
ElementList.call(this, setting_list);
}
FavoriteList.prototype = new ElementList();
FavoriteList.constructor = FavoriteList;
So this a short code snipplet of an educational project of mine.
What I want to do is reduce repeating code so I created a generic ElementList object
So
the Example FavoriteList inherties the parent Objects prototype
The constructors is pointing to the Childobject
the Parent constructor is called within the child.
That works just perfectly fine my problem is
// add the entry to the current elements
this.element_list.push(new FavoriteEntry(data));
This should create a new instance of an Object BASED on the CHILD so therefore I need to get the name of the child instance that's calling the parent method
i tried
- this.constructor (point to the parent)
- this.constructor.name
- this instanceof FavoriteList (works)
since I DON'T want to pass a name and i think iterating through instanceof "options" is not really smart.
I would ask for some insights how I can access the childs instance name in the parent elements method body.
Please I only need an explicit answer to this!! I already read workarounds! If It's not possible just say so :)
thx in advance :)
this.element_list.push(new FavoriteEntry(data));
This should create a new instance of an Object BASED on the CHILD so
therefore I need to get the name of the child instance that's calling
the parent method
No, you don't seem to need to know the name. All you need is a helper function to generate new Entry instances, that can be overwritten to generate more specific entries. Maybe you're already doing that by passing a children_type with the data…
i tried - this.constructor (point to the parent)
It should work if you had set the constructor correctly. Change your code to
FavoriteList.prototype.constructor = FavoriteList;
// ^^^^^^^^^^
Also, you might want to use Object.create instead of new to set up the prototype chain.
I'm not sure if I fully understand but the code new FaforiteEntry should create either a FororiteEntry or another type based on the current object type.
Maybe the following example could help you out:
var ElementList = function(args) {
this.element_list = [];
}
ElementList.prototype.addEntry = function(args) {
this.element_list.push(new this.entryType(args.val));
};
//will create element_list items of type String
ElementList.prototype.entryType = String;
function FavoriteList(args) {
ElementList.call(this, args);
}
FavoriteList.prototype = Object.create(ElementList.prototype);
FavoriteList.constructor = FavoriteList;
//will create element_list items of type Array
FavoriteList.prototype.entryType = Array;
//adding entries to f would create items of type Array
var f = new FavoriteList();
f.addEntry({val: 2});
console.log(f.element_list);//[[undefined, undefined]]
//adding entries to e would create items of type String
var e = new ElementList();
e.addEntry({val: 2});
console.log(e.element_list);//[ String { 0="2"...
Simple code example:
function Parent(){
// custom properties
}
Parent.prototype.getInstanceName = function(){
for (var instance in window){
if (window[instance] === this){
return instance;
}
}
};
var child = new Parent();
console.log(child.getInstanceName()); // outputs: "child"