Javascript and prototyping procedure - javascript

I am trying to write a script that does a slide show. I can do it with functions, but I want to use the prototype method. What I am having a hard time figuring out is the procedure. Here is what I have tried to do
var displayVars = {
slide: '',
thumb: ''
}
//setup display
display = function(slide,thumb) {
displayVars.slide = $(slide);
displayVars.thumb = $(thumb);
// set slider width
}
display.prototype.play = function() {
// move slide to this location
display.hightlight();
}
display.prototype.hightlight = function() {
// add border to element
}
$(function() {
newdis = new display('.show-slide','.window-thumbs');
displayVars.timer = setTimeout(newdis.play,500);
});
If you notice in the play function I want to call the highlight method. What I really want is to run the highlight function every time the play function is called. I can't get my head to see how this can be done because "display" or "this" will not let me access the highlight function.

The problem is not with the innards of your prototype functions, but rather with the way you set up the timeout handler.
displayVars.timer = setTimeout(function() { newdis.play(); }, 500);
Then you'll be able to use this in the "play" function:
display.prototype.play = function() {
// move slide to this location
this.hightlight();
}
There's no intrinsic "membership" relationship between a function and an object of any sort. Object properties can refer to functions, but the only time that means anything is when a function call is made via the object property reference. Since you weren't calling the function, but just grabbing a reference to it to pass to "setTimeout()", there was nothing to set the value of this. By wrapping it in an anonymous function that explicitly calls "play" via the object reference, you set up this correctly.
Another way to do this is with the "bind()" function available in newer browsers:
displayVars.tinmer = setTimeout(newdis.play.bind(newdis), 500);
That will have more-or-less the same effect as the anonymous function (with some extra subtleties that don't make much difference most of the time).

Related

Passing a pointer/reference to a variable as parameter

I know this question has been asked multiple times (yes, I did some research) but I can't see to find a solution that fits my needs.
What I have done so far:
I was building a function that tracked the percentage of how far the user scrolled down a page and display this nicely into some progressbar. This worked perfectly but when I opened the developer console on Chrome and looked at the Timeline tab (this displays what is being run in a nice graphic), I realised that my code was quite "active". It ran every pixel the user scrolled down the page, which is quite alot to be honest.
So I thought to myself, how can this be improved and I have come up with a solution that involves executing a function only once per {whatever} milliseconds. This involves a variable set to true or false, if the function has already been executed in the {whatever} milliseconds.
What i want to accomplish:
I want to be able to set a reference to an external variable that will act as a flag to determine if the function has already been executed or not.
function qeue(fn, interval, status){ // this function name might not be very fitting..
// fn = function to be executed
// interval = function can only run once between the intervals
// status = tricky part..
// This should contain a reference to an external variable that is either true or false
}
How can this be accomplished?
side note
If this explanation isn't helping, and you still don't get what I want:
How can I pass a reference to a variable into a function, so that function can act based on the value of that variable?
Why normal parameters are not an option
I want to implement some sort of recursive setTimeout functionality inside a function, that checks if another function has been executed or not, if I pass this in to a parameter, this parameter cannot change during the process.
Hope you guys can help me out!
Thank you
Thank you for all your great answers. You made me learn alot. I am going with the debounce strategy! I marked T.J. Crowder as the best answer, because it was a good explanation and one of the first. But thank you all once again!
What you've described wanting to do doesn't immediately say "use a reference to a variable" to me (as Teemu points out, sounds like you want debouncing), but answering your question about references to variables...
JavaScript doesn't have any form of references to variables (other than through closures, which might be problematic here). But you can readily do what you're talking about by just using an object and using a property on it. The property is the "variable."
Simple example:
function foo(obj) {
var counter = 0;
var timer = setInterval(function() {
console.log("foo: " + obj.property);
if (++counter === 5) {
clearInterval(timer);
}
}, 500);
}
var o = {property: "unchanged"};
// Give the "reference" to `property` to `foo`:
foo(o);
// Update it periodically while `foo` is doing its asynchronous thing
setTimeout(function() {
o.property = "update 1";
}, 1000);
setTimeout(function() {
o.property = "update 2";
}, 1700);
In JavaScript values such as integers, strings, etc. are passed by value. If you want to pass a reference, you have to pass an object into the JavaScript function. (JavaScript objects are passed by reference)
function adjustValues(referenceObject) {
referenceObject.foo = 2;
referenceObject.bar = "newValue";
}
referenceObject = {
foo: 1,
bar: "initialValue"
};
adjustValues(referenceObject);
why don't you use the setInterval function, it will do exactly what you want.
Example:
setInterval(function() {
// logic to be implemented
}, delayInMilliseconds)
How can this be accomplished?
Not with a variable. There are no "references to variables" in JS. I can see two simple solutions:
pass a getter/setter function:
function queue(getStatus) {
…
getStatus() // gets current value
…
}
var executed = false;
queue(function() { return executed; });
pass an object with a property:
function queue(status) {
…
status.executed // gets current value
…
}
var status = {executed: false};
queue(status);
I have come up with a solution that involves executing a function only once per {whatever} milliseconds. This involves a variable set to true or false, if the function has already been executed in the {whatever} milliseconds.
I cannot see the reason why this variable would need to be a parameter to the function, and be available (or even settable?) outside it. Just use a local variable inside queue.
Btw, this functionality is known as debouncing, you don't have to write this yourself. Many implementations are already available on the web, sometimes as part of larger libraries. See for example What does _.debounce do?.
Try the following example :
'use strict';
var observable = 0;
function incObservable() {
++observable;
console.log('incObservable observable: '+observable);
}
function observe() {
console.log('observe observable: '+observable);
}
var observer = setInterval(observe, 100);
setTimeout(function() {
incObservable();
setTimeout(function() {
incObservable();
setTimeout(function() {
incObservable();
}, 300);
}, 300);
}, 300);
setTimeout(function() {
// Stop obsever
clearInterval(observer);
}, 1000);
// observe observable: 0
// observe observable: 0
// incObservable observable: 1
// observe observable: 1
// observe observable: 1
// observe observable: 1
// incObservable observable: 2
// observe observable: 2
// observe observable: 2
// observe observable: 2
// incObservable observable: 3
// observe observable: 3

Javascript: Reassigning 'this', or alternative?

I am building a small app which is part of a sales-enquiry process. It has 'pages' which the visitor progresses through. I have laid out these pages as part of a large object literal. In the following code, branch-select is one of those pages. As you can see, the init() function sets a sibling value by using this to refer to the parent branch-select. However, the save() function is called from a click event, so instead of using this, it seems I have to laboriously write out the full object reference each time to set values? Please see the code & comments below to illustrate the problem:
// This is part of a larger object called "stepData"
"previous page": {
// ...
}
"branch-select": {
ref: "Select Type",
visited: false,
init: function(){
this.visited = true; // Here I can use "this" to set other values in the parent object
// ....
},
next: "",
save: function(){
branchKey = $(this).attr('data-value'); // this function is invoked from a click event, so "this" refers to the DOM element that was clicked. Therefore throughout the rest of the function if I want to set values on the parent object, I have to write out the full object reference each time...
switch(branchKey){
case "Lodges":
stepData['branch-select'].ref = "Lodges";
stepData['branch-select'].values[0].a = "Lodges";
stepData['branch-select'].next = "lodge-2"; // Do I really have to write out stepData['branch-select'] each time?
break;
case "Caravans":
stepData['branch-select'].ref = "Caravans";
stepData['branch-select'].values[0].a = "Caravans";
stepData['branch-select'].next = "van-2";
break;
}
stepData[stepData['branch-select'].next].init();
}
},
"next page": {
// ...
}
In the interests of DRY (Don't repeat yourself) code, I was wondering if there is any neat solution to this?
EDIT:
Webkit's answer presents a new problem; the clicked DOM element (.branch-select) is dynamically introduced, so to bind the click event, I have to use:
$("#template-holder").on('click', ".branch-select", stepData['branch-select'].save);
(template-holder is the parent element which is always present). How would I integrate the call() method into the above code?
Another way to have "this" reference your object when handling an event is using 'call'.
for example:
var setData = {
save: function(){
// 'this' shall be setData!
var _bs = this['branch-select'];
_bs.ref = "Lodges"...
}
}
then:
$(".someElement").on('click', function() {
setData.save.call(setData)
});
**updated (I'm pretty sure this should work just the same):
$("#template-holder").on('click', ".branch-select", function() {
stepData['branch-select'].save.call(setData)
});

scope Issue seeing object methods

I have tried searching through a lot of S.O. pages but nothing has touched EXACTLY on this top while also NOT USING JQUERY.... I am trying to stick to pure JavaScript as I want to learn it 115% before advancing my current knowledge of JQuery.
I have an object called ScreenResizeTool like this...
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg);
}, true);
}
and a method like this...
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
My trouble is probably obvious to an experienced JavaScript user but I am having trouble not making this into a messy dirty awful OOP set. I have done various tests to show and prove to myself that the this inside the addEventHandler changes when it becomes bound to the window. This much I assumed before testing but I was able to see that once window.resize event happens the listen method is gone and not a part of the global window variable....
I have also tried adding a this capture such as this.me = this inside the object constructor however it also couldn't see the me variable once it ran. Once the window took the function over it no longer knew anything about the me variable or any reference to my class methods....
I am aware that I could separate this differently but my goal here is to learn how to fully encapsulate and use as many clean OOP structures as possible as I just came from the .NET world and I need it in my life.
I am also aware that I could make messy calls and or store this object or access to the methods inside the window variable but that seems outright wrong to me. I should be able to fully encapsulate this object and have its events and methods all implemented in this class structure.
I also know that the currImg variable is not going to be seen either but lets start small here. I assume once I figure out my incorrect train of thought on scope for JavaScript I should be fine to figure out the currImg problem.
I know there's 1000 JavaScript programmers out there waiting to rip me a new one over asking this simple question but I gotta know...
Thoughts anyone?
this inside a function bound to a DOM Object (like window) will always refer to that object.
this inside a constructor function will always refer to the prototype.
A common practice to circumvent the this issue, as you mentioned, is to cache it in a variable, often called self. Now you want the variables and properties of your object available after instantiation, so what you need is the return keyword, more specifically to return the parent object itself. Let's put that together:
function ScreenResizeTool() {
var self = this;
// method to instantiate the code is often stored in init property
this.init = function() {
window.addEventListener('resize', function() {
self.listen(); // self will refer to the prototype, not the window!
}, true);
};
return this;
}
ScreenResizeTool.prototype.listen = function() { // Dummy function
var h = window.innerHeight, w = window.innerWidth;
console.log('Resized to ' + w + ' x ' + h + '!');
};
Pretty easy huh? So we have our prototype now, but prototypes can't do anything if there's not an instance. So we create an instance of ScreenResizeTool and instantiate it with its init method:
var tool = new ScreenResizeTool();
tool.init();
// every time you resize the window now, a result will be logged!
You could also simply store the listen & init methods as private functions inside your constructor, and return them in an anonymous object:
function ScreenResizeTool() {
var listen = function() { ... };
var init = function() { ... };
// in this.init you can now simply call listen() instead of this.listen()
return {
listen: listen,
init: init
}
}
Check out the fiddle and make sure to open your console. Note that in this case I'd rather use the first function than the second (it does exactly the same) because prototypes are only useful if you have multiple instances or subclasses
The whole concept of this in JavaScript is a nightmare for beginners and in my code I usually try to avoid it as it gets confusing fast and makes code unreadable (IMHO). Also, many people new to JavaScript but experienced in object-oriented programming languages try to get into the whole this and prototype stuff directly though the don't actually need to (google JS patterns like IIFE for example as alternatives).
So looking at your original code:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg); // global function listen?
}, true);
}
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
First off, you probably mean addEventListener instead. In its callback you refer to listen but as a global variable which would look for it as window.listen - which doesn't exit. So you could think to do this:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
this.listen(currImg); // what's this?
}, true);
}
As you want to use the prototype.listen function of ScreenResizeTool. But this won't work either as the event listener's callback function is called with a different this and not the this that is your function scope.
This is where something comes in which makes most programmers cringe, you have to cache this, examples from code I've seen:
var _this = this;
var that = this;
var _self = this;
Let's just use the latter to be able to refer to the function within the event callback:
function ScreenResizeTool(currImg) {
var _self = this;
window.addEventListener('resize', function() {
_self.listen();
}, true);
}
Now this will actually work and do what you want to achieve: invoke the prototype.listen function of ScreenResizeTool.
See this JSFiddle for a working example: http://jsfiddle.net/KNw6R/ (check the console for output)
As a last word, this problem did not have anything to do with using jQuery or not. It's a general problem of JS. And especially when having to deal with different browser implementations you should be using jQuery (or another such library) to make your own code clean and neat and not fiddle around with multiple if statements to find out what feature is supported in what way.

