Declare variable in another scope - javascript

Look at this snipped code:
main = function() {
alert('This is ' + T);
}
caller = function() {
var T = 'me';
main();
}
caller();
as you see, I wanna function main identify the value of variable T, but the browser appears this error: T is undefined.
I can handle this error with change the scope of variable T to global scope or even pass the T variable to function main, but for some reason I don't want to use those and I want to declare variable T in scope of function main. Is it possible or not? How can I handle this scenario?
Thanks.

You have 3 options:
to declare T outside both
to pass T to main(T) as a parameter
to write main inside caller

T is a local variable to caller so it will not be visible inside main, one easy solution is to pass T as a parameter to caller from main
you need to pass T as a parameter
main = function(T) {
alert('This is ' + T);
}
caller = function() {
var T = 'me';
main(T);
}
caller();
Another solution is to declare T in a shared scope, this this case the global scope or declare main as a closure function inside main

The way I see it, you have a few options beside the obvious ones you have already stated.
One way is to declare main in caller:
caller = function() {
var T = 'me',
main = function() {
alert('This is ' + T);
};
main();
}
caller();
An other case would be to wrap both caller and main into an object, but that could be overkill. Still another way could be to set the this variable using Function.prototype.call or Function.prototype.bind:
main = function() {
alert('This is ' + this);
}
caller = function() {
var T = 'me';
main.call(T);
}
caller();
Or
main = function() {
alert('This is ' + this);
}
caller = function() {
var T = 'me',
newMain = main.bind(T);
newMain();
}
caller();

The best way to do this is to define a new function which declares T, main and caller. This way both functions have access to the value but it is not global
var pair = (function() {
var T;
var main = function() {
alert('This is ' + T);
};
var caller = function() {
T = 'me';
main();
};
return { 'main': main, 'caller': caller}
})();
pair.main(); // Call main
pair.caller(); // Call caller

Try to pass the object of the variable T to the function main and access it via this
main = function() {
alert('This is ' + this);
}
caller = function() {
var T = 'me';
main.call(T);
}
caller();

I can recall for three options:
make T global;
make a getter in caller object and use it to get the value
pass T as a parameter

Try this
var T = '';
main = function() {
alert('This is ' + T);
}
caller = function() {
T = 'me';
main();
}
caller();

I think something like this can handle it.
main = function() {
alert('This is ' + T);
}
caller = function() {
var T = 'me';
eval('var func = ' + main);
func();
}
caller();

Related

Overriding a javascript function definition

I am trying to monkeypatch a 3rd party javascript library but the original function definition I am overloading keeps getting called.
var ns = {};
ns.topFxn = function(){
var _me = "me";
function _toOverride(){
console.log("This is the original: " + _me);
}
function pubFxn(){
_toOverride();
}
console.log("Original");
ns.pubFxn = pubFxn;
};
//attempt to monkey patch
var oldTopFxn = ns.topFxn;
ns.topFxn = function(){
oldTopFxn();
function _toOverride(){
console.log("This is the overriden: " + _me);
}
console.log("MonkeyPatch");
};
ns.topFxn();
ns.pubFxn();
OUTPUT:
scratch.js:15> Original
scratch.js:26> MonkeyPatch
scratch.js:10> This is the original: me
I think this is because this function is indirectly called by another function, and that function might hold a closure on the function it is pointing to - so maybe this isn't possible? Any suggestions on how to override?
jsfiddle
You can't override a local function in another function, because of variable scope. The name _toOverride is local to each function, and assigning it in your function has no effect on the function with the same name in a different function.
You would have to override ns.pubFxn.
var oldTopFxn = ns.topFxn;
ns.topFxn = function(){
oldTopFxn();
var oldPubFxn = ns.pubFxn;
function _toOverride(){
console.log("This is the overriden: " + _me);
}
ns.pubFxn = function() {
oldPubFxn();
_toOverride();
}
console.log("MonkeyPatch");
};

javascript scope and global variables

