Changing the current closure? - javascript

I am trying to get some old code to work properly with minimal modification. The code was written on the assumption that it would run from a particular context. I have the context object.
Original code:
function oldExample(){
console.log(window); //New Error: window is undefined
console.log(gBrowser); //New Error: gBrowser is undefined
}
New, working code:
function fixedExample(){
console.log(this.window);
console.log(this.gBrowser);
}
//Elsewhere
function loadData(context) {
fixedExample.call(context);
}
Notes:
1. loadData and oldExample are defined in separate files.
2. context has other children besides window and gBrowser; This is an example
Is there a way to transition my oldExample code to work properly without needing to stuff this. everywhere? I.e., how can I run oldExample in a different context?

The only way I know how to do this is to define the properties as variables of the current context:
var object = {gBrowser: 'test'};
function oldExample(){
console.log(gBrowser);
}
var gBrowser = object.gBrowser;
oldExample();
This example outputs 'test'
But all this does is move the property access outside of the function definition, doesn't save you anything.

You can use bind method in javascript.
fixedExample.bind (context);
Now you need not use 'this' inside fixedExample and can use window directly.

Related

In this context, what exactly does $('id').on('click', this.method.bind(this)) do?

Here is the app I'm referring to:
I am trying to fundamentally understand the bind method in Javascript.
My understanding when I play around with it in the console is that bind returns a copy of the function, with "this" bound to whatever you pass into bind.
function logThis(){
console.log(this)
}
logThis.bind({today: 'Tuesday'})
//Will return a copy of the logThis function, with 'this' set to the
{today:'Tuesday'} object. The code does not run right away though.
var explicitlyLogThis = logThis.bind({today: 'Tuesday'});
explicitlyLogThis(); //This will run the code and display the {today: 'Tuesday'} object to the console.
This is my understanding so far. I understand that to actually run this new function that has 'this' explicitly bound using the bind method, you need to set it to a variable and then run it.
I see a contradiction when I look at the app in the above link. If you look at the bindEvents method on line 56, we have .on('keyup', this.create.bind(this)). I understand that we have to set 'this' to App when we run the create method because jQuery defaults to setting 'this' to the jQuery object itself. So this line is actually the same as: $('#new-todo').on('keyup', App.create.bind(App)).
That isn't where my confusion is. My question is:
How exactly are these copies of the functions with 'this' set to App actually being called? The app does not set them to a variable and then call that variable the way I had to when I was working in the console.
It just invokes the bound functions directly as soon as an event occurs on one of the jQuery elements. But I thought writing it this way would just return a copy of the function, and not run the function itself, if I am basing my assumptions on what I have figured out in the code I wrote above. I thought in order to invoke the function immediately, you would need to use call or apply.
I also realize that the app runs the bindEvents method when it starts (see line 46). So I understand that when you start the app, copies of the various functions are created with the correct 'this' bound to the functions. But...when/how do they actually get invoked without assigning them to variables? How are these copies accessed?
I think I have a flawed understanding of the bind method, so I would love some help. Thanks!
It sounds like you understand bind well enough. Perhaps there is some confusion with passing anonymous functions. As you know calling bind returns a new function and this can optionally be stored as a variable or passed as a function argument.
In the example below btn1 accepts a bound function as you've seen. This could also be written in a more long hand fashion with btn2. They're identical. btn3 doesn't receive a bound function, when its clicked its context is the button element, this looses all visibility of MagicalApp fucntions.
<button id="example1">button one bound</button>
<button id="example2">button one bound</button>
<button id="example3">button two unbound</button>
<script>
class MagicalApp {
add() {
console.log('this could do addition');
}
}
const app = new MagicalApp();
function contextOfEvent(event) {
console.log('contextSensitive', this.add)
}
const btn1 = document.querySelector("#example1");
btn1.addEventListener('click', contextOfEvent.bind(app));
const btn2 = document.querySelector("#example2");
const btn2ClickHandler = contextOfEvent.bind(app)
btn2.addEventListener('click', btn2ClickHandler);
const btn3 = document.querySelector("#example3");
btn3.addEventListener('click', contextOfEvent);
</script>

Cannot Find JavaScript Namespace

