ORIGINAL QUESTION:
i am studying js and i would like to know if there is any (useful) difference between these two ways of protecting the global scope other than the fact that the second one can be reused (called again).
option1:
var question = {};
(function(){
question.option1 = function() {
// some code
};
})();
option2:
var question = {};
question.option2 = function () {
//some code
};
question.option();
thanks!
EDIT 1:
thank you #luisperezphd. is there any difference between what you wrote and this (besides verbosity)?
var question = {};
question.option3 = {};
question.option3.privateVar = 0;
question.option3.testing = function () {
question.option3.privateVar++;
// some code
};
question.option3.testing();
EDIT 2:
thank you lanston and luisperezphd! i did not realize that question.option3.privateVar was available in the global.
is there any difference between this:
var question = {};
(function(){
var privateVar = "some value";
question.option4 = function(){
alert(privateVar);
}
})();
question.option4();
and this:
var question = {};
question.option5 = function() {
var privateVar = "some value";
var someFunction = function() {
alert(privateVar);
}
return someFunction;
}
question.option5()();
?
There is no difference in the example you gave, but there is a difference you haven't exploited. In the first example, you can utilize variables and not pollute the global namespace or the object namespace. The equivalent of a private field in most object orientated languages. You would do that like so:
var question = {};
(function(){
var PrivateVariable = 0;
question.option1 = function() {
PrivateVariable++;
// some code
};
})();
alert(question.PrivateVariable); // returns 'undefined'
The reason the code above returns undefined is because PrivateVariable is not a field in question. But functions in question can access PrivateVariable. This is truly a private variable.
On the other hand, if you wrote it like this:
var question = {};
question.PrivateVariable = 0;
question.option1 = function() {
question.PrivateVariable++;
// some code
};
alert(question.PrivateVariable); // returns 0
In this second case, PrivateVariable is in fact not private and publicly accessible.
Incidentally, you normally wouldn't reference question from inside a function that belongs you question. Instead, you would use the this keyword, like so:
var question = {};
question.PrivateVariable = 0;
question.option1 = function() {
this.PrivateVariable++;
// some code
};
But this would only work on public variables. It makes it more clear what is going on. Also, in some cases, it makes maintaining the code easier in that if you change the name of the variable from question you wouldn't have to change references to it inside the function. There are other benefits, but I don't know if I should get into it here.
#svdsvd,It's quite different between liusperezphd's and yours, the PrivateVariable,you can't get it in the global,but you can get your question.option3.privateVar in the global
Related
If I have:
name.sub = function() {
var sub = {};
var placeholder = "test"
var test = function() {
return 42;
};
// Desired code would be here
return sub;
};
I want to use a placeholder to access the variable so that I get 42.
Something like
window["name"]["sub"][placeholder] is seemingly looking for name.sub.test.
The only answers I found were if it was a global variable.
Using eval would work, but I've heard it should be avoided where possible.
placeholder = "test";
console.log(eval(placeholder + '()'))
// Would return 42
My actual end goal is to have an associative array where:
console.log(array[placeholder]);
// Would return 42
Any help would be appreciated.
This is what I ended up using for anyone interested:
name.sub= function() {
var sub = {};
var placeholder = "test"
var test = function() {
return 42;
var newObj = {};
newObj["test"] = function() {test()}
console.log(newObj[placeholder]())
// Should return 42
};
You can't access variables inside a function from outside said function.
Instead, you could do this:
name.sub = function(placeholder) {
var functions = {
"test": function() {
return 42;
},
};
return functions[placeholder]();
};
name.sub("test"); // 42
I'm not sure if that's what you're looking for but hopefully it is. Explain more?
You can't access local variables inside a function the same way you can access properties of window in global scope.
kangax wrote an interesting article about Understanding delete [in JavaScript], which includes an explanation of what Activation objects are - which I think is what you're looking for.
I suggest you read the entire article, but long story short:
Inside functions, declared variables (and functions) are added as properties to the activation object of the current scope, as they get added to window in the global scope.
But unlike window:
Note that Activation object is an internal mechanism and is never really accessible by program code.
Conclusion: What you're asking is not (currently) possible.
Your options are limited to:
Using an intermediate object:
name.sub = function() {
var sub = {};
var placeholder = "test";
var array = {};
array.test = function() {
return 42;
};
console.log(array[placeholder]());
return sub;
};
Using eval, exactly like you suggested:
name.sub = function() {
var sub = {};
var placeholder = "test";
var test = function() {
return 42;
};
console.log(eval(placeholder + '()'));
return sub;
};
Using window, by removing var from test's declaration:
name.sub = function() {
var sub = {};
var placeholder = "test";
test = function() {
return 42;
};
console.log(window[placeholder]());
return sub;
};
I suggest the first option for the sake of performance over eval, for compatibility over window (might collide with other code), and simply because of personal taste and what I consider good practice.
By what i understand of your question,
its seem like you are looking for a key/value pair to a JavaScript object literal,
window.name is reserved btw: https://developer.mozilla.org/en-US/docs/Web/API/Window/name
var sub = {
'test': function() {
return 42;
},
'test2': 42;
}
sub['test']();
sub['test2'];
add by
Using dot notation:
sub.test3 = "value3";
Using square bracket notation:
sub["test4"] = "value4";
Maybe im thinking to simple , but seems like this is what you are looking for
var steve = function() {
var bob = {};
bob.WayCoolTest = function () {console.log('done deal');};
return bob;
}
window["steve"]["WayCoolTest"]
running this in chrome console, my test app, anywhere results with undefined. I do not understand why, can someone explain why this does not work and help me fix it. Thank you very much!!
Using window is usually redundant - let me demonstrate:
var foo = '123';
alert(foo); //123
alert(window.foo) //123
alert(window['foo']) //123
It is evident which is more convenient. There is a circumstance in which the use of window makes sense, but that circumstance is almost always because of poor architecture. Using window allows us to access a variable global variable - funny wording :) This should clear it up:
var foo = '123';
var bar = 'abc';
var prop;
if (blah) { //some condition here
prop = 'foo';
}
else {
prop = 'bar';
}
Now...how can we use prop to get the value of a corresponding variable?
console.log(window[prop]); //123 or abc - bracket notation lets us use variable property names
This sort of thing is very common within objects, but not with window. The reason is that we should be avoiding global variables (properties of window) as much as possible. Therefore, any logic that needs a variable property name should be inside of an object, dealing with objects - NOT window. Now window can be out of the picture.
It is usually bad practice to create functions inside of other functions. That would mean that each time you call function A, you recreate function B. Most of the time, people do that because they don't know better, not because they need to.
It appears to me that you intended to give steve a property called WayCoolTest, both being functions. That can be done.
var steve = function() {
console.log("I'm Steve!");
}
steve.WayCoolTest = function() {
console.log("I'm a Way Cool Test!");
}
steve(); //I'm Steve!
steve.WayCoolTest(); //I'm a Way Cool Test!
This works because functions are objects in javascript. Therefore, you can add properties to them.
Let's step it up!
To emphasize good practices, I'm going to wrap this example in an object testApp (it acts like a namespace..we're using that instead of window).
I will create a property of testApp called steve, which will be a function.
Rather than creating steve directly as a function, I will use an IIFE (immediately invoked function expression), which is a function that will return something to be set as steve.
Inside the IIFE, I will create the function for steve and also attach WayCoolTest to it, as demonstrated in the previous example, then that function is returned and assigned to steve.
var testApp = {
steve : (function() {
var steve = function() { //the name here doesn't matter, just being consistent, since this will be the value of the property `steve`.
console.log("I'm Steve!");
}
steve.WayCoolTest = function() {
console.log("I'm a Way Cool Test!");
}
return steve;
}());
};
testApp.steve(); //I'm Steve;
testApp.steve.WayCoolTest(); //I'm a Way Cool Test!
Now, let's consider another variation.
var testApp = {
steve : (function() {
var steve = function() { //the name here doesn't matter, just being consistent, since this will be the value of the property `steve`.
console.log("I'm Steve!");
WayCoolTest(); //Steve can use this, but nothing else can! Encapsulation.
}
var WayCoolTest = function() { //THIS PART!! No longer a property of "steve"
console.log("I'm a Way Cool Test!");
}
return steve;
}());
};
testApp.steve(); //I'm Steve! I'm a Way Cool Test
testApp.steve.WayCoolTest(); //undefined, of course
That is very useful if for example steve is a complicated function and you want to break it up into some other small functions that only steve will know about.
To complement the other answers, your code would work this way:
var steve = function() {
var bob = {};
bob.WayCoolTest = function () {console.log('done deal');};
return bob;
}
window["steve"]()["WayCoolTest"]();
or
var steve = (function() {
var bob = {};
bob.WayCoolTest = function () {console.log('done deal');};
return bob;
})();
window["steve"]["WayCoolTest"]();
or, the dirtiest way...
steve=(function() {
var bob = {};
bob.__defineGetter__("WayCoolTest", function () {console.log('done deal');});
return bob;
})();
window["steve"]["WayCoolTest"];
Assuming steve is in the global scope and you dont seem to use steve as a constructor you can use immediate function and return the new object that you have created inside of it.
var steve = (function () {
var bob = {};
bob.WayCoolTest = function () {
console.log('done deal');
};
return bob;
})();
window["steve"]["WayCoolTest"]();
If it is in a closure then you would have to either remove var for hoisting to happen so it becomes a part of window or set it to the window object as a property.
window.steve = (function () {
var bob = {};
bob.WayCoolTest = function () {
console.log('done deal');
};
return bob;
})();
If you declare using var it doesn't get associated with the global window scope. It's instead a local variable. Also bob is not a property on the steve object they way you have it set up
For some time now, I've been using the design pattern of objectOne, shown below. I cannot remember where I picked it up. I tried to find it but couldn't. Perhaps its some hybrid of things I read about. Today I discovered that it is very flawed, since this is resolving to the window object, making all public methods global. I was under the impression that when this is used within a function, it would refer to the function itself, as opposed to the global window object. I guess this is not the case? Could someone explain something that I'm missing or point me to a resource that explains it? I'm also interested in either fixing this pattern or finding a similar one that doesn't have this problem with global method names. I suppose if I would use a variable other than this, perhaps fn, and i return that, then it would fix things. Thanks in advance for any help with this question, sorry its sort of vague.
JS Fiddle:
http://jsfiddle.net/nLL8y/3/
myapp = {};
myapp.objectOne = function() {
var that = this,
p = {};
this.public = function() {
console.log(this);
};
p.private = function() {};
return this;
}();
myapp.objectTwo = {
public: function() {
console.log(this);
},
notPrivate: function() {}
};
myapp.objectThree = function() {
var fn = {},
p = {};
fn.public = function() {
console.log(this);
};
p.private = function() {};
return fn;
}();
//creates global functions
myapp.objectOne.public();
//doesn't allow private
myapp.objectTwo.public();
//seems to work
myapp.objectThree.public();
myapp is used as a namespace in your example. objectOne and objectTwo are constructors so they should start with capital letter. But your biggest problem is using methods directly as opposed to creating objects:
var myapp = {};
myapp.ObjectOne = function() {
this.public = function() {
console.log(this);
};
var private = function() {};
};
myapp.ObjectTwo = function() {
this.public = function() {
console.log(this);
},
this.notPrivate = function() {}
};
var o1 = new myapp.ObjectOne();
o1.public();
var o2 = new myapp.ObjectTwo();
o2.public();
Example 1
var Reptile = function () {
var reptile = this;
this.showBla = function() {
alert(reptile.bla);
}
}
var turtle = new Reptile();
turtle.bla = 'whatever';
turtle.showBla();
Example 2
var Reptile = function () {
this.showBla = function() {
alert(this.bla);
}
}
var turtle = new Reptile();
turtle.bla = 'whatever';
turtle.showBla();
Is example 1 legit? As it sometimes seems to screw things over to define "this" directly in the constructor...?!?
Yes, it is legit and is useful in cases where you may need to define a function inside a function that may be invoked in a way there "this" will pointer to something else. Books recommend naming this variable var that = this;
Example 1 is a common pattern for maintaining the reference to the current instance. In a callback situation, like:
setTimeout(turtle.showBla, 0);
Example 1's var reptile... saves the this reference and will show 'whatever'. Example 2 will show undefined, unless you manually assign scope on the calling side (e.g., in jQuery):
setTimeout($.proxy(turtle.showBla, turtle), 0);
I have the following JS code:
var Item = function ()
{
this.property = '';
this.myfunction = function ()
{
var value = this.property;
};
};
however, this does not point to the defining class so value doesn't get anything.
how do I access this.property from inside my function?
You need to create a closure which captures the value of parent scope's this:
var Item = function ()
{
this.property = '';
var self = this;
this.myfunction = function ()
{
var value = self.property;
};
};
Update: As others have pointed out, this closure is not needed when Item is used as a constructor (new Item()). Noting it here for future reference.
just create an alias for this. It will get closure'd.
var Item = function ()
{
this.property = '';
var self = this;
this.myfunction = function ()
{
var value = self.property;
};
};
Your code works as is if you call Item() as a constructor.
var item = new Item();
item.property = "the property";
item.myfunction(); // value = "the property"
this changes depending on the context. The context being how a function was invoked, not how it was defined, but how it was called.
Besides that, you seem to be mixing up two patterns here. I'm sure you meant something like:
var Item = function() {
this.property = '';
};
Item.prototype.myfunction = function() {
var value = this.property;
};
Instead you kind of mixed a closure pattern with prototypal, which doesn't seem very useful there. Closure is good for hiding members, allowing for true private members, but here you're exposing the property anyway. There's no reason not to stick that function on the prototype.
Do yourself a favor and ignore any concepts you have of more traditional OO, they won't do you any good here. Prototypal isn't nearly the same thing.