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).
Related
I created a class in JavaScript as follows:
class TreeMatching
{
constructor()
{
this.thresholdPoints=0;
this.neighborWeight = 0.4;
this.totalFrequency = 0.0;
this.listSeq = [];
this.listFreq = [];
this.mapScore = new Object();
this.tree = new Trie();
}
createTree()
{
var list_Dictionary;
var loadWordList = $.get("../wordFrequencyTop5000.txt", function(data)
{
list_Dictionary = data.split("\n");
});
loadWordList.done(function()
{
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
this.tree.insert(string); //<-- Cannot read property 'insert' of undefined
}
});
}
}
which is supposed to call the insert method in class Trie as follows:
class Trie
{
constructor()
{
this.count=1;
this.root = new TrieNode();
}
insert(word)
{
var children = new Object();
for(var i=0; i<word.length(); i++){
var c = word.charAt(i);
var t;
if(children[c]){
t = children[c];
}else{
t = new TrieNode(c);
children.put(c, t);
}
children = t.children;
//set leaf node
if(i==word.length()-1)
t.isLeaf = true;
}
}
}
However, the line of code where the error is marked, the outer function's this value, is not having properties tree, mapScore, etc.
Is there a way that I can access those values from the inner callback function?
Thanks
look at 'this' - you will have to define local variable to maintain reference to "this" inside the call, as described in the link.
createTree()
{
var self = this;
var list_Dictionary;
var loadWordList = $.get("../wordFrequencyTop5000.txt", function(data)
{
list_Dictionary = data.split("\n");
});
loadWordList.done(function()
{
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
self.tree.insert(string); //<-- Now you should be able to do it
}
});
}
'this' in the inner anonymous has different scope. Try to use the advantage of closer in JS which will get access to the function caller scope.
var that = this;
loadWordList.done(function() {
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
that.tree.insert(string); // 'that' will hold 'this' in the right scope
}
});
The anonymous function inside loadWordlist.done creates a new scope with an new context.
if you want to keep the old context you can use the ES2015 arrow function:
loadWordList.done(() => {
//code here
);
or make a var inside createTree() like this:
var that = this;
and then inside the loadWordList callback you can refer to the right context using:
that.tree.insert(string);
I personally prefer the arrow function because 'that' is a lousy choice for a var name. And since your using the ES2015 classes browser support must not be an issue.
I am following a tutorial to create getter and setter in Javascript, I have the code like this:
// Create a new User object that accept an object of properties
function User(properties) {
// Iterate through the properties of the object, and make
// sure it's properly scoped
for (var i in properties) { (function(){
// Create a new getter for the property
this['get' + i] = function() {
return properties[i];
};
// Create a new setter for the property
this['set' + i] = function(val) {
properties[i] = val;
};
})(); }
}
// Create a new User object instance and pass in an object of
// properties to seed it with
var user = new User({
name: 'Bob',
age: 28
});
// Just note that the name property does not exist, as it's private
// within the property object
console.log(user.name == null);
// However, we are able to access its value using the new getname()
// method, that was dynamically generated
console.log(user.getname());
However, console shows error saying user does not have method getname. The code is trying to dynamically generate getter and setter method, it looks ok to me. Any thoughts?
The other answers are correct in that you need to pass i into your anonymous function, but you could also do this with ES5 Getters and Setters:
// Create a new User object that accept an object of properties
function User(properties) {
var self = this; // make sure we can access this inside our anon function
for (var i in properties) {
(function(i) {
Object.defineProperty(self, i, {
// Create a new getter for the property
get: function () {
return properties[i];
},
// Create a new setter for the property
set: function (val) {
properties[i] = val;
}
})
})(i);
}
}
The benefit of using ES5 getters and setters is that now you can do this:
var user = new User({name: 'Bob'});
user.name; // Bob
user.name = 'Dan';
user.name; // Dan
Since they're functions, they modify the passed in properties, not just the object itself. You don't have to use getN or setN anymore, you can just use .N, which makes using it look more like accessing properties on an object.
This approach, however, isn't universally portable (requires IE 9+).
Here's what I'd probably do in practice though:
function User(properties) {
Object.keys(properties).forEach(function (prop) {
Object.defineProperty(this, prop, {
// Create a new getter for the property
get: function () {
return properties[prop];
},
// Create a new setter for the property
set: function (val) {
properties[prop] = val;
}
})
}, this);
}
The above gets rid of your for loop. You're already using an anonymous function, so might as well get the most of it.
Probably a closure issue:
function User(properties) {
// Iterate through the properties of the object, and make
// sure it's properly scoped
for (var i in properties) {
(function(i){
// Create a new getter for the property
this['get' + i] = function() {
return properties[i];
};
// Create a new setter for the property
this['set' + i] = function(val) {
properties[i] = val;
};
}.call(this, i));
}
}
Also, as #Paul pointed out, this was actually referring to the function which was contained in the for loop. Not the User function. I fixed that by using a call and assigning the User this to the function (no need for extra variable).
Your in-loop function is losing the this, do a var t = this; outside loop and refer to t inside. Also, pass i into your function.
function User(properties) {
var t = this, i;
for (i in properties) (function (i) {
t['get' + i] = function () { return properties[i]; };
t['set' + i] = function (val) { properties[i] = val; };
}(i));
}
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()
Am I doing something wrong or is this just not possible:
(function(namespace,undefined)
{
//Private properties and methods
var foo="bar";
function test(){return foo;}
//Public properties and methods
namespace.foobar=foo+"123";
namespace.showFoo=function(){return test();};
})(window.namespace=window.namespace || {});
Then I try to "extend" the above namespace and add a new method:
(function(namespace,undefined)
{
//Public method
namespace.sayGoodbye=function()
{
alert(namespace.foo);
alert(namespace.bar);
alert(test());
}
})(window.namespace=window.namespace || {});
The alert shows undefined for the properties and throws an error for the test() method.
Thanks.
Why would you expect to have foo and bar available ? Those identifiers are never assigned to your namespace object anywhere.
Any variable that is declared with var is only available in the Function(-Context) of the current Activation/Variable Object. Same goes for function declarations, in your case, test(). Both these are only stored within the AO from the first anonymous function and are not stored within your namespace object. You would have to explicitly assign the values
namespace.foo = foo;
namespace.bar = "hello I am bar";
You have several bugs in your code. That code is working. Example.
(function(namespace)
{
if(namespace === undefined) {
window.namespace = namespace = {};
}
//Private properties and methods
var foo="bar";
function test(){return foo;}
//Public properties and methods
namespace.foobar=foo+"123";
namespace.showFoo=function(){return test();};
})(window.namespace);
(function(namespace)
{
if(namespace === undefined) {
window.namespace = namespace = {};
}
//Public method
namespace.sayGoodbye=function()
{
alert(namespace.foobar);
alert(namespace.showFoo());
}
})(window.namespace);
window.namespace.sayGoodbye();
Bugs:
1. You never set the variable window.namespace.
2. If you declare variables/functions in a private way in a function then only this specific function can access these variables/functions.
If you want to use a namespace you can do it like this:
var namespace = (function(){
var private = "private";
function privateFunc() {
return private;
}
return {
"publicFunc": function(){return privateFunc()}
}
})();
namespace.publicFunc() === "private";
//alert(namespace.publicFunc());
// extend namespace
(function(namespace){
var private = "other private";
namespace.newFunc = function(){return private};
})(namespace);
namespace.newFunc() === "other private";
//alert(namespace.newFunc());
Namespaces declaration and extending of namespaces:
var namespace = function(str, root) {
var chunks = str.split('.');
if(!root)
root = window;
var current = root;
for(var i = 0; i < chunks.length; i++) {
if (!current.hasOwnProperty(chunks[i]))
current[chunks[i]] = {};
current = current[chunks[i]];
}
return current;
};
// ----- USAGE ------
namespace('ivar.util.array');
ivar.util.array.foo = 'bar';
alert(ivar.util.array.foo);
namespace('string', ivar.util); //or namespace('ivar.util.string');
ivar.util.string.foo = 'baz';
alert(ivar.util.string.foo);
Try it out: http://jsfiddle.net/stamat/Kb5xY/
Blog post: http://stamat.wordpress.com/2013/04/12/javascript-elegant-namespace-declaration/
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++;
};