I'm trying to avoid using global variable when using functions within objects.
I want to invoke a function inside other function and use a variable from the first function's scope.
For example:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(){
var code = "Hey, you're from " + this.data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView();
})
}
}
Clearly it's not working. I want to use data inside buildView without making data a global variable.
I thought using this would be the right course of action because I'm calling buildView from a function where data is defined.
How can this be achieved? Thanks.
You can pass the information along:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(data){
var code = 'Hey, you\'re from ' + data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView(data);
})
}
}
There is no way to access the data variable itself. That is locally scoped to the anonymous function you pass to getJSON (and getJSON passes it as an argument, which is beyond your control).
You have to copy the value somewhere.
In your particular example, there are no scopes shared between getData and buildView other than the global scope. So if you want to pass the value through scopes, then a global is your own (terrible) option.
You can simply pass it as an argument:
showForecast.buildView(data);
Or you can store it as a property:
showForecast.myData = data;
I like Vinny's answer.
One round-bout way is to make a module out of it:
var showForecast = function(){
var data;
var init = function () {
this.getData();
};
var buildView = function(){
var code = 'Hey, you\'re from ' + this.data.city;
$('body').append(code);
};
var getData = function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
this.data = data;
showForecast.buildView();
})
};
return {
'init': init,
'buildView': buildView,
'getData': getData
};
}();
This way the scope of var data is limited to the function. It's like a private variable.
As you are trying to avoid global, you should consider using namespaces. There is no such thing called namespace in Javascript. But you can define yourself using small utility method mentioned here.
http://www.zachleat.com/web/namespacing-outside-of-the-yahoo-namespace/
A utility method which helps creating custom namespaces.
jQuery.namespace = function() {
var a=arguments, o=null, i, j, d;
for (i=0; i<a.length; i=i+1) {
d=a[i].split(".");
o=window;
for (j=0; j<d.length; j=j+1) {
o[d[j]]=o[d[j]] || {};
o=o[d[j]];
}
}
return o;
};
Define name space
jQuery.namespace( 'jQuery.showForecast' );
Define methods using revealing module pattern
https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript
jQuery.showForecast = (function() {
var data;
var init = function() {
getData();
}
var buildView = function() {
var code = "Hey, you're from " + data.city;
$('body').append(code);
}
var getData = function() {
$.getJSON('http://ipinfo.io/', function(_data) {
console.log(data);
data = _data;
buildView();
})
}
return {
init: init
};
})(); // Execute it immediately
Usage:
You can access only init method as it is exposed to outside.
jQuery.showForecast.init()
Define another namespace
jQuery.namespace( 'jQuery.showForecast.extended' );
jQuery.showForecast.extended = {
// Define some more
};

Trying to simulate class in javascript, but cannot reach variables inside

