I started trying to create a timer function that would let me wrap a callback function so that I could later alter the behavior dynamically.
This led to a general realization that I really don't understand functions yet, and definitely don't understand what is happening with 'this'
I have a test environment setup on jsfiddle
myns = {};
myns.somefunc = function(txt) {
this.data = txt;
this.play = function() {
alert(this.data + ' : '+dafunc.data);
};
};
var dafunc = new myns.somefunc('hello world');
myns.Timer = function(msec, callback) {
this.callback = null;
this.timerID = null;
this.ding = function() {
this.callback();
};
this.set1 = function( msec, callback ) {
this.stop();
this.callback = callback;
this.timerID = setTimeout(this.ding, msec );
};
this.set2 = function( msec, callback ) {
this.callback = callback;
var wrappedDing = (function(who) {
return function() {
who.ding();
};
})(this);
this.timerID = setTimeout(wrappedDing, msec );
};
//this.set1(msec, callback);
this.set2(msec, callback);
};
var ttimer = new myns.Timer(1000, dafunc.play);
If I use the set1 method, then the callback doesn't work.
So I am trying the set2 method. This gets me to the play method but "this" is not referring to the instance of somefunc.
I thought I was on the right track, but the mix up on 'this' has me confused.
Any clues would be welcome.
The problem is that, unlike in a language like python, when you take a dafunc.play and pass it somewhere else (callback = dafunc.play) it forgets it was associated with dafunc, son you you would need to use yet another wrapper function, like you did in the set2 function.
var ttimer = new myns.Timer(1000, function(){ return dafunc.play(); });
Making all there extra functions by yourself is annoying. You could instead use the bind method that is available in newer browsers:
var wrappedDing = this.ding.bind(this);
new myns.Timer(1000, dafunc.play.bind(dafunc) );
Or you could use a similar shim if you need to support older versions of IE too.
Finally, if you are not going to take advantage of some form of inheritance or dynamic binding, you could instead rewrite your code to use closures. Since everything is lexicaly scoped, you don't have to worry about the this anymore:
(btw, I ended up simplifying the code in the proccess...)
myns = {};
myns.somefunc = function(txt) {
var obj = { data : txt };
obj.play = function() {
alert(obj.data);
};
return obj;
};
var dafunc = myns.somefunc('hello world');
myns.timer = function(msec, callback) {
var timerID = null;
var set = function(){
stop();
timerID = setTimeout(callback, msec);
};
set();
return {
set: set
};
};
var ttimer = myns.timer(1000, dafunc.play);
And one last thing: If you don't hate yourself use console.log and your browser's debugger and development console instead of using alerts for output.
Related
I need to extend a jQuery Plugin (https://github.com/idiot/unslider) in order to add additional behavior with another public method.
(function(){
// Store a reference to the original remove method.
var originalMethod = $.fn.unslider;
// Define overriding method.
$.fn.unslider = function(){
// Execute the original method.
originalMethod.apply( this, arguments );
console.log( "Override method" );
function test() {
console.log("test called");
}
this.each(function() {
// Operations for each DOM element
console.log("each dom element?");
}).data('unslider', {
// Make test accessible from data instance
test: test
});
return this;
}
})(jQuery);
I already managed to make the public method accessible when calling
var slider = $('#slider');
slider.data('unslider').test();
However, I want to keep the old behavior of unslider anyways, but extend the Plugin with another function. Does anyone have an idea?
I created a fiddle, so you can check whats happening:
My new function gets called, but the old ones are gone:
http://jsfiddle.net/b2os4s7e/1/
If you look at the source of unslider, you can see it stores the Unslider instance inside the data:
// Enable multiple-slider support
return this.each(function(index) {
// Cache a copy of $(this), so it
var me = $(this),
key = 'unslider' + (len > 1 ? '-' + ++index : ''),
instance = (new Unslider).init(me, o);
// Invoke an Unslider instance
me.data(key, instance).data('key', key);
});
In your code you're overwriting this object with your own object. However, the slider expects there to be an Unslider instance. So what you want to do is get this instance and then extend it with your own functions:
var key = $(this).data('key');
var obj = $(this).data(key);
obj.test = function() { console.log('Working!'); };
See http://jsfiddle.net/b2os4s7e/2/
Just define:
$fn.unslider2 = function() { ... }
With any name and behaviour you like.
For extend JQuery should use .fn.extend
(function($){
$.fn.extend({
helloworld: function(message){
return this.each(function(){
$(this).click(function(){
alert(message);
});
});
}
});
})(jQuery)
the object .fn.extend is used for extend funcionality of jQuery
Thanks for your answers! I did it this way:
(function($){
var originalMethod = $.fn.unslider;
$.fn.extend({
unslider: function(o) {
var len = this.length;
var applyMethod = originalMethod.apply( this, arguments );
var key = applyMethod.data('key');
var instance = applyMethod.data(key);
// Cache a copy of $(this), so it
var me = $(this);
if (instance) {
instance.movenext = function (callback) {
return instance.stop().to(instance.i + 1, callback);
};
instance.moveprev = function (callback) {
return instance.stop().to(instance.i - 1, callback);
};
}
return applyMethod.data(key, instance);
}
});
})(jQuery)
The key was to address the data attribute as sroes suggested.
Moreover i needed to apply the original method, since i need the old methods.
Can you please help answering this. Please not the contraints.
var myLib = {
var callback_one = function (result_from_web_service) {
console.log('callback_one');
};
var callback_one = function (result_from_web_service) {
console.log('callback_two');
};
var init = function () {
console.log('initializing...');
async_call_one(callback_one);
async_call_two(callback_two);
};
var doStuff = function () {
console.log('doStuff is called');
};
};
// User of my library
myLib.init();
myLib.doStuff();
// output
initializing...
doStuff is called
callback_one
callback_two
// What i need:
initializing...
callback_one
callback_two
doStuff is called
Constraint:
calling myLib.init shall not end up calling myLib.doStuff. i.e. myLib.init should be independent of myLib.doStuff
myLib.doStuff() should be called after myLib.init() and its callbacks are returned.
Thanks,
//You must change your API so init is async
//There is no way to have it wait until all initialization is done before it retuns
var init = function (initDone) {
console.log('initializing...');
var n = 0;
function serviceDone(){
n++;
if(n >= 2){ initDone() }
}
async_call_one(function(x){ callback_one(x); serviceDone() });
async_call_two(function(x){ callback_two(x); serviceDone() });
};
// User of my library
myLib.init(function(){
myLib.doStuff();
})
The way I parallelized those calls is very ad-hoc s not the most maintainable (there I need to keep the calls to serviceDone and the value of N in sync).. In the long run I would recommend using one of the many JS async programming libs out there.
hugomg has a good answer.
Yet I think it is really specific and could benefit a sort of workflow implementation, like this (approximately...):
function void() {}
var myLib = {
var g_flow = [];
g_flow[this.init] = [];
g_flow[this.init]["whendone"] = this.callback_one;
g_flow[this.init]["done"] = false;
g_flow[this.callback_one] = [];
g_flow[this.callback_one]["whendone"] = this.callback_two;
g_flow[this.callback_one]["done"] = false;
g_flow[this.callback_two] = [];
g_flow[this.callback_two]["whendone"] = this.doStuff;
g_flow[this.callback_two]["done"] = false;
g_flow[this.doStuff] = [];
g_flow[this.doStuff]["whendone"] = void;
g_flow[this.doStuff]["done"] = false;
var callback_one = function (result_from_web_service) {
console.log('callback_one');
};
var callback_one = function (result_from_web_service) {
console.log('callback_two');
};
var init = function () {
console.log('initializing...');
};
var doStuff = function () {
console.log('doStuff is called');
};
var flow_onward(hwnd) {
async_call(function(){ hwnd(); myLib.flow_onward(g_flow[hwnd]["whendone"]); });
}
flow_onward(this.init);
};
// User of my library
myLib.init();
myLib.doStuff();
Doing this way you can ensure the sequentiality and expand the numbers of callback as much as you want.
ps: this code has not been tested
I want to make a class in javascript to reuse from my main code in the connection with an indexeddb object. What I have now is:
function DATABASE() {
this.DB_NAME = 'MYdatabase';
this.DB_VERSION = 1;
this.db = null;
this.results = null;
}
DATABASE.prototype.open = function(callback) {
var req = indexedDB.open(this.DB_NAME, this.DB_VERSION);
req.onsuccess = function (evt) {
this.db = this.result;
callback();
};
req.onerror = function (evt) {
console.error("openDb:", evt.target.errorCode);
};
req.onupgradeneeded = function (evt) {
console.log("openDb.onupgradeneeded");
};
}
My problem here is that when the onsuccess executes I loose the scope of my main class and this is not what I expected. How can I do what I am looking for?
I want to make some connections at the same time with this, something like:
var DB = new DATABASE();
DB.open(function(res){});
var DB2 = new DATABASE();
DB2.open(function(res){});
var DB3 = new DATABASE();
DB3.open(function(res){});
thanks so much.
Under var req add var self = this; and use like this whenever the scope changes:
self.db = self.result;
My problem here is that when the onsuccess executes I loose the scope of my main class and this is not what I expected.
It's not scope, but the value of this during a function call depends on how the function is called. So what's happening is that the functions you're assigning to req are getting called with this being a different value than it is in the call to open.
How can I do what I am looking for?
Since your functions already close over the scope of the call to open, the easiest way is to do what Andy suggested:
DATABASE.prototype.open = function(callback) {
var req = indexedDB.open(this.DB_NAME, this.DB_VERSION);
var self = this; // <=== New
req.onsuccess = function (evt) {
self.db = this.result; // <=== Changed
callback();
};
// ...
}
Note: In the changed line, I don't know what this.result is, so I don't know whether to change this to self there as well. It's entirely possible that you actually want this.result, if result is a property of the object that this points to on the callback.
More:
You must remember this
Closures are not complicated
Does this work for you? Putting the open function inside the DATABASE instead of on the prototype.
function DATABASE() {
var _this=this;
_this.DB_NAME = 'MYdatabase';
_this.DB_VERSION = 1;
_this.db = null;
_this.results = null;
_this.open = unction(callback) {
var req = indexedDB.open(_this.DB_NAME, _this.DB_VERSION);
req.onsuccess = function (evt) {
_this.db = _this.result;
callback();
};
req.onerror = function (evt) {
console.error("openDb:", evt.target.errorCode);
};
req.onupgradeneeded = function (evt) {
console.log("openDb.onupgradeneeded");
};
}
}
var that = this
req.onsuccess = function (evt) {
that.db = that.result;
callback();
};
Also I recommend to read this article: Scope and this in JavaScript
I wrote a small hash change object, it will alert the url hash whenever it changes:
(function() {
function hashChange() {
this.previousHash;
this.initialize();
}
hashChange.prototype.initialize = function() {
this.setInterval = window.setInterval(this.checkHashChange, 0);
}
hasChange.prototype.uponHashChange = function(hash) {
alert('I executed!');
var hashValue = hash.split('#')[1];
alert(hashValue);
}
hashChange.prototype.checkHashChange = function() {
var hash = window.location.hash;
if(hash && hash !== this.previousHash) {
this.previousHash = hash;
this.uponHashChange(hash); // <---- doesn't execute
}
}
var hashChange = new hashChange();
})();
But this:
this.uponHashChange(hash);
Never gets executed. Why?
this.setInterval = window.setInterval(this.checkHashChange, 0);
This line is not going to do exactly what you mean. this.checkHashChange will lose its binding to its current this (which would be a hashChange instance), and will instead be invoked in the context of the window object.
You need to bind it explicitly to the correct context object:
var self = this;
this.setInterval = window.setInterval(function() { self.checkHashChange() }, 0);
Matt Greer has suggested Function.bind, which would make it more concise and likely more readable:
this.setInterval = window.setInterval(checkHashChange.bind(this), 0);
Unfortunately, Function.bind is not yet widely supported across browsers.
I have written some javascript that I would to encapsulate in a closure so I can use it elsewhere. I would like do do this similar to the way jQuery has done it. I would like to be able to pass in an id to my closure and invoke some functions on it, while setting some options. Similar to this:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
I have read a lot of different posts on how to use a closure to do this but am still struggling with the concept. Here is where I left off:
_snr = {};
(function (_snr) {
function merge(root){
for ( var i = 1; i < arguments.length; i++ )
for ( var key in arguments[i] )
root[key] = arguments[i][key];
return root;
}
_snr.draw = function (options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
}
var options = merge(defaults, options)
return this.each(function() {
//More functions here
});
};
_snr.erase = function () {};
})(_snr);
When ever I try to call the draw function like the first code section above, I get the following error, '_snr is not a function'. Where am I going wrong here?
EDIT
Here is what I ended up doing:
function _snr(id) {
// About object is returned if there is no 'id' parameter
var about = {
Version: 0.2,
Author: "ferics2",
Created: "Summer 2011",
Updated: "3 September 2012"
};
if (id) {
if (window === this) {
return new _snr(id);
}
this.e = document.getElementById(id);
return this;
} else {
// No 'id' parameter was given, return the 'about' object
return about;
}
};
_snr.prototype = (function(){
var merge = function(root) {
for ( var i = 1; i < arguments.length; i++) {
for ( var key in arguments[i] ) {
root[key] = arguments[i][key];
}
}
return root;
};
return {
draw: function(options) {
var defaults = {
canvasId : 'canvas',
imageSrc : 'images/someimage.png'
};
options = merge(defaults, options);
return this;
},
erase: function() {
return this;
}
};
})();
I can now call:
<script type="text/javascript">
_snr("#canvas").draw({
imageSrc : someImage.png
});
</script>
Because you declared _snr as an object and not a function. Functions can have properties and methods, so there's various ways to achieve what you want, for example one of them would be say...
_snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
You can also pass the outer context into a closure to hide your variables from accidentally polluting the global namespace, so like...
(function(global) {
var _snr = function(tag) {
this.tag = tag;
}
_snr.foo = function() {
//Code goes here
}
//export the function to the window context:
global._snr = _snr;
})(window);
window._snr('#tag').foo('wat');
Happy coding.
Because your _snr is an object, not a function. You have to call it like this:
_snr.draw({
canvasId: '#canvas',
imageSrc: 'someImage.png'
});
When you do _snr('#canvas') that is a function call which is why you're getting that error. _snr is an object with some methods attached to it such as draw() and erase(). The reason jQuery is able to pass arguments into the $ is because they return the $ as a function object which is why we're able to pass it various selectors as arguments.
You are going wrong at the first line _snr = {}
It needs to be
_snr = function(){
selector = arguments[0]||false;
//snr init on dom object code
return _snrChild;
}
Im on a mobile phone but when im on a pc I will maybe fix the whole code c:
Here you have a snr object and that has erase and draw methods. What you intend to do is to write a _snr function which will get an id and return a wrapper object. That returned object should have erase and draw methods. so you can do
var returnedObject = _snr("my_id");
returnedObject.draw("image.png");