I have a static inner function:
function A() {
}
A.B = function() {
}
I am not able to create function object of inner static function using:
new window['A.B'](); //Does not work
where as
new window['A'](); //Works
&
new A.B(); //Also works if I create inner static function object directly.
Does anyone know how to create static inner function object using window?
A.B is not the property of window. A is the property of window and B is the property of A. So if you really want to do this with brackets for some strange reason, it's new window['A']['B']().
If you want to access any property of an object while descending based on dots, that's not built-in.
function descend(obj, path) {
var parts = path.split('.');
for(var i = 0; i < parts.length; i++) {
obj = obj[parts[i]];
}
return obj;
}
// var B = descend(window, 'A.B');
// new B()
Related
var someObject = function(arg) {
this.property = function() {
// do something with the argument
return arg;
}();
};
var obj = new someObject(some argument);
// object.property instanceof "someObject" should be true
When property of someObject is used, a new instance of newObject should be created. For example, when I use the native DOM Element's nextSibling property, a new DOM Element object instance is returned. I wonder if it is possible to create a similar structure. Or would such cause infinite recursion?
Strictly speaking, this is possible in ES5 (all latest browsers, yes that includes IE).
ES5 specifies getters and setters via the get and set keyword or the Object.defineProperty function so you can make functions behave like properties (think innerHTML). Here's how you can do it:
function Mother () {
this.name = '';
Object.defineproperty(this,'child',{
get: function(){
return new Mother();
}
});
}
So the object can now create new instances of itself simply by reading the child property:
var a = new Mother();
a.name = 'Alice';
b = a.child;
b.name = 'Susan';
alert(a.name) // Alice
alert(b.name) // Susan
a instanceof Mother; // true
b instanceof Mother; // true
Having said that, your observation about DOM elements is wrong. The DOM is simply a tree structure. You can create a similar structure yourself using old-school javascript:
function MyObject () {}
var a = new MyObject();
var b = new MyObject();
var c = new MyObject();
a.children = [b,c];
b.nextSibling = c;
c.prevSibling = b;
// now it works like the DOM:
b.nextSibling; // returns c
a.children[1]; // returns c
b.nextSibling.prevSibling instanceof MyObject; // true
No, that's not possible. You could set function to the property, but anyway, you will need to invoke function somehow (with property() notation or with call/apply), because function it's an object itself, and only () or call/apply say to interpreter that you want to execute code, but not only get access to function's object data.
Your understanding of the nextSibling property in the DOM is incorrect. It does not create a new DOMElement, it simply references an existing DOM Node.
When you create a sibling of an element to which you have a reference (e.g., via jQuery or document.createElement), the browser knows to update sibling and parent/child references.
So, the behavior you're trying to emulate doesn't even exist.
As others have intimated, simply accessing a property on an object is not sufficient to get the Javascript interpreter to "do" anything (other than deference the name you're looking up). You'll need property to be a function.
nextSibling doesn't return a new element, it returns an existing element which is the next sibling of the target element.
You can store an object reference as a property of another object just like you can store primitive values.
function SomeObject(obj) {
this.obj = obj;
}
var someObject = new SomeObject(new SomeObject());
someObject.obj instanceof SomeObject //true
However if you want to create a new instance of SomeObject dynamically when accessing someObject.obj or you want to return an existing object based on conditions that shoul be re-evaluated every time the property is accessed, you will need to use a function or an accessor.
function SomeObject(obj) {
this.obj = obj;
}
SomeObject.prototype.clone = function () {
//using this.constructor is a DRY way of accessing the current object constructor
//instead of writing new SomeObject(...)
return new this.constructor(this.obj);
};
var someObject = new SomeObject(new SomeObject());
var someObjectClone = someObject.clone();
Finally with accessors (be aware that they aren't cross-browser and cannot be shimmed)
function SequentialObj(num) {
this.num = num;
}
Object.defineProperty(SequentialObj.prototype, 'next', {
get: function () {
return new this.constructor(this.num + 1);
},
configurable: false
});
var seq = new SequentialObj(0);
console.log(seq.next); //SequentialObj {num: 1}
console.log(seq.next.next.next); //SequentialObj {num: 3}
If you want this.property() to return a new someObject you can write the class as follows:
var someObject = function(arg) {
this.arg = arg;
};
someObject.prototype.property = function(arg) {
// do something with the argument
return new someObject(arg||this.arg);
}();
var obj = new someObject(/*some argument*/);
// object.property instanceof "someObject" should be true
If you want it to return some already instantiated version you can write the code as follows:
var someObject = (function() {
var previous;
function(arg) {
this.arg = arg;
this.propertyBefore = previous;//refers to the someObject created before this one
if(previous) previous.property = this; //before.property now references this class
//this.property will be undefined until another instance of someObject is created
previous = this;
};
})()
var obj = new someObject(/*some argument*/);// returns someObject already created earlier (similar to nextSibling)
One small note - its best practice in javascript to declare class names with a capitalized name (SomeObject rather than someObject)
I'm trying to get the instance name of my class.
The way I do this is I loop through all global objects and compare it with the this pointer.
It works in Chrome and FF, but in IE, it doesn't. The problem seems to be the global variables don't seem to be in window.
How can I loop through the global variables in IE ?
PS: I know it only works as long as there is only one instance, and I don't want to pass the instance's name as a parameter.
function myClass()
{
this.myName = function ()
{
// search through the global object for a name that resolves to this object
for (var name in this.global)
{
if (this.global[name] == this)
return name
}
}
}
function myClass_chrome()
{
this.myName = function ()
{
// search through the global object for a name that resolves to this object
for (var name in window)
{
if (window[name] == this)
return name ;
}
} ;
}
// store the global object, which can be referred to as this at the top level, in a
// property on our prototype, so we can refer to it in our object's methods
myClass.prototype.global = this
//myClass_IE.prototype.global = this
// create a global variable referring to an object
// var myVar = new myClass()
var myVar = new myClass_chrome()
//var myVar = new myClass_IE()
alert(myVar.myName() );// returns "myVar"
Better idea, solved:
function myClass_IE()
{
this.myName = function ()
{
// search through the global object for a name that resolves to this object
for (var i = 0; i < document.scripts.length; i++)
{
var src = document.scripts[i].innerHTML ;
//document.write('script ' + i + ' = ' + document.scripts[i].innerHTML )
var idents = src.replace(/\W/g, ' ').replace(/(function|if|for|while|true|false|null|typeof|var|new|try|catch|return|prototype|this)/g, '').split(' ');
for(var j = 0; j < idents.length; j++)
{
//var iden = String(idents[j]).trim();
var iden = String(idents[j]);
if (window[iden] == this)
{
// http://mcarthurgfx.com/blog/article/iterating-global-variables-in-internet-explorer
// http://blog.stevenlevithan.com/archives/faster-trim-javascript
return iden;
}
}
}
}
}
function myClass()
{
this.myName = function ()
{
// search through the global object for a name that resolves to this object
for (var name in this.global)
{
if (this.global[name] == this)
return name
}
}
}
function myClass_chrome()
{
this.myName = function ()
{
// search through the global object for a name that resolves to this object
for (var name in window)
{
if (window[name] == this)
return name ;
}
} ;
}
// store the global object, which can be referred to as this at the top level, in a
// property on our prototype, so we can refer to it in our object's methods
myClass.prototype.global = this
//myClass_IE.prototype.global = this
// create a global variable referring to an object
// var myVar = new myClass()
//var myVar = new myClass_chrome()
var myVar = new myClass_IE()
alert(myVar.myName() );// returns "myVar"
In IE, it global variables aren't enumerable unless you explicitly define them as properties of the window object.
var noEnum = true; // won't show up in a for...in loop
window.willEnum = true; // will show up in a for...in loop
Clearly, you found your own solution but it will work for inlined scripts only - although this could be extended to external scripts using ajax to fetch the contents from the cache (or from the server if they're not cached).
I do not understand quite completely how to apply constructors on this object creation method:
var MyObject = {
...
};
I know that you can do:
var MyObject = new Object();
MyObject.prototype.constructor = function(props)
{
...
}
or...
function MyObject(prop1, prop2)
{
this.prop1 = prop1;
...
}
Can I do something like this?
var MyObject = {
MyObject: function(prop1, prop2)
{
...
}
}
No, you can't, that would simply create a (static) method on MyObject -- MyObject.MyObject. In JavaScript, a constructor is the class. Class methods and properties are created either inside the constructor using this. or by adding to the prototype (outside of the constructor) using MyClass.prototype.. You can think of "objects" in JavaScript as static classes.
Example from here
Creating constructors
To write your own constructors, you use the this keyword within the constructor to refer to the newly-created object. The constructor initializes the object.
In the example below:
The make7Table constructor creates a multiplication table for number 7
The size property is introduced to keep track of the number of elements
The value of each element is initialized
function make7Table(numElements)
{
this.size = numElements;
var cnt;
for(cnt = 0; cnt < numElements; cnt++)
{
this[cnt] = cnt*7;
}
}
// Use the constructor to create and initialize an array.
myArray = new make7Table(10);
document.write(myArray[5]);
document.write("This table has " + myArray.size + " elements");
To run the code, paste it into JavaScript Editor, and click the Execute button. myArray[5] retrieves the element with the value of 5*7 = 35.
var MyObject = new Object();
MyObject.prototype.constructor = function(props)
{
...
}
is the same as
var MyObject = {};
MyObject.prototype.constructor = function(props)
{
...
}
Is something like this possible:
function FooClass()
{
var barsArray=new Array();
var index=0;
function addBar()
{
barsArray[index]=new BarClass(index);
}
}
function BarClass()
{
var myIndex;
function BarClass(index)
{
myIndex=index;
}
}
I'm inferring that you want to have a Foo object, and that Foo object should contain an array of Bar objects. Each Bar object should know its array index within its parent Foo object.
If that is an accurate description, then try this:
function BarClass(idx) {
this.myIndex = idx;
}
function FooClass(howMany) {
this.barsArray = [];
for (var x = 0; x < howMany; x++) {
this.barsArray[x] = new BarClass(x);
}
}
var foo = new FooClass(5);
// foo.barsArray[0].myIndex === 0
// foo.barsArray[1].myIndex === 1
// foo.barsArray[2].myIndex === 2
// foo.barsArray[3].myIndex === 3
// foo.barsArray[4].myIndex === 4
// foo.constructor === 'FooClass'
// foo.barsArray[0].constructor === 'BarClass'
Good luck!
Not quite (actually it compiles, but probably doesn't do what you intended).
I'm assuming you want to create a FooClass class with an addBar method that appends a BarClass object to it's barsArray member.
The addBar function is just a local variable inside the FooClass function/constructor. To make it accessible from outside the constructor, you need to assign it to this.addBar. Other than remembering to increment index, that's all you would need to change in FooClass.
For the BarClass class, remember that a "class" is really just a constructor function. You don't need to (and can't) a separate constructor. BarClass would just be a single function that takes an index and assigns it to this.myIndex.
function FooClass()
{
// use this.barsArray = [], etc if you want them to be publically available
var barsArray=[]; // It's usually better to use "[]" instead of "new Array()"
var index=0;
this.addBar = function() {
barsArray[index]=new BarClass(index);
index++;
}
}
function BarClass(index)
{
this.myIndex=index;
}
If you change barsArray and index to be properties instead of local variables ("this.barsArray = [];" and "this.index = 0"), you can put addBar in FooClass.prototype and there will only be one instance of the function:
function FooClass()
{
this.barsArray=[];
this.index=0;
}
FooClass.prototype.addBar = function() {
this.barsArray[this.index]=new BarClass(this.index);
this.index++;
};
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.