I am generating a lot of "classes" (actually functions trying to simulate classes as in c# or other object oriented languages), and are looking for the best way to do this.
As you might notice, I also have jQuery available.
This is how all classes are generated at this point:
MyClass = (function() {
function innerClass() {
var self = this;
var myField;
// This function works as the constructor
this.init = function(opts) {
// Arguments to the constructor
var defaultOpts = {
myInitArgument: null
}
opts = $.extend(defaultOpts, opts);
self = this;
// Any custom constructor code is generated here...
}
// A function
this.myFunction = function() {
myField = "Hello World!";
}
// Returns an object with all selected fields and function that should work as "public". Those not mentioned here, will not be visible outside this class.
return {
init: this.init,
myFunction: this.myFunction,
myField: myField,
}
}
return innerClass;
})();
Then I create instances of the class like this:
var myObject = new MyClass();
myObject.init({myInitArgument: 'test'});
My main problem here is that inside the myFunction, "myField" will be set to "Hello World!" if I break in the debugger (i.e. Chrome Developer Tools), but using "myObject.myField" returns undefined.
I made a fiddle if you would like to play around with this sample.
What is the best way to accomplish this problem, and are there perhaps other things you feel of warning me about?
JavaScript is a bit weird when it comes to making classes and objects. IMO, this is the most reliable and readable method of doing it: start with a function that becomes your primitive object (Fruit).
Edit: thanks to #Bergi for pointing out that previous version had vestigial variables, needed to be moved to init().
function Fruit(opts) {
this.init(opts);
}
Now, expand the function, giving it more functions, like init, etc:
Fruit.prototype.init = function(opts) {
// init values go here
this.cost = 0;
this.count = 0;
var that = this; // in the iteration below, we want to refer to our parent
for( k in opts )(function(k, v) {
that[k] = v;
})(k, opts[k]);
}
// now, here's a specialized set of functions that sets properties (price/quant)
// note that they do "return this" - this is so you can conveniently chain
// commands. ex: apple.setPrice(10).setQuantity(5);
Fruit.prototype.setPrice = function(how_much) {
this.cost = how_much;
return(this);
}
Fruit.prototype.setQuantity = function(how_many) {
this.count = how_many;
return(this);
}
Simple function to return a computed value. At this point, once instantiated, the object becomes 'self aware'. Helper functions like this become more readable.
Fruit.prototype.getEarnings = function() {
return( this.cost * this.count );
}
So far we've only setup the abstract structure. To use this, create a new object:
var apple = new Fruit({ genus: 'Malus' });
var orange = new Fruit({ genus: 'Citrus' });
apple.setPrice(1.50).setQuantity(20);
orange.setPrice(1.25).setQuantity(40);
console.info( apple.genus + " will earn you $" + apple.getEarnings() ); // $30
console.info( orange.genus + " will earn you $" + orange.getEarnings() ); // $50
I don't understand what you do that much complicated things to have classes.
var myField and <returned object>.myField are two different variables, modifying one won't change the other.
You can try this (encapsulation):
return {
init: this.init,
myFunction: this.myFunction,
getMyField: function() {return myField;},
}
// ...
$('.console').append('<br />Outside myFunction: ' + myObject.getMyField());
or this (get operator):
return {
init: this.init,
myFunction: this.myFunction,
get myField() {return myField;},
}
// ...
$('.console').append('<br />Outside myFunction: ' + myObject.myField);
This worked fine for me
$('.console').append('Class simulation test:<br />');
// My class
function MyClass() {
var self = this, myField;
// This function works as the constructor
this.init = function(opts) {
// Arguments to the constructor
$('.console').append('<br />Inside myFunction: ' + JSON.stringify(opts));
var defaultOpts = {
myInitArgument: null
}
opts = $.extend(defaultOpts, opts);
//self = this; // no need of this
// Any custom constructor code is generated here...
this.myFunction('Hello from the constructor!');
}
// A function
this.myFunction = function(value) {
this.myField = value; //if you dont use var it will either refer to parent my field or window
$('.console').append('<br />Inside myFunction: ' + this.myField);
};
console.log(JSON.stringify(arguments[0]));
this.init(arguments[0]);
// Returns an object with all selected fields and function that should work as "public". Those not mentioned here, will not be visible outside this class.
return {
myFunction: this.myFunction,
myField: myField,
}
}
// instanciate
var myObject = new MyClass({myInitArgument: 'test'});
// test
myObject.myFunction('Hello from the outside!');
$('.console').append('<br />Outside myFunction: ' + myObject.myField);
I have recently been researching this. I have succeeded, here. The ticker object at that link is a real psuedo class.
var foo = function(args){
this.args = args;
this.whatever = "whatever";
}
foo.prototype.addBar = function(bar){
this.args += bar;
}
foo.prototype.getArgs = function(){
return this.args;
}
var baz = new foo("Hello");
baz.addBar(" Super Man");
var helloStr = baz.getArgs();
//helloStr holds "Hello Super Man"
var what = baz.whatever;
//what holds "whatever"
Simple, no need for inner function, new foo() is the constructor.

Javascript closure and "this"

My question is... in CallMeLaterTestObj function in the TestObj the "this" is the window object and not TestObj. How can I restructure this so that within the CallMeLater function I don't have to wrap the call function() { v.CallMeLaterTestObj(); } in a closure or using the bind function since it has limited support to newer browsers. Two objectives:
Keeping "this" in function calls within the object
Maintaining a separate value for "value" for each separate object so they don't share the same value.
// Emulating public api, private methods, private variables, public fields.
// New portion of question
Re-written to include binding function and prototypical notation. How do you move the Binding function into a base object that all new objects would get?
This is as close as I can come to getting this to use the best of both worlds. I have no idea what the pitfalls of this approach are though
var BaseObject = function ()
{
_getBinding = function (method)
{
var _self = this;
return function ()
{
_self[method].apply(_self, arguments);
};
};
return {
CallInline: _getBinding
}
}();
var TestObj = function (value)
{
$.extend(this, BaseObject);
// public var
this._value = value;
};
TestObj.prototype = function()
{
var privateVar = false;
// these are private
_giveMe = function () {
return this._value;
},
_callMeLaterTestObj = function () {
console.log('I am ' + this.constructor.name + ' my value is ' + this._value);
};
// public API
return {
GiveMe : _giveMe,
CallMeLaterTestObj : _callMeLaterTestObj
}
}();
function CallMeLater(v, i)
{
setTimeout(v.CallInline('CallMeLaterTestObj'), 10);
}
var V1 = new TestObj(1);
var V2 = new TestObj(2);
var V3 = new TestObj(3);
console.log('V1= ' + V1.GiveMe());
console.log('V2= ' + V2.GiveMe());
console.log('V3= ' + V3.GiveMe());
console.log('---');
V1.CallMeLaterTestObj();
console.log('---');
I think what you're looking for is this:
function TestObj(value) {
var _value = value;
this.giveMe = function() {
return _value;
};
this.callMeLaterTestObj = function() {
console.log('I am ' + this.constructor.name + ' my value is ' + _value);
};
return this;
};
function callMeLater(v, i) {
setTimeout(function() {
v.callMeLaterTestObj();
}, 10);
}
var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);
console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');
callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);​
To access constructor.name, you need to declare the function with function name() syntax, rather than var name = function() syntax.
To keep private variables and expose a public api, expose the public variables as properties of this in the function.
Be sure to return this from the constructor function to make it work.
It's also good practice to follow the naming convention of CamelCase for class names (of which TestObj is one) and lowerCamelCase for variables / methods / objects / etc. Helps keep things clear as to which variables are instances, and which are Classes.
Test and see the console output expected here.
note
Regarding wrapping v.callMeLaterTestObj() in a closure for the setTimeout, this technique is completely cross-browser compatible. You won't have any issues.
The bind method is newer, although there are many libraries that will shim that for you in older browsers. My personal favourite is underscore.
note 2
You can't call a method on an object in setTimeout without wrapping it in a closure somewhere, however if you want to you can abstract the closure in the Class without using a generic bind function (as provided by Underscore or jQuery and others) you can 'roll your own' in the Class like this:
function TestObj(value) {
var _value = value;
var _self = this;
this.giveMe = function() {
return _value;
};
this.callMeLaterTestObj = function() {
console.log('I am ' + this.constructor.name + ' my value is ' + _value);
};
this.getBinding = function(method) {
var _self = this;
return function() {
_self[method].apply(_self, arguments);
};
};
return this;
};
function callMeLater(v, i) {
setTimeout(v.getBinding('callMeLaterTestObj'), 10);
}
var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);
console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');
callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);​
explanation:
You need to use some sort of binding because, when you pass the method to setTimeout, you pass it by reference. So all setTimeout sees is a function - not the object it was on, which is why you lose the context of this.
Since setTimeout will therefore execute the function in the default scope - i.e. the browser window - you need a way to get this back, by reference, either through an inline anonymous function, or by returning a closure that uses the apply method to 'reset' this.
note 3
If you wanted to have your own bind method, and not include a library that provides it for you or include it in every class then you can use this one from Underscore, which defers to the native method in newer browsers:
function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
Then use it like this:
function callMeLater(v, i) {
setTimeout(bind(v.callMeLaterTestObj, v), 10);
}
This will work well in all browsers.
No, you can't. That's just the way to do it. Btw, you can easily shim the bind method so that it is available in older browsers, too.
An alternative would be to move the closure into the prototype method, if you know that you always will need to bind the actual function:
TestObj.prototype.getCallMeLaterTestObj = function () {
var that = this;
return function() {
console.log('I am ' + that.constructor.name + ' my value is ' + that._value);
};
};
setTimeout(v.getCallMeLaterTestObj(), 10);
Btw, your prototype has no constructor property so the log will not work as expected.
Your only chance is to avoid the this keyword entirely:
TestObj = function() {
var privateVar = false; // these are private static
function TestObj(value) {
function giveMe() {
return value;
}
function callMeLaterTestObj() {
console.log('I am TestObj my value is ' + giveMe());
}
this._value = value;
this.giveMe = giveMe;
this.callMeLaterTestObj = callMeLaterTestObj;
/* you could do this as well:
return {
_value: value,
giveMe: giveMe,
callMeLaterTestObj: callMeLaterTestObj
}; */
}
return TestObj;
})();
var v = new TestObj;
setTimeout(v.callMeLater, 10);
But this is not very memory-efficient, as it does not use prototypical inheritance at all.