I am trying to create namespaces in JavaScript as in the following script:
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// function script
}
Then I call the function in HTML:
onclick="hlAdmin.editCompany(123)"
I get a reference error: Cannot find "editCompany".
Anyone know why?
Based on your comments I assume the following:
The equivalent script (and scoping is like):
<html><head>
</script>
var hlAdmin = hlAdmin || {};
hlAdmin.editCompany = function (src) {
// error in this script
}
</script>
</head></body>
<button onclick="hlAdmin.editCompany(123)">Caption</button>
</body></html>
In this example hlAdmin is indeed in the global scope (the root-scope of the host, called window in browsers).
If (in this example) you get reference error: Cannot find "editCompany", then one should look at other error-messages in your (browser's) error-log, because when there is a fatal error in the function for hlAdmin.editCompany, then that function will not be created (hence .editCompany becomes a property that points to undefined instead of a method that points to the function OR .editCompany doesn't even exist (depending on engine/error)).
To investigate if you indeed have a scoping-problem you could test this by: window['hlAdmin'] || (window['hlAdmin']={}); (or some equivalent variant). If that made the code work, then it seems you have some scoping-problem.
Hope these steps help someone in the future.
It's generally considered bad form to mix inline javascript and non-inline. The preferred way to do this would be to keep all the javascript in one place using an event handler:
window.hlAdmin = window.hlAdmin || {};
window.hlAdmin.editCompany = function (src) {
// function script
}
document.getElementById('yourElementId').onclick = function() {
hlAdmin.editCompany(123);
};
To more specifically address the issue: One thing that could cause this issue is if the hlAdmin object is not ending up in the global scope. You stated that this declaration is "at the top of the JavaScript file", but if it's in any kind of function (such as a function set to window.onload, or the jQuery $(function() { ... });) it would not end up in the global scope when declared as a var. A variable declared with var will only end up globally scoped if it's in the root scope, outside of any kind of function. If rather than using var hlAdmin you instead use window.hlAdmin, this will make sure that even if you're inside a document ready function or something similar, you're creating your hlAdmin in the global context, which will fix the problem if it is in fact an issue of scope.
I found the problem.
The browsers (at least Aurora and Chrome) are dropping the namespace in the onclick attribute. When you look at the browser html the namespace has just disappeared from the markup.

How to use JSONP with Object Oriented Javascript

I am new to JSONP and had implemented cross domain functionality for my application and everything is working fine. Now i want to change my javascript code to apply object orientation.
My api is
http://localhost:8080/myApplication/getComments?callback=displayComments
CrossDomain.prototype.displayComments = function(data) {
// code to display the comments
}
Now I am getting an error in firebug given below
ReferenceError: displayComments is not defined
I changed the api to
http://localhost:8080/myApplication/getComments?callback=this.displayComments
and found that the function is appended inline to the callback like this
http://localhost:8080/myApplication/getComments?callback=callback=function (jsonData)
{
//code to display the comments
}
this time another error in firebug
SyntaxError: function statement requires a name
I have a doubt whether to use JSONP in object oriented javascript or not.
Please help.
Thanks in advance.
There's no point in defining the function on the prototype of a function unless you are going to create instances of that function, so start by doing that.
var myCrossDomain = new CrossDomain();
Then you have to call the method on the object, not as a global (it isn't a global, so you can't do that anyway)
var uri = "http://localhost:8080/myApplication/getComments?callback=" +
encodeURIComponent("myCrossDomain.displayComments");
In response to edits and comments:
Yes i am creating an instance of this in another js file
Then reference it as shown above.
I changed the api to
http://localhost:8080/myApplication/getComments?callback=this.displayComments
It's JSON-P. It runs by adding a new script element. Everything gets called in the global context. That is going to call this.displayComments which will be the same as window.displayComments.
If you want to call your method directly, then you need to specify the global variable holding the instance explicitly.
If you don't want to call it directly then you can use the more traditional approach of generating a new, anonymous function which has access to said object through a closure, assigning that function to a global variable (with a unique name) and using that name as your callback argument.
and found that the function is appended inline to the callback like this
http://localhost:8080/myApplication/getComments?callback=callback=function (jsonData)
You haven't shown us the code that creates the URI so we can't tell why that might be the case.

javascript window.open doesn't work

