I created a function to return me a jquery element.
function GetDialogButton() {
return $('a.dialog');
};
This was done as the same element was used within multiple other functions. I thought it best if it was obtained from a single place, therefore making it easier to change in future should the atribute name change.
I would like to improve this getter so that it does not perform a search everytime when called multiple times within a single page load.
How can I do this? do I cache it? or perhaps there is no need as this is optimised out?
You can create a cache variable, but it will pollute the global namespace again
var dialogButton;
function GetDialogButton() {
if(dialogButton){
return dialogButton;
}
dialogButton = $('a.dialog');
return dialogButton;
};
Creating a global cache variable is not necessary. You can do it without adding a variable to the global scope. Something like this would do:
var GetDialogButton = (function() {
var set;
return function() {
if (set === undefined) {
set = $('a.dialog');
}
return set;
};
}());
Well, you could lazy-load it.
var $dialogButton = null;
function GetDialogButton() {
if($dialogButton == null)
$dialogButton = $('a.dialog');
return $dialogButton
};
Another alternative, if you expect there to only be one dialog button you could give the element an id and then the act of searching for it will be more efficient
<a id="dialogButton">...</a>
$('#dialogButton')... // nice and quick
You could keep the global namespace clean by;
function GetDialogButton() {
if (typeof GetDialogButton.element === 'undefined' ) {
GetDialogButton.element = $("a.dialog");
}
return GetDialogButton.element;
};
Related
I was tearing my hair out to get this done...particularly for an html5 detection script. I wanted a variable that is set only once and that can't be overwritten again. This is it:
var StaticConfiguration = {};
StaticConfiguration.Main = {
_html5: null
}
StaticConfiguration.getVariable = function(name) {
return StaticConfiguration.Main["_" + name];
}
StaticConfiguration.setVariable = function(name, value) {
if(StaticConfiguration.Main["_" + name] == null) {
StaticConfiguration.Main["_" + name] = value;
}
}
First, I define a global object StaticConfiguration containing all of these variables - in my case, just "html5". I set it to null, since I want to set it inside the application. To do so, I call
StaticConfiguration.setVariable("html5", "true");
It's set then. If I try to set it again, it fails - of course, since _html5 is not null anymore. So I practically use the underscore to "hide" the static variable.
This is helping me a lot. I hope it's a good approach - please tell me if not :)
First off, it's true, not "true" all strings (apart from the empty string) evaluate to true, including the string "false".
Second off, do you really need to protect data like this? There's not really any way to safely run a user's Javascript i your context anyway. There's always a way around protection like this. If offending code really cared, it could just replace the whole StaticConfiguration object anyway.
Matthew's code is a better approach to the problem, but it doesn't follow a singleton pattern, but is a class that needs to be instanciated. I'd do it more like this, if you wanted a single object with "static" variables.
StaticConfiguration = new (function()
{
var data = {}
this.setVariable = function(key, value)
{
if(typeof data[key] == 'undefined')
{
data[key] = value;
}
else
{
// Maybe a little error handling too...
throw new Error("Can't set static variable that's already defined!");
}
};
this.getVariable = function(key)
{
if (typeof data[key] == 'undefined')
{
// Maybe a little error handling too...
throw new Error("Can't get static variable that isn't defined!");
}
else
{
return data[key];
}
};
})();
Personal sidenote: I hate the "curly brackets on their own lines" formatting with a passion!
Take a look at Crockford's article on Private Members in JavaScript. You can do something like this:
var StaticConfiguration = (function() {
var html5; /* this is private, i.e. not visible outside this anonymous function */
return {
getVariable: function(name) {
...
},
setVariable: function(name, value) {
...
}
};
)();
How about:
var StaticConfiguration = new (function()
{
var data = {}
this.setVariable = function(key, value)
{
if(typeof data[key] == 'undefined')
{
data[key] = value;
}
};
this.getVariable = function(key)
{
return data[key];
};
})();
Similar to the other answer, but still allows arbitrary keys. This is truly private, unlike the underscore solution.
I'm a little curious as to why you think that you have to go to this extent to protect the data from being overwritten. If you're detecting the browser, shouldn't it only be done once? If someone's overwriting it with invalid data, then I would assume that it would be a problem in the client implementation and not the library code - does that make sense?
As a side note, I'm pretty big on the KISS principle, especially when it comes to client side scripting.
I know i'm a little late to the party but in situations like this i usually
var data;
if (data === undefined || //or some other value you expect it to start with{
data = "new static value"
};
I am wanting to attach into an Elements default property such as innerHTML as a backup under an object that way it does not pollute the Elements properties. so to help give an idea of what I am trying to achieve and what currently works:
Element.prototype._backupPropertyDescriptors = {};
Element.prototype._backupProperties = {};
Element.prototype._backupPropertyDescriptors._innerHTML = Object.getOwnPropertyDescriptor(Element.prototype,'innerHTML');
//This is what I want to do but loses Elements scope:
Object.defineProperty(Element.prototype._backupProperties,'_innerHTML',Element.prototype._backupPropertyDescriptors._innerHTML);
//the scope has changed from element to _backupProperties so this property fails.
//The working version:
Object.defineProperty(Element.prototype,'_innerHTML',Element.prototype._backupPropertyDescriptors._innerHTML);
//the reason for this is I want to be able to manipulate the get and set such as:
Object.defineProperty(Element.prototype._backupProperties,'_innerHTML',{configurable:true,enumerable:true,get:function(){console.log('getting',this.innerHTML);return this.innerHTML},set:function(val){console.log('doing something here before setting');this.innerHTML = val;}});
The problem with this is once it is inside of backup the this statement no longer holds the element...
I know one way to do this would be to use a bind or call but that still poses the how do I get the elements scope... as this during define property is the window..
So for anyone looking to try and do this, here is the solution I found :) might be something better out there, but this does work. requires only 3 properties in the prototype and then all others get put inside a single one.
Element.prototype._backupPropertyDescriptors = {};
Element.prototype._backupProperties = {};
Object.defineProperty(Element.prototype,'_backupProvider',
{
writeable:false,
enumerable:true,
configurable:true,
get:function()
{
var _backupProperties = this._backupProperties;
_backupProperties._Element = this;
return {_Element:this,_backupPropertyDescriptors:this._backupPropertyDescriptors,_backupProperties:_backupProperties};
}
});
//These first ones set up the main provider and property and descriptor holders.
//then just copy a descriptor:
Element.prototype._backupPropertyDescriptors._innerHTML = Object.getOwnPropertyDescriptor(Element.prototype,'innerHTML');
//and assign it to a new property inside the backupProperties:
Object.defineProperty(Element.prototype._backupProvider._backupProperties,'_innerHTML',
{
enumerable:true,
configurable:true,
get:function()
{
return this._Element._backupProvider._backupPropertyDescriptors._innerHTML.get.call(this._Element);
},
set:function(val)
{
console.log('setting html to: ',val);
this._Element._backupProvider._backupPropertyDescriptors._innerHTML.set.call(this._Element,val);
}
});
//and if you wanted to do something really crazy.... like overwrite the original..
Object.defineProperty(Element.prototype,'innerHTML',
{
enumerable:true,
configurable:true,
get:function()
{
return this._backupProvider._backupProperties._innerHTML;
},
set:function(val)
{
console.log('setting html to: ',val);
//do some crazy two way template binding here or something else crazy
this._backupProvider._backupProperties._innerHTML = val;
}
});
that is all.. thanks for the help #Bergi
I have some trouble with getting the name of a method stored in a variable... Here is an exemple of what I want :
Function MyObject(){
this.actualMethod = this.thatName;
}
MyObject.prototype.thatName = function(){}
MyObject.prototype.getActualMethodName = function(){
return this.actualMethod.name; /* This doesn't work since the function itself is anonymous, so this.actualMethod.name doesn't work... I want it to return "thatName" */
}
I tried to navigate through the prototype with my console, in vain... Is there a way to do this ?
You need to name the function:
MyObject.prototype.thatName = function thatName() {};
Or, as you mentioned, you can find its name in the prototype (but I wouldn't suggest you to do it):
for (var key in MyObject.prototype) {
if (MyObject.prototype[key] === this.actualMethod) {
return key;
}
}
But why do you need this? Maybe there could be a better solution.
I have the 'phone_dlg_manager' constructor function and its private methods show and init_country_code_combobox. The dialog reference is held in the phone_dlg variable. The show method triggers init_country_code_combobox and I have two options:
1) Explicitly pass the variable country_combobox that the init_country_code_combobox methods needs:
function phone_dlg_manager(ctx, open_dlg_button, edit_ctrl, item)
{
var phone_dlg;
show();
function show()
{
phone_dlg = ctx.application.ui.create_dialog(0, "PhoneEditorDlg");
init_country_code_combobox(phone_dlg.country);
read_to_dialog_controls(this._form_item);
phone_dlg.visible = true;
}
function init_country_code_combobox(country_combobox)
{
country_combobox.items.clear();
country_combobox.items.start_adding();
country_combobox.items.finish_adding();
}
}
2) Since phone_dlg is accessible withing init_country_code_combobox through closure, I can access the property that I need without explicitly passing the variable:
function phone_dlg_manager(ctx, open_dlg_button, edit_ctrl, item)
{
var phone_dlg;
show();
function show()
{
phone_dlg = ctx.application.ui.create_dialog(0, "PhoneEditorDlg");
init_country_code_combobox(phone_dlg.country);
read_to_dialog_controls(this._form_item);
phone_dlg.visible = true;
}
function init_country_code_combobox()
{
var country_combobox = phone_dlg.country;
country_combobox.items.clear();
country_combobox.items.start_adding();
country_combobox.items.finish_adding();
}
}
The second option seems easier to understand when reading code, however it makes the init_country_code_combobox function know more than it needs. Which option should I choose?
Thanks
This is mostly a matter of style. Option 1 is a little cleaner, and more extensible, since you can use init_country_code_combobox() to initialize more than just the one dialog. But if this is unlikely to be necessary, option 2 is not unreasonable.
I was tearing my hair out to get this done...particularly for an html5 detection script. I wanted a variable that is set only once and that can't be overwritten again. This is it:
var StaticConfiguration = {};
StaticConfiguration.Main = {
_html5: null
}
StaticConfiguration.getVariable = function(name) {
return StaticConfiguration.Main["_" + name];
}
StaticConfiguration.setVariable = function(name, value) {
if(StaticConfiguration.Main["_" + name] == null) {
StaticConfiguration.Main["_" + name] = value;
}
}
First, I define a global object StaticConfiguration containing all of these variables - in my case, just "html5". I set it to null, since I want to set it inside the application. To do so, I call
StaticConfiguration.setVariable("html5", "true");
It's set then. If I try to set it again, it fails - of course, since _html5 is not null anymore. So I practically use the underscore to "hide" the static variable.
This is helping me a lot. I hope it's a good approach - please tell me if not :)
First off, it's true, not "true" all strings (apart from the empty string) evaluate to true, including the string "false".
Second off, do you really need to protect data like this? There's not really any way to safely run a user's Javascript i your context anyway. There's always a way around protection like this. If offending code really cared, it could just replace the whole StaticConfiguration object anyway.
Matthew's code is a better approach to the problem, but it doesn't follow a singleton pattern, but is a class that needs to be instanciated. I'd do it more like this, if you wanted a single object with "static" variables.
StaticConfiguration = new (function()
{
var data = {}
this.setVariable = function(key, value)
{
if(typeof data[key] == 'undefined')
{
data[key] = value;
}
else
{
// Maybe a little error handling too...
throw new Error("Can't set static variable that's already defined!");
}
};
this.getVariable = function(key)
{
if (typeof data[key] == 'undefined')
{
// Maybe a little error handling too...
throw new Error("Can't get static variable that isn't defined!");
}
else
{
return data[key];
}
};
})();
Personal sidenote: I hate the "curly brackets on their own lines" formatting with a passion!
Take a look at Crockford's article on Private Members in JavaScript. You can do something like this:
var StaticConfiguration = (function() {
var html5; /* this is private, i.e. not visible outside this anonymous function */
return {
getVariable: function(name) {
...
},
setVariable: function(name, value) {
...
}
};
)();
How about:
var StaticConfiguration = new (function()
{
var data = {}
this.setVariable = function(key, value)
{
if(typeof data[key] == 'undefined')
{
data[key] = value;
}
};
this.getVariable = function(key)
{
return data[key];
};
})();
Similar to the other answer, but still allows arbitrary keys. This is truly private, unlike the underscore solution.
I'm a little curious as to why you think that you have to go to this extent to protect the data from being overwritten. If you're detecting the browser, shouldn't it only be done once? If someone's overwriting it with invalid data, then I would assume that it would be a problem in the client implementation and not the library code - does that make sense?
As a side note, I'm pretty big on the KISS principle, especially when it comes to client side scripting.
I know i'm a little late to the party but in situations like this i usually
var data;
if (data === undefined || //or some other value you expect it to start with{
data = "new static value"
};