Calling a function by a string in JavaScript and staying in scope

I've been playing around and searching a bit, but I can't figure this out. I have a pseudo private function within a JavaScript object that needs to get called via eval (because the name of the function is built dynamically). However, the function is hidden from the global scope by a closure and I cannot figure out how to reference it using eval().
Ex:
var myObject = function(){
var privateFunctionNeedsToBeCalled = function() {
alert('gets here');
};
return {
publicFunction: function(firstPart, SecondPart) {
var functionCallString = firstPart + secondPart + '()';
eval(functionCallString);
}
}
}();
myObject.publicFunction('privateFunctionNeeds', 'ToBeCalled');
I know the example looks silly but I wanted to keep it simple. Any ideas?
The string passed to eval() is evaluated in that eval()'s scope, so you could do
return {
publicFunction: function(firstPart, SecondPart) {
var captured_privateFunctionNeedsToBeCalled = privateFunctionNeedsToBeCalled;
var functionCallString = 'captured_' + firstPart + secondPart + '()';
eval(functionCallString);
}
}
However, a better solution would be to avoid the use of eval() entirely:
var myObject = function(){
var functions = {};
functions['privateFunctionNeedsToBeCalled'] = function() {
alert('gets here');
};
return {
publicFunction: function(firstPart, secondPart) {
functions[firstPart+secondPart]();
}
}
}();
myObject.publicFunction('privateFunctionNeeds', 'ToBeCalled');

Categories

Resources