I want to use window.open to open a window to one of my JSP file. But the browser keeps showing connecting... And even firebug stops working every time I click the text. Neither the p nor the input tags work, but when I use a href to link the JSP it can link to the file:
<!DOCTYPE html>
<html>
<head><title>Sample JSP Page</title>
<script>
function open(){
//window.open("hello.jsp","hello","height=700, width=800");
var x=window.open("hello.jsp","window","status=1,height=700, width=800");
x.focus();
}
</script>
</head>
<body>
<h1>Sample JSP Page</h1>
<p onclick="open()">do not work</p>
<form>
<input type="button" value="new window" onclick="window.open('test-with-utils')"></form>
</body>
</html>
That's because you have redefined window.open when you defined the function open. Use a different function name instead.
Change the name of the function.
The window object is the top level object in JavaScript, and contains in itself several other objects, such as "document", "history" etc.
When you define a variable or a function of your own you really add a new property to the window object. And this will work ( and a little live example ):
var foo = "bar";
alert ( window.foo ); // says "bar"
In addition if you add this little snippet to your code:
window.onerror = function ( msg, url, num ) {
alert ( "Error: " + msg + "\nURL: " + url + "\nLine: " + num );
return true;
};
you will get this error, when press the button:
Error: Uncaught RangeError: Maximum call stack size exceeded
This means an endless recursion. It is a side effect - you define a new open function, and when you call window.open(), you recursively call your function.
Just to expand on the reason that you are having problems here, you may want to read a little about javascript Scope (Very Helpful Blog). Essentially, consider the following code:
<script>
var thisOne=true;
function thatOne() {
alert("whizbang");
}
var theOther={foo:"bar"};
//More code here...
</script>
Once you reach the comment, you know you can access those variables and the function directly, like if (thisOne) {...}, element.onclick=thatOne; or console.log(theOther.foo). However, you can also access them as children of the root object which, in a web browser, is called window. So you can do:
console.log(window["thisOne"]);
window.thatOne.call(obj, params);
console.log(window.foo.bar);
so by defining open() as a function which is not inside another element (which is to say, is inside the root element), you overwrite the window.open() function. When you attempt to call the function later on, you get problems because the open function calls window.open, which calls window.open, which calls window.open...
There's a few ways to get around this -
Define the onclick handler inline
To do this, get rid of the whole <script>..</script> element then, using whichever element you choose (that supports it) add the onclick attribute:
onclick="window.open('hello.jsp','window','status=1,height=700, width=800');"
This is a nice and quick method, and it keeps all the logic right there with it's triggering element, but it is not easily extensible and you may find yourself sneered at by some. ("Oh, you use inline javascript? how quaint")
change the method name
This will take the least effort from you in terms of getting your page working now from what you have (it's also essentially what everyone else has suggested). Just change the name of the open method to something like openANewWindow() or gotoJSP() or anything that doesn't already exist in the root object, making sure to get both where you define it (in the script element) and where you use it (in the onclick attributes).
Use a closure
This is almost definitely not what you want in this case, its more complexity than you need for a single function. Just including this as an example of how to get out of the root object, seeing as being in it seems to be the heart of your problem.
You have probably already seen in javascript how to define an object, but you may not know that by defining an object, all you are really doing is adding an object property to the root object. You can use this behavior to your advantage, to give a hierarchical structure to your functions.
For example:
<script>
var MyFunctions = (function() {
function open(){
var x=window.open("hello.jsp","window","status=1,height=700, width=800");
x.focus();
}
return {open:open};
})();
</script>
This creates an anonymous function that is immediately run. Inside the scope of this function, another function, open() is defined, however it is defined within the scope of that anonymous function, not the root object (window). After open() is defined, a reference to it is returned as the value of the object property: open.
The result of all this is that the open property of the MyFunctions object is the function you need. You can then call it with MyFunctions.open() or even window.MyFunctions.open().

Javascript scope help

I am relatively new to javascript so please be patient if what i am asking is completely stupid!
I am trying to make a simple module. Inside the module i want to have a config object that holds settings for the module. I am also using jquery. The jquery selectors work only when in a function directly in the main object/module.
I understand that javascript has functional scope so I am suprised that I cannot use the jquery selectors anywhere inside the module.
EDIT:
I want to be able to directly set all of my configs inside the configs object using jquery selectors. This way i keep all the messy stuff inside one place and can then access configs.whatever throughout the rest of the module. At the moment jquery selectors do not work inside the configs module.
var OB = function() {
var configs = {
'mode' : 'test',
'numOfSelects' : $('.mySelect').find('select').length, // This doesnt work
}
var getMode = function() {
return configs.mode;
}
function init() {
alert(configs.numOfSelects); // This alerts 0 until the following line
alert($('.mySelect').find('select').length); // This correctly alerts 2
};
var handlers = {
successHandler : function() {
alert("Success");
},
errorHandler : function() {
alert("error");
}
}
return {
init : init,
getMode : getMode
}
}( );
$(document).ready(function(){
OB.init();
});
It isn't that jQuery isn't in scope — that's that the code isn't executing when you think it is. The variable config is defined when that anonymous function (var OB = function() {}()) is executed. The DOM isn't ready yet, so that DOM traversal doesn't find anything. When you do the DOM traversal in init(), that isn't executed until it's explicitly called inside the $(document).ready() handler, at which point that DOM is set up. That's the difference you're seeing.
OB() needs to be called after the DOM has completely loaded. Hence the answer by Marcelo, which calls OB() in the ready() method.
EDIT: It's funny that my original answer below was incorrect because I didn't notice two little parentheses at the end of the definition of OB, and it turns out that these are the culprit. You define and then immediately invoke OB, which is before the DOM has been fully loaded. Remove those parentheses and make the change I suggest below.
Calling OB() returns an object with init and getMode, but you haven't called OB(), you've only referred to OB. Try this instead:
$(document).ready(function(){
OB().init();
});
Also, I assume you want to later refer to getMode. In particular, you will to get the copy of getMode that has access to the same local scope that your init() call had access to. To achieve this, you will need to store the result of calling OB() for later use:
var ob;
$(document).ready(function(){
ob = OB();
ob.init();
});
function some_other_function() {
... ob.getMode() ...;
}

Categories

Resources