Apologies for the title but I wasn't sure how to phrase this.
In a normal module you have the following format:
var SomeClass = function() {
...
};
module.exports = SomeClass;
You can shorthand that to:
module.exports = function() {
...
};
There is however a problem with the second notation if you need to use the 'class' name inside itself ( to instantiate a new object of itself for example). Using the 1st example this would be easy ...
var SomeClass = function() {
...
function doSomething() {
var x = new SomeClass();
}
...
};
module.exports = SomeClass;
Is this possible using the 2nd notation? I know I can't use 'this' as that is an instance variable, I also can't use any form of the module.
module.exports = function() {
...
function doSomething() {
var x = new ??????
}
...
};
I'm asking purely from an aesthetic and a uniformity point of view.
Any Help appreciated.
Thanks
It is possible to do this in the form that you describe. Theoretically, to you in that scope, there is no difference between SomeClass and module.exports: both are variables and you give them both the same value.
Therefor, if you wanted, you could have something like this (though it looks odd):
module.exports = function() {
// ...
function doSomething() {
var x = new module.exports();
}
// ...
};
Is it bad practice to access variables in a javascript class without instantiating it?
For example:
var Test = function(){
this.tests.push("Something");
}
Test.prototype.tests = [];
var test = new Test();
console.log(Test.prototype.tests); //Is this okay to do? Obviously I can use test.test here, but this is a poor example of why I am wanting to do this; it merely shows my question.
I've run across an instance where I only have an id and I want to use the class to get the correct instance of itself for me such that: Test.prototype.getByID(id); but I wanted to make sure it is proper to do this.
You could take advantage of closures in JavaScript and do something like the following (jsfiddle). This has the advantage of making your "list of tests" private so it can't be messed with and you don't have to access the prototype which does feel a bit odd:
var Test = (function() {
var tests = {};
var Test = function(id){
tests[id] = this;
this.id = id;
}
Test.getByID = function(id) {
return tests[id];
}
return Test;
}());
var test1 = new Test(1);
var test2 = new Test(2);
var test2_2 = Test.getByID(2);
alert( test1 === test2_2 );//should be false
alert( test2 === test2_2 );//should be true
I'm trying todo some OO in Javascript, and I'm coming from C++ and Ruby. I've managed to create one object, but nesting an object inside is being alittle bit of a pain.
function Model()
{
//...
}
function Player(props)
{
var props = {
// ...
}
var model = new Model(props); // I've tried 'this.model = new Model() as well
}
var props = {
// ...
}
var player = new Player(props);
Player gets created fine, but if I try and nest the object it fails. What am I doing wrong.
Example
You were close. There are much better ways of "seudo-extending" object in javascript. jQuery.extend is one possible way. You can write your own method that check properties as well. I think the biggest break down for you was overwriting props in the Player function.
With functions this is key
Functions are the only scope in JavaScript, so be careful with naming variables
It's important to understand the difference between the object literal var a = {} and functions var a = new Method();. However, it seems you have that down well.
Code
function Model(data)
{
this.Name = data.Name;
this.Other = data.Other;
}
function Player(props)
{
var privateProps = {
Name: 'testing'
};
privateProps.Other = props.Other;
this.model = new Model(privateProps); // I've tried 'this.model = new Model() as well
}
var props = {
Other: 'Other'
}
var player = new Player(props);
I'm having a little trouble working out how my JavaScript should be structured, etc..
My OOP skills in languages such as PHP, ActionScript 3 and so on are what I'm assuming to be on-par, but JS is lacking this which has thrown me off quite a bit in my learning.
I have a vague understanding of the prototype feature which I used a little in AS2 - I believe this is the closest I'll be able to get. At the moment, I'm laying out my code similar to this:
var slideshow =
{
property: value,
/**
* This is a method
*/
myMethod: function()
{
// do method things
}
};
// ------
slideshow.property ++;
slideshow.myMethod();
This all works okay, but it's void my ability to do something like:
var myslideshow1 = new Slideshow();
var myslideshow2 = new Slideshow();
myslideshow1.property = 10;
myslideshow2.property = 16;
I'm not sure on how to go about creating two different instances of one "object" I've created (in this case, slideshow).
I can't find any resources that explain the prototype feature in a way that makes sense.
Any pointers would be supoib.
Any javascript function can act as a constructor for a class, so try this:
function SlideShow(params) {
return {
property: value,
myMethod: function() {
//do method things
};
};
};
var slideshow1 = new SlideShow(params);
slideshow1.property = 10;
//etc.
I would frown apon using prototype to add methods to a class as there could be performance issues
Here is a sample class structure you could use. JavaScript classes are not much different the functions.
function MyItem(){
this.d = '';
this.clear = function( ) {
this.d = '';
}
}
var myItem = new MyItem( )
myItem.d = "test";
alert(myItem.d);
myItem.clear();
alert(myItem.d)
Some good reading here
You should avoid using the new operator, everything is public. A better way to do what you want to do, and have private variables and functions is to do the following:
var slideshow = function () {
var self = {};
var private_param = "hello";
var private_func = function(say) {
alert(say);
};
var method = function() {
private_func(private_param);
};
var param = 500;
self.method = method;
self.param = param;
return self;
// return object, with the method func and param param publicly accessible
// private_param and private_func are not accessible to the outside
};
var presentation = slideshow(); // new slideshow, you could edit to pass in init params
presentation.method(); // hello
Here's what I'm trying to do -- this is pseudo code and doesn't work. Does anyone know how to accomplish this for real:
// Define the class
MyClass = Class.extend({});
// Store the class name in a string
var classNameString = 'MyClass';
// Instantiate the object using the class name string
var myObject = new classNameString();
Would it work if you did something like this:
var myObject = window[classNameString];
..?
Here's a more robust solution that will work with namespaced functions:
var stringToFunction = function(str) {
var arr = str.split(".");
var fn = (window || this);
for (var i = 0, len = arr.length; i < len; i++) {
fn = fn[arr[i]];
}
if (typeof fn !== "function") {
throw new Error("function not found");
}
return fn;
};
Example:
my = {};
my.namespaced = {};
(my.namespaced.MyClass = function() {
console.log("constructed");
}).prototype = {
do: function() {
console.log("doing");
}
};
var MyClass = stringToFunction("my.namespaced.MyClass");
var instance = new MyClass();
instance.do();
BTW: window is the reference to the global Object in browser JavaScript. Which is also this, and should work even in non-browser environments such as Node.js, Chrome extensions, transpiled code etc.
var obj = new this[classNameString]();
The limitation is that the class being called must be in the global context. If you want to apply the same to a scoped class you need to do:
var obj = (Function('return new ' + classNameString))()
However, there really is no reason to use a string. JavaScript functions are themselves objects, just like strings which are objects also.
Edit
Here is a better way to get the global scope that works in strict mode as well as non-browser JS environments:
var global;
try {
global = Function('return this')() || (42, eval)('this');
} catch(e) {
global = window;
}
// and then
var obj = new global[classNameString]
From: How to get the global object in JavaScript?
If MyClass is global, you can access it as a property of window object (assuming your code runs in a browser) using subscript notation.
var myObject = new window["MyClass"]();
If classNameString come from secure source you can use
var classNameString = 'MyClass';
var myObject = eval("new " + classNameString + "()");
This solution works with namespaces and is independent on platform (browser/server).
Browser global object is window and whenever you define global variables with var or functions with function, you are adding them in window.
Thus you can get your "class" definition there:
var args = [];
var className = 'MyClass';
var obj = new window[className](args);
But this won't work for ES6 class declarations
Classes declared using ES6 keyword class are per-standard treated differently.
A class declared with class MyClass { } defines a global class that does not become a property of window global object. In other words the following applies
class MyClass {};
typeof window.MyClass === 'undefined';
So, how to do the same with ES6 classes? Object access notation is required because is what is needed to parse the string name, but parent object to search in is no longer available.
One way is to create your own context object, declare there your class and search for it there. In code:
// this variable actually goes in `window`
var classes = {};
// declare class inside
classes.MyClass = class {
// the class code
};
var args = [];
var className = 'MyClass';
var obj = new classes[className](args); // dynamic for "new classes.MyClass(args)"
function myClass(arg){
}
var str="myClass";
dynamic_class=eval(str);
var instance=new dynamic_class(arg); // OK
Edit: inline example
function Person(name){
this.name=name;
}
var person1=new (eval("Person"))("joe");
Here is improved version of Yuriy's method that also handles objects.
var stringToObject = function(str, type) {
type = type || "object"; // can pass "function"
var arr = str.split(".");
var fn = (window || this);
for (var i = 0, len = arr.length; i < len; i++) {
fn = fn[arr[i]];
}
if (typeof fn !== type) {
throw new Error(type +" not found: " + str);
}
return fn;
};
In my case I was preloading data from my server and attempting to load the preloaded data using classes (in particular, I am using vuex-orm). Instead of anything super fancy, I opted for a list of models that I expect, and mapped them to classes that I had already imported at the top of the file. Observe:
import Video from '#models/Video'
import Purchase from '#models/Purchase'
let modelClassMap = {
'Video': Video,
'Purchase': Purchase,
}
Object.entries(preload.models).forEach(entry => {
const [modelName, modelData] = entry
if(modelClassMap[modelName]){
modelClassMap[modelName].insertOrUpdate({data: modelData})
}
})
Explicit, secure, simple. Nice!
On Firefox, there are security rules for extensions, and so for the Javascript console. Don't make the mistake I did to test in the console because none of those solutions work.
Whereas when you test from a page it works better :
the eval solution works well
Function('return new '... works (object is created) except for the constructor's arguments that are passed as "undefined"
I didn't test other solutions.