change scope of 'this' in esquire JS

I am trying to use enquire.js to trigger a reload of my bxslider when the screen size is small, to show fewer images.
I have registered a screen width as below.
enquire.register("screen and (max-width:900px)", {
match: this.ChangeSliderDown,
unmatch:this.ChangeSliderUp
});
Now as part of the transition i need to do a calculation based on a variable that is associated with the Prototype of the current class.
ChildCarousel.prototype = {
...
ChangeSliderUp: function()
{
var maxSlides = (this.ourCarouselCollection.length < 3) ? 1 : 3;
...
}
}
in all my other functions referring to this allows me to access variables such as the ourCarouselCollection in the instance of enguire js i get the object that is a result of the register call.
why is this happening and is it possible to change it?
adding the bind(this method solved the problem
enquire.register("screen and (max-width:900px)", {
match: this.ChangeSliderDown.bind(this),
unmatch:this.ChangeSliderUp.bind(this)
});
The value of this has nothing to do with scope, it is resolved within an execution context and is set by the call or with bind. Also, it is convention that only functions that are intended to be called as constructors have names that start with a capital letter (so ChangeSliderUp should be changeSliderUp).
The ChangeSliderUp method is expecting to be called with this referencing an instance of ChildCarousel as its this. When you assign a reference to the function like:
match: this.ChangeSliderDown
then the function will be called without this being set to the instance and will default to the global object or be undefined in strict mode.
You can use bind per Bluephlame's answer, or use a closure something like:
// Assuming that this referenes an instance of ChildCarousel
// where this code is running
var carousel = this;
enquire.register("screen and (max-width:900px)", {
match: function() {carousel.ChangeSliderDown();},
unmatch: function() {carousel.ChangeSliderUp();}
});
but I can't test that. It should ensure that the function is called as a method of an instance, hence setting this to the instance.

How can I have setTimeout execute a method in an object?

I would like to have a pulldown menu close itself upon a mouseleave event, after a short delay. But I'm having trouble getting it working.
Consider the following methods in an object: (I am using jQuery)
myObj = {};
myObj.message = "woot!";
myObj.bindEvents = function() {
var that = this;
$("#menuPanel")
.bind("mouseleave", function() {
that.timer = setTimeout(that.closeMenu,500);
});
}
myObj.closeMenu = function() {
// close the menu
alert(this.message);
}
This doesn't work. That is to say, this.message comes up undefined. After a bit of digging, I understand why. :) The 'that' reference is not available to code inside of setTimeout at the time of execution.
I'm wondering, what is the "best" way to get around this type of problem? How can I have a method that uses setTimeout call another method in the same object, and still have access to the properties in the object?
Thanks in advance for your help.
The problem here is that you're detaching the closeMenu method from it's object. You would have the same problem if you did this:
var closeMenu = myObj.closeMenu; // detaching the method from the object
closeMenu();
Detaching and calling methods like this means they no longer apply to the objects they were created on. In your example, you're doing almost the same thing:
// Setting the first parameter of setTimeout to be the detached closeMenu method
that.timer = setTimeout(that.closeMenu,500);
A fix for the first method would be to use the call or apply methods:
var closeMenu = myObj.closeMenu; // detaching the method from the object
closeMenu.apply(myObj);
But that wouldn't work for a timer. Instead, create an anonymous function:
that.timer = setTimeout(function () { that.closeMenu(); },500);
It might also be worth mentioning bind() - not to be confused with jQuery's $('#selector').bind() - a method that's been floating around on various blogs and in some libraries (Prototype being the most notable) for a while now, and has finally been implemented in ECMAScript edition 5.
that.timer = setTimeout(that.closeMenu.bind(that),500);
I use a similar method in one or two classes I created, because it just makes things easier.

Categories

Resources