I'm having a problem with the scope of a public variable in javascript. The variable is declared in the main function (function Level) of my javascript class. The loadXML function is called from outside the class, but knows the this.layers variable. When my xml is loaded and redirected to another function the this.layers variable suddenly is undefined. Anyone having experience with this kind of problem.
var Level = (function()
{
function Level()
{
this.layers = 3;
}
Level.prototype.loadXML = function()
{
console.log(this.layers); //variable is defined!
$.get("xml/level_" + this.currentLevel + ".xml", Level.buildGrid);
};
Level.buildGrid = function(xml)
{
console.log(this.layers); //variable is undefined!
};
return Level;
})();
Thanks in advance.
Return a new function from buildGrid that will be passed as jQuery's callback and pass to the wrapped function the current level so that you can get informations from the argument passed. The buildGrid function is so private to the Level's closure and can be accessed only inside it.
var Level = (function () {
var buildGrid = function (level) {
return function(xml) {
console.log(xml);
console.log(level.layers);
};
};
function Level() {
this.layers = 3;
}
Level.prototype.loadXML = function () {
console.log(this.layers); //variable is defined!
$.get("xml/level_" + this.currentLevel + ".xml", buildGrid(this));
};
return Level;
})();
this.layers only exists within the scope of level which is a constructur.
try the following:
var t = new Level.Level()
t.layers
Related
I have read this answer and IIFE but I can't seem to find the correct solution to my problem.
I have a simple class here:
define(['jquery'], function($) {
// Need 'self' because someCallback() is being called with .call() and 'this' changes
var self;
function Foo(number) {
self = this;
this.someNumber = number;
}
Foo.prototype = {
someCallback: function () {
//Use self because 'this' changes to a DOM element
var num = self.someNumber;
//Do something with the num
return num * 2;
}
};
return Foo;
});
and someCallBack() is being called by a jQuery plugin using .call(). Because of this, the context changed, hence the use of the self variable.
However, this is wrong because:
define(['foo'], function(Foo) {
describe('context question', function () {
var foo1 = new Foo(1);
var foo2 = new Foo(2);
it('"this" should work', function () {
var call1 = foo1.someCallback.call(this); // 4
var call2 = foo2.someCallback.call(this); // 4
expect(call2).toBe(4); // Only works because it is 'new' last
expect(call1).toBe(2); // Fails because 'self' is taken from foo2
});
});
});
How exactly should I wrap the self variable to make this code work?
You could probably just use the revealing module pattern and declare it as a "global" variable (local to the module):
define(['jquery'], function($) {
var someNumber;
function Foo(number) {
someNumber = number;
}
Foo.prototype = {
someCallback: function () {
return someNumber * 2;
}
};
return Foo;
});
Two ways of calling an object method which stores its own this value include
Define the method as a nested function which references its this value in a closure which stores this value in a variable. The function defined could be anonymous or declared with a name but must be evaluated each time a class instance is created, so as to create a new Function object capturing different values of self in function scope.
Take a statically defined function object and bind its this value using bind. Bind creates a new wrapper function object each time it is called.
The first method looks like (without Jquery or Jasmine):
function Foo(number)
{ var self = this;
this.num = number;
this.someCallback = function() // method with new Foo object stored as self in function scope
{ // something with num:
return self.num * 2;
}
}
and the second method could look like
function Foo(number)
{ this.num = number
this.someCallback = this.someCallback.bind(this); // bind prototypical method as local method.
}
Foo.prototype = {
someCallback: function () {
// this value is bound by constructor;
//Do something with the num
return this.num * 2;
}
};
Given the following function:
function countdown() {
var result = "";
// do something with the result variable
return result;
}
How can result variable be updated from within another function, before calling the countdown function?
function something(){
// update private variable "result"
}
function countdown(modifiedResult) {
var result = "";
// do something with the result variable
result = modifiedResult;
return result;
}
function something(modifiedResult){
// update private variable "result"
var updatedResult = countdown(modifiedResult);
alert(updatedResult);
}
something("I am updated value");
The variable result is private, and cannot be accessed from the outside.
One solution is to wrap the whole thing in a factory:
function countdownFactory () {
var result = 0;//since its named countdown i assumed it should be a number
return {
get: function () { return countdown},
set: function (value) {countdown = value}
};
}
To use this you would do:
var countdown = countdownFactory();
countdown.set(2);
countdown.get() // returns 2
JavaScript doesn’t have keywords for defining if a variable/function/object is private/public/protected/final/etc (assessor keywords)… But since it has closures and each closure has its own scope we can emulate some of these access controls, a private variable is simply a variable declared inside an “unreachable scope”.
learn more ....
http://asp-net-by-parijat.blogspot.in/2015/08/private-variables-and-functions-in.html
A closure should do it, but you need some access to the function. Here in this case it is countdown.setResult(), which puts the value to private variable result.
var countdown = function () {
var result = '';
function countdown() {
return result;
}
countdown.setResult = function (r) {
result = r;
};
return countdown;
}();
function something(){
countdown.setResult(42);
}
alert(countdown()); // empty string
something();
alert(countdown()); // 42
Is it possible, while creating an instance of an object, to check during the instantiation itself the type of the parameter passed to the constructor, and set one of the object's variable accordingly?
This is what I'm trying to do:
function Test(num1) {
this.num1 = num1;
this.isValidNumber = (function() {
console.log(this.num1); // logs "undefined" upon instantiation
if (isNaN(this.num1)) {
return false;
} else {
return true;
}
}());
}
var testObj = new Test(5);
I want isValidNumber's value to be true or false according to the parameter passed to the constructor.
Your IIFE (Immediately-Invoked Function Expression) creates a new context, which means this doesn't point to your constructor anymore but to the Window object (unless you're using strict mode which would make it undefined). One possible solution is to execute this function with the right context:
this.isValidNumber = function() {
if (isNaN(this.num1)) {
return false;
} else {
return true;
}
}.call(this);
The much simpler solution is obviously:
function Test(num1) {
this.num1 = num1;
this.isValidNumber = !isNaN(this.num1);
}
First of all, the code could (and probably should) be rewritten the following way:
function Test(num1) {
this.num1 = num1;
console.log(this.num1);
this.isValidNumber = !isNaN(this.num1);
}
As for the reason why your code is not working as expected – your self invoking function has its own this parameter which is unrelated to the this parameter within your constructor.
In this case the this parameter of your self invoking function references the global object. (In strict mode it would've been undefined)
If you really need to reference the this parameter of a containing function then there are several ways to achieve this, the simplest of which being:
function Test(num1) {
this.num1 = num1;
var self = this;
this.isValidNumber = (function() {
console.log(self.num1);
return !isNaN(self.num1));
}());
}
In your particular case you don't even need to capture it, and this would also achieve the same effect:
function Test(num1) {
this.num1 = num1;
this.isValidNumber = (function() {
console.log(num1);
return !isNaN(num1));
}());
}
But again, the self invoking function is simply not required here.
This in the inner function no longer references the parent object. A common way of storing the object reference is to create a 'self' variable and assign this to it when it's in the correct scope
function Test(num1) {
var self = this;
this.num1 = num1;
this.isValidNumber = (function() {
console.log(self.num1);
if (isNaN(self.num1)) {
return false;
} else {
return true;
}
}());
}
var testObj = new Test(5);
I can't get how to access that value, this is my code:
function Filters()
{
this.filters = ["filter_1", "filter_2", "filter_3"];
this.someData = "test";
this.draw = draw;
function draw(){
for(var i=0; i<this.filters.length;i++)
{
var filter = this.filters[i];
$("#" + filter).click(function(){
doSomething();
});
}
}
function doSomething(){
alert(this.someData);
}
}
I am aware of the fact that since doSomething() is called from within the closure, this. will refer a JQuery object being worked on. So how do I go about being able to use someData from my object in that function/closure ? Can't seem to figure it out.
Thanks for help :)
No, this inside doSomething will be the global object. You need to keep a reference to this in a separate variable:
function Filters()
{
var that = this; // reference to this
this.filters = ["filter_1", "filter_2", "filter_3"];
this.someData = "test";
this.draw = draw;
function draw(){
for(var i=0; i<this.filters.length;i++)
{
var filter = this.filters[i];
$("#" + filter).click(function(){
doSomething();
});
}
}
function doSomething(){
alert(that.someData);
}
}
Unrelated to your problem: you could also pass a reference to doSomething as the event listener, instead of wrapping it in another function:
$("#" + filter).click(doSomething);
I have a namespace in the following format allowing for public and private members:
function A() {
return('a');
}
namespace1 = (function () {
// private
namespace2 = (function() {
// private
prC = function () {
return(namespace1.puB() + 'c');
};
puC = function () {
return(prC());
};
// public
return({
puC: puC
});
})();
prB = function () {
return(A() + 'b');
};
puB = function () {
return(prB());
};
// public
return({
puB: puB,
namespace2: namespace2
});
})();
document.write('A() = '); try { document.write(A()); } catch (ex) { document.write('inaccessible'); }
document.write('<BR />');
document.write('namespace1.prB() = '); try { document.write(namespace1.prB()); } catch (ex) { document.write('inaccessible'); }
document.write('<BR />');
document.write('namespace1.puB() = '); try { document.write(namespace1.puB()); } catch (ex) { document.write('inaccessible'); }
document.write('<BR />');
document.write('namespace1.namespace2.prC() = '); try { document.write(namespace1.namespace2.prC()); } catch (ex) { document.write('inaccessible'); }
document.write('<BR />');
document.write('namespace1.namespace2.puC() = '); try { document.write(namespace1.namespace2.puC()); } catch (ex) { document.write('inaccessible'); }
Output:
A() = a
namespace1.prB() = inaccessible
namespace1.puB() = ab
namespace1.namespace2.prC() = inaccessible
namespace1.namespace2.puC() = abc
How might I go about appending both public and private members to such a namespace (IE: from different files)?
Here's a JSFiddle.
"How might I go about appending both public and private members to such a namespace..."
Well, your functions are exposed because you didn't properly declare your variables with var.
But once you fix that, you can add more exposed properties (since all properties are exposed) from any code that can reference your namespace objects.
Adding more properties that reference local (the proper term for private) functions, you'd need a new function and variable scope.
Just invoke a function that references the namespace objects, create some functions inside that function, and add properties that reference those local functions.
// Other file
(function() {
var newLocalFunc = function() {
// local function
}
var anotherLocalFunc = function() {
// local function
}
namespace1.exposedFunc = function() {
return newLocalFunc()
}
namespace1.namespace2.anotherExposedFunc = function() {
return anotherLocalFunc()
}
})();
And again... don't forget to put var before your variables in the original code.
Any variable declared without the var keyword will be in the global scope. So your puB() function is NOT inaccessible or private, it is just not a member of the object returned by the namespace1 function. Try window.prB() for example, you'll see that method exists within the global scope of the window object.
<head>
<script type="text/javascript">
obj1 = {}; //in global scope
var obj2 = {}; //in global scope. Although used the var keyword, this line itself is in the global scope; so the variable.
function someFunc() {
obj3 = {}; //in global scope
var obj4 = {}; //'so-called' private (inaccessible from global scope)
}
</script>
</head>
For combining two different JS files under the same 'namespace' (or let's say object):
File-1.js
var namespace1 = (function() {
// some code...
var namespace2 = (function() {
// some code...
return {
obj2: 'value2'
};
})();
return {
obj1: 'value1'
};
})();
File-2.js
namespace1.namespace3 = (function() {
// some code...
var ns4 = (function() {
// some code...
return {
obj4: 'value4'
};
})();
return {
obj3: 'value3',
namespace4: ns4
};
})();
What is what:
namespace1 is declared inside the global scope; so it is accessible
from anywhere and it is our main object.
namespace2 is inaccessible (private).
namespace3 is inaccessible in the global scope but accessible as a
member of namespace1; e.g.: namespace1.namespace3.
namespace4 is accessible as a member of namespace1. e.g.:
namespace1.namespace4.
So; the members of our main object namespace1 is:
namespace1 = {
obj1: String,
namespace3: {
obj3: String,
namespace4: {
obj4: String
}
}
};
You are a long way from Kansas, here.
You can't "declare" things as public or private.
As long as you define things inside of function, and then choose to return specific things, or append them to an object/array which you passed in as an argument, then you will have "public" (outer) access to those things, after the function returns.
In order to have "private" access, you return a function which references something on the inside.
var Wallet = function (amount, overdraft_limit) {
var balance = 0,
overdraft = overdraft_limit || 0,
deposit_funds = function (funds) { balance += funds; return true; },
withdraw_funds = function (request) {
var funds = 0;
balance -= request;
funds = request;
return funds;
},
validate_request = function (pin) { /* ... */ },
sufficient_funds = function (val) { return val <= (balance + overdraft); },
add = function (pin, deposit) {
if (!validate_request(pin) || deposit <= 0) { return false; }
var result = deposit_funds(deposit);
return result;
},
deduct = function (pin, withdrawl) {
if (!validate_request(pin) || withdrawl <= 0) { return false; }
if (!sufficient_funds(withdrawl)) { return false; }
var funds = withdraw_funds(withdrawl);
return funds;
},
check = function () { return balance; },
public_interface = { deduct : deduct, add : add, check : check };
return public_interface;
};
var myWallet = Wallet(30, 20);
var cash = myWallet.deduct(40);
cash; // 40
myWallet.check(); // -10
myWallet.balance = 40000000000;
cash = myWallet.deduct(4000);
cash; // === false
By building functions inside of my "constructor", which have access to balance, the variable that I return that "public" object to can call methods to interact with the "private" data, but can't access it or modify it through any method but to use those "public" functions.
Nesting this stuff 8-layers deep, using IIFEs uses the exact-same concept of closure which I just demonstrated.
Explicitly decide what you're going to return and what you are not.
The functions which you send into the world are public. The functions/etc inside of the function, which weren't returned or attached to an object are private.
They have been closed over, by the "constructor" function which returned, and now they are 100% inaccessible, except by using the functions which were built inside of the constructor, which reference the private vars, and were returned as public methods.