I have the following code:
function Show() {
this.showId = $("#meta-show-id").val();
this.title = $("#meta-title").val();
this.season = $("#meta-season").val();
this.episode = $("#meta-episode").val();
this.storageId = $("#meta-show-id").val() + '-' + $("#meta-season").val() + '-' + $("#meta-episode").val();
this.torrents = [];
this.putioId = null;
this.status = null;
this.subtitle = null;
}
Show.prototype = {
constructor: Show,
checkStorage: function() {
var storage = new Storage();
storage.get(this.storageId, function(data){
if (jQuery.isEmptyObject(data)){
console.log("empty");
}
else {
this.subtitle = data.subtitle;
}
});
}
}
When I call the checkStorage() method on object, method checks in chrome.storage.sync for data, and sets object this.subtitle property.
But this doesn't seem to work, this.subtitle value doesn't change.
What am I doing wrong?
This is a normal result, and it happens because of scope changing. I don't know if Storage is has your own implementation (because it is used with getItem instead of get), but anyways, you are calling a method that I guess calls back the function you provide as a second argument, right?
And because this function is called from somewhere else, this is not the object that you want.
Here is a simple example: http://jsfiddle.net/6c2e6/1/
Check out logs, and see what this is. It's the Window, because my test function is at top level..
Related
I've an object called 'sheet1'
var nr = 1;
function Sheet(title){
this.div = document.createElement('div');
this.div.dataset.sheetNr = nr;
this.div.dataset.sheetTitle = title;
document.getElementById("sheets").appendChild(this.div);
nr++;
}
Sheet.prototype = {
constructor: Sheet,
get : function(data){
return this.div.dataset.data;
}
}
var sheet1 = new Sheet("Title1");
now when I call the function
sheet1.get("sheetNr");
it returns 'undefined' !...how can I solve this problem?
// console.log(data); outputs sheetNr
but when I change my function like
Sheet.prototype = {
constructor: Sheet,
get : function(){
return this.div.dataset.sheetNr;
}
}
and then call
sheet1.get();
it returns the number of the sheet...1 in this case...
http://codepen.io/anon/pen/rOXZjq?editors=101
You don't have a data attribute defined on the div you are creating sot it doesn't exist as called. When you call this.div.dataset.sheetNr you are actually hitting a defined attribute. To use the variable data as an index you need to call:
this.div.dataset[data] instead.
I apologize for the amount of code, but I think this is actually a problem with AppMobi's getCurrentLocation function. Basically what happens is I delegate a tap event to each list element. Then when you tap it, it runs an asynchronous getCurrentLocation and updates some stuff. Then the next time you tap another list element, the variables bound in the callback of the getCurrentLocation Function only refer to the first time it was called. Why doesn't this work??
app = { events: [{text: "foo", time: new Date()}, {text: "bar", time: new Date()}] };
$(document).ready(refreshEvents);
function refreshEvents() {
for (var index in app.events) {
insertEventHTML(app.events[index]);
}
}
function insertEventHTML(event) {
var text = event.text;
var time = event.time;
var new_element = $('<li class="event_element"></li>');
var new_checkin_element = $('<div class="check_in_button"></div>');
new_checkin_element.bind('tap', function(e) {
check_in(e);
fade($(this), 1.0);
});
new_element.append(new_checkin_element);
var text_element = $('<div class="text_element">' + text + '</div>');
new_element.append(text_element);
var time_element = $('<div class="time_element">' + time + '</div>');
new_element.append(time_element);
$('#your_events').append(new_element);
}
function check_in(e) {
$(e.target).siblings('.time_element').text('just now');
var time = new Date(); // time and event_index are the trouble variables here
var event_index = getEventIndex(e); // the first time this function runs, event_index is correct
// then each subsequent time, it remains the same value as the first
if (!app.settings.use_location) {
app.events[event_index].times.unshift({time: time, location: null});
} else {
AppMobi.geolocation.getCurrentPosition(onLocationFound, errorFunction);
}
function onLocationFound(response) {
var lat = response.coords.latitude;
var lon = response.coords.longitude;
var last_time = app.events[event_index].times[0];
if (last_time != undefined && last_time.time == time) {
// event_index and time here will only ever refer to the first time it was called. WHY???
add_checkin(response, event_index, time);
}else{
console.log('onLocationFound was called twice');
}
}
function errorFunction(error) {
$.ui.popup({title: 'geolocation error', message: 'Geolocation error. Turn off location services in settings.'});
}
function add_checkin(response, event_index, time) {
// and then of course event_index and time are wrong here as well. I don't get it.
app.events[event_index].times.unshift(
{
time: time,
location: {
latitude: response.coords.latitude,
longitude: response.coords.longitude
}
});
AppMobi.cache.setCookie('app', JSON.stringify(app), -1);
}
}
function getEventIndex(e) {
var target = $(e.target).parent();
var siblings = target.parent().children('li');
for (var i = 0; i < siblings.length; i++) {
if ($(target)[0].offsetTop == $(siblings[i])[0].offsetTop) {
return i;
}
}
}
Well, your issue seems to be that you are declaring a private variable event_index inside the check_in function and try to resolve it's value by accessing a global event_index variable inside onLocationFound.
Here's what you could do instead:
AppMobi.geolocation.getCurrentPosition(function (response) {
onLocationFound(response, event_index);
}, errorFunction);
function onLocationFound(response, event_index) { //... }
EDIT:
it is declared within check_in...
You are right, I totally missed that somehow. Well in that case it's very unlikely that the event_index variable inside onLocationFound isin't the same as in check_in. Do a console.log(event_index) inside onLocationFound and it should be the same. The only way it could be different is if you modify the local variable before the handler is called, which you doesn't seem to do, or if getCurrentPosition stores the first handler somehow and ignores subsequent handlers, but this API wouldn't make any sense.
EDIT 2:
As we suspect the handler might not be registered correctly the second time, I would suggest to check this way:
function check_in() {
if (!check_in.called) {
check_in.called = true;
check_in.onLocationFound = onLocationFound;
}
//...
function onLocationFound() {
console.log(arguments.callee === check_in.onLocationFound);
}
}
You can also simple do onLocationFound.version = new Date() and check arguments.callee.version to see if it stays the same.
I know you marked this answered already but....it might not actually be a problem with the library. Can I direct you to a post by #Joel Anair where he has posted an article and the example number five seems to be the "gotcha" which might have gotcha ;)
How do JavaScript closures work?
Basically in the for loop they are all being set to the same reference of i so event_index will all be the same value/reference. (which explains why they're all the same).
Instead of just saying:
var thing = timeConsumingMethod();
I have my variable hidden in a method like so:
function _thing() {
var thing = timeConsumingMethod() );
return thing;
}
It gets called a number of times. I'm concerned that I'm made things very inefficient. I assume it calls timeConsumingMethod every time (which is unneeded, it's always the same) I call "_thing()" to get my variable.
How do I manage these types of variables in simple efficient way? Is something like this a solution?:
function _thing() {
return _thing.thing
}
_thing.thing = timeConsumingMethod();
Basically, i want the protection of a function and to (ideally0 access my variable using _thing() or something similar, but I only want timeConsumingMethod to run once.
edit: tried this, doesn't work either:
function _thingy() {
var thing = timeConsumingMethod();
}
_thingy.test = function() {
return( _thingy.thing)
}
Why not just:
function SomethingTimeConsuming() { ... }
function LazyThing(sourceFunction) {
this.sourceFunction = sourceFunction;
this.value = null;
this.Value = function() {
if ( this.value == null) this.value = sourceFunction();
return this.value;
}
}
JSFiddle: http://jsfiddle.net/YSAjJ/
Output:
[14:20:20.079] Calling time-consuming function *(1 time)
I know the title is a little bit confusion, here is the details:
Say I have a custom object defined in javascript, and there is a public member defined in it:
function Test()
{
this.testArray = [];
}
And I have two methods for this object, one is read out some xml file and filled into the array:
Test.prototype.readXML = function()
{
var self = this;
$.get('assest/xml/mydata.xml', function(d){
$(d).find("item").each(function(){
var item = new Item;
item.ID = ($(this).attr("ID"));
item.body = ($(this).find("body").text());
});
self.testArray.push(item);
});
}
And another function, which will display the content into the HTML page.
Test.prototype.appendInfo = function()
{
var i;
for (i=0; i<testArray.length;i++)
{
$('#testdisplay').append(testArray[i].ID +"<br />");
$('#testdisplay').append(testArray[i].body = "<br /");
}
}
However, the display function continue gives me error that the testArray is not defined. I'm not sure where is the problem, since I put the display function behind the reading function. I expect that the data will be stored in the array and could be accessed anytime I need them.
Hope some one will kindly help me about this! Thank you!
}
}
So I notice two problems with your code.
First when you do your ajax call you need to pass a deferred back to the user. Since ajax calls are async it may not finish right away.
So your readXML function should do this. It should return the jquery get.
Test.prototype.readXML = function() {
var self = this;
return $.get('assest/xml/mydata.xml', function(d){
$(d).find("item").each(function(){
var item = new Item;
item.ID = ($(this).attr("ID"));
item.body = ($(this).find("body").text());
});
self.testArray.push(item);
});
}
Next you your second function append was just missing some context.
Test.prototype.appendInfo = function() {
var i;
for (i=0; i<this.testArray.length;i++) {
$('#testdisplay').append(this.testArray[i].ID +"<br />");
$('#testdisplay').append(this.testArray[i].body = "<br /");
}
}
So your code should look like this.
var mytest = new Test();
mytest.readXML().done(function(){
mytest.appendInfo();
}).fail(function(){
// put some fallback code here
});
Updated:
Added additional this's.
There is no testArray in your appendInfo() function, that's why it says it's not defined. You should use this.testArray instead.
Every time you want to use a variable declared inside your scope, but outside the function you are using, you must use this.yourVariable
Im looking through some code (unfortunatly the author isnt around anymore) and im wondering why he has used the .call method.
hmlPlaylist.prototype.loadVideos = function () {
var scope = this;
this.config.scriptUrl = '_HMLPlaylistAjax.aspx?' + Math.random();
jQuery.ajax({
type: 'GET',
url: this.config.scriptUrl,
success: function (d, t, x) {
scope.loadVideos_callback.call(scope, d);
},
error: function () {
}
});
};
hmlPlaylist.prototype.loadVideos_callback = function (data) {
var jsonData = '';
var jsonError = false;
try {
jsonData = eval("(" + data + ")");
} catch (jError) {
jsonError = true;
}
if (!jsonError) {
if (jsonData.playlists.length > 0) {
this.buildPlaylistList(jsonData.playlists);
}
if (jsonData.videos.length > 0) {
this.buildVideoList(jsonData.videos);
this.bindVideoNavs();
}
}
else {
// no json returned, don't do anything
}
};
Obviously he seems to have used it to pass a 'this' reference to the loadVideos_callback method but why? The 'loadVideos_callback' method is attached to the prototype of 'hmlplaylist' which is the 'class'. So if you access this inside the 'loadVideos_callback' method you get to the same thing dont you?
yes, I think you are right (I can't see the code in action). You still need the closure around scope, but in this case the use of call is not necessary.
To pull some of the comments into this answer, this is always the context on which the method was invoked. So if a new instance of htmlPlayList was created, and the method invoked on that instance, this would be a reference to that instance.