In JavaScript I am looking for a way to use bind() on an anonymous and async function.
Example:
exports.foo = function () {};
exports.foo.prototype = {
load : function(id) {
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
});
}
};
I got this working by making the functions non-anonymous, but I think it makes my code look ugly. Especially after having 4 different anonymous functions in a row.
exports.foo = function () {};
exports.foo.prototype = {
load : function(id) {
function _load(object) {
this.object = object;
}
var _loadThis = _load.bind(this);
var query = new Parse.Query("SomeObject");
query.get(id).then(_loadThis);
}
};
Is there a better way?
Well it's not necessarily "better", but you can call .bind() directly after the closing brace of your function instantiation expression:
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
}.bind(this));
A function instantiation expression gives you a function object reference, so putting a . after it and calling bind makes sense. What gets passed to the .then function, therefore, is the return value from the call to .bind.
This syntax is not correct:
exports.foo.prototype = {
load = function(id) {
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
});
}
};
The prototype is an object who's properties are defined as load: function() {}, not load = function() {}.
It should be:
exports.foo.prototype = {
load: function(id) {
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
});
}
};
A simple way to do it is to declare a variable to the correct 'this' and use closuers to keep a reference to it.
exports.foo = function () {};
exports.foo.prototype = {
load : function(id) {
var self = this;
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
self.object = object; // this is the wrong this
});
}
};
Related
I am getting the error 'TabsViewModel is not a constructor' with the following code. I've looked at other posts regarding this error, but most suggest that the function name is in use somewhere else, but as you can see this is a very simple example and TabsViewModel is not used elsewhere.
define(['utils','ko','text!tmpl/risersandfallerstabs_partial.html','text!tmpl/errorPage_partial.html'], function(utils,ko,tmplrisersandfallerstabsStr,tmplErrStr) {
let tabsmodel = {};
$(function() {
tabsmodel = new TabsViewModel();
ko.components.register('risersandfallerstabs', {
viewModel: utils.createVM(tabsmodel),
template: tmplrisersandfallerstabsStr
});
utils.applyBindings(tabsmodel,'risersandfallerstabs');
var TabsViewModel = (function(){
let self = {};
self.test = "123";
return self;
});
});
})
Why am I getting this error?
You're trying to call TabsViewModel before it exists. Move your function definition above the function execution.
var TabsViewModel = (function() {
let self = {};
self.test = "123";
return self;
});
tabsmodel = new TabsViewModel();
I was running into a similar issue, the problem was that my class was an anonymous function instead of a normal function.
Didn't work:
var TabsViewModel = () => { ... }
Worked:
var TabsViewModel = function() { ... }
I'm building a jQuery app using OOP principles and I'm trying to implement a externally added callback function which invokes a method from inside of my object.
function testObject() {
var self = this;
var functions = new Array();
this.updateObject = function() {
console.log('updated')
}
this.addFunction = function(func) {
functions.push(func)
}
this.callFunctions = function() {
$.each(functions, function(key, value) {
functions[key]()
})
}
}
var myobject = new testObject();
myobject.addFunction(
function() {
$(':text').on('change', function() {
return self.updateObject();
})
}
)
This is an overly simplified version of the plugin I'm building. The callback works fine, but I cannot use the self.updateObject(); inside of it, since it outputs Illegal Invocation.
How can I call a method from inside the callback properly?
The problem is self is out of scope of the callback function, because the function only has variables in the scope of where it was defined. The callback is defined outside of the testObject.
A solution is to bind the this context in the callback function to self using Function.prototype.call(self), when you call it in callFunctions(). Then in the callback, you can use this to refer to the testObject instance. In your callback example it contains a jQuery event so you will lose the this context. To rectify that you can create a local self that equals this before the jQuery change event.
function testObject() {
var self = this;
var functions = new Array();
this.updateObject = function() {
console.log('updated')
}
this.addFunction = function(func) {
functions.push(func)
}
this.callFunctions = function() {
$.each(functions, function(key, value) {
functions[key].call(self); // call it and bind the context to self
})
}
}
var myobject = new testObject();
myobject.addFunction(
function() {
var self = this; // needed because the change event will overwrite 'this'
$(':text').on('change', function() {
return self.updateObject(); // use self to access testObject
})
}
)
myobject.callFunctions();
Alternatively you can pass self as an argument to the callback. To do that, change the .call() line to:
functions[key].call(null, self);
and change the callback to accept an argument like so:
myobject.addFunction(
function(self) { // self as an argument
$(':text').on('change', function() {
return self.updateObject(); // use self to refer to testObject
})
}
)
function testObject() {
var self = this;
var functions = new Array();
this.updateObject = function() {
console.log('updated')
}
this.addFunction = function(func) {
functions.push(func.bind(self)) // Bind the context
}
this.callFunctions = function() {
$.each(functions, function(key, value) {
functions[key]()
})
}
}
var myobject = new testObject();
myobject.addFunction(
function() {
var self = this;
$(':text').on('change', function() {
return self.updateObject();
})
}
)
Or you can use this as well:
myobject.addFunction(
function() {
$(':text').on('change', this.updateObject);
}
)
I spent the better part of the day reading about the module pattern and its 'this' scope. Eventually I found a work-around for my problem, although with a feeling there's a better way of doing things.
The actual code is >200 lines, but I've boiled it down to the following:
objA has a method (publicA) that objB wants invoke by callback. The detail that complicates things is that publicA needs help from publicA_helper to do its job. (http://jsfiddle.net/qwNb6/2/)
var objA = function () {
var privateA = "found";
return {
publicA: function () {
console.log("privateA is " + this.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
var objB = function () {
return {
callback: function (callback) {
callback();
}
}
}();
objA.publicA(); // privateA is found
objB.callback(objA.publicA); // TypeError: Object [object global]
Fair enough – I've grasped that the caller's context tends to influence the value of 'this'. So I add measures to retain 'this' inside objA, of which none seems to work. I've tried the
var objA = (){}.call({}) thingy, setting var self = this; (calling self.publicA_helper() accordingly). No luck.
Eventually, I added a private variable var self;, along with a public method:
init: function() {self = this;},
...and by making sure I call objA.init(); before passing objA.publicA to objB.callback, things actually work.
I cannot stress the immensity of the feeling that there's a better way of doing this. What am I missing?
The generalized solution is extremely simple.
Write all the module's methods as private, then expose those that need to be public.
I write all my modules this way :
var objA = function () {
var privateA = "found";
var A = function () {
console.log("privateA is " + A_helper());
},
var A_helper = function () {
return privateA;
}
return {
publicA: A
//A_helper need not be exposed
};
}();
Thus, all methods are in the same scope, each one having direct access to all other methods in the same module, and the ambiguous this prefix is avoided.
objB.callback(objA.publicA); will now work as expected.
See fiddle
I've tried the var objA = (){}.call({}) thingy,
How? You want to use call on the callback that you want to invoke with a custom this, not on your module closure. It should be
var objB = {
callback: function (callback, context) {
callback.call(context);
}
};
objB.callback(objA.publicA, objA);
I've tried setting var self = this;
The self variable is supposed to be in a closure and point to the object on the methods are stored. That is only this when your module IEFE would be invoked on your module - it's not. Or if it was a constructor - it's not. You could change that with call as above:
var objA = function () {
var privateA = "found",
self = this;
this.publicA = function () {
console.log("privateA is " + self.publicA_helper());
};
this.publicA_helper = function () {
return privateA;
};
return this;
}.call({});
But that's ugly. In your case, the self variable simply needs to point to the object literal which you're returning as your module:
var objA = function () {
var privateA = "found",
self;
return self = {
publicA: function () {
console.log("privateA is " + self.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
Btw, since you're creating a singleton you don't need an explicit self, you could just reference the variable that contains your module (as long as that doesn't change):
var objA = function () {
var privateA = "found";
return {
publicA: function () {
console.log("privateA is " + objA.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
Another method would be to simply make all functions private and then expose some of them - by referencing them local-scoped you will have no troubles.
var objA = function () {
var privateA = "found";
function publicA() {
console.log("privateA is " + helper());
}
function helper() {
return privateA;
}
return self = {
publicA: publicA,
publicA_helper: helper // remove that line if you don't need to expose it
};
}();
The reason is that the context is getting changed when you are invoking the callback. Not a generalized solution, but shows that the code works by specifying the context while invoking callback.
var objA = function () {
var privateA = "found";
return {
publicA: function () {
console.log("privateA is " + this.publicA_helper());
},
publicA_helper: function () {
return privateA;
}
};
}();
var objB = function () {
return {
callback: function (callback) {
callback.call(objA);
}
}
}();
objA.publicA(); // privateA is found
objB.callback(objA.publicA); // privateA is found
I have the following code
var PROMO = PROMO || {};
PROMO.Base = (function () {
var _self = this;
var Init = function () {
WireEvents();
};
var WireEvents = function () {
//wire up events
};
} ());
In the same file I have the code to call the above function
I am trying to get to an end point where I can use the following code
$(document).ready(function () {
PROMO.Base.Init();
});
this gives the error
Cannot call method 'Init' of undefined
Now I know there are many ways to write javascript, but in this case I want to be able to call my functions, or least the Init method in the way shown above.
var PROMO = PROMO || {};
PROMO.Base = (function () {
var _self = this;
var Init = function () {
WireEvents();
};
var WireEvents = function () {
//wire up events
};
var reveal = {
Init: Init
};
return reveal;
} ());
You need to return the public facing functions. See updated code.
Working fiddle with both patterns, using IIFE and direct attribution.
Using var makes the definition private and your function is returning nothing. Use this:
PROMO.Base = {
Init: function() {
},
WireEvents: function() {
};
};
You are wrapping the definition with an IIFE(Immediately Executed Function Expression). So your PROMO.Base object will be assigned the value of that (function(){//blabla})(); returns. But your function doesn't have a return statement. By default it will return undefined.
Which is way your PROMO.Base will be undefined and you get this:
Cannot call method 'Init' of undefined
If you really want that IIFE:
var PROMO = PROMO || {};
// NEVER use _self = this inside static functions, it's very dangerous.
// Can also be very misleading, since the this object doesn't point to the same reference.
// It can be easily changed with Function.prototype.call and Function.prototype.apply
PROMO.Base = (function () {
_PROMO = {
Init : function () {
document.body.innerHTML += "itworks";
},
WireEvents : function () {
//wire up events
}
}
return _PROMO;
} ());
PROMO.Base.Init();
Update
The better and easier pattern is to simply assign the functions to PROMO.Base. Dully note you should not capitalize static functions, but only constructors. So if something is not meant to be instantiated, don't call it Init, it should be init. That is the convention.
var PROMO = {};
PROMO.Base = {};
PROMO.Base.init = function() {
console.log("this works");
};
PROMO.Base.wireEvents = function() {
console.log("this is a static function too");
};
You can attach it to the window object like ...
window.PROMO = (function($, _){
// this will access PROMO.Base
PROMO.Base = {
// inner functions here
Init:{}
};
})(jQuery, _);
Then load it as you do.
Or if you depend from jQuery
(function($){
var PROMO = {
// inner functions
Init: function(){},
WireEvents: function(){}
};
$.PROMO = PROMO;
})(jQuery);
On DOM ready
jQuery(function ($) {
var promo = $.PROMO || undefined;
promo.Base.Init();
});
I'm trying to understand how to best use the JavaScript module pattern. My problem is that it seems there's no way to refer to the module from within itself, because this is set to the Window object.
I've got this simple test code:
var Test = function() {
var that = this;
return {
something: function() {
console.info(that);
}
}
}
var test1 = Test();
test1.something();
var test2 = Test();
test2.something();
Both test1 and test2 print a reference to the Window object instead of the module itself.
Any idea how to change it so that I have a valid this inside the module?
If you did
var test1 = new Test()
You could then do
test1.something();
An alternative module structure would be to do something like this:
var myModule = function () {
var obj = {};
obj.something = function () {
return console.log(obj);
};
obj.something2 = function () {
return console.log(obj === this); // true
};
return obj;
};
var test = myModule();
test.something();
test.something2();
Hope this helps
I think you're confusing the JavaScript module pattern with JavaScript constructor functions.
JavaScript constructor functions
If you write a function and call it with the new keyword in front of it, then that function is called as a constructor function.
It will automatically return a new object, that you can refer to within the constructor function using the this keyword.
var Test = function() {
var that = this;
this.something = function () {
console.info(that);
console.info(this);
};
}
var test1 = new Test();
test1.something();
You can return your own object instead, but you wouldn't normally do that in a constructor, you'd just use this instead:
var Test = function() {
var that = this;
return {
something: function () {
console.info(that);
console.info(this);
}
};
}
var test1 = new Test();
test1.something();
If you don't call it with the new keyword in front of it, then it's called like a regular function, meaning any references to this inside of it refer to the object of which the function is a property (which, in the absence of anything else, will be the global object, which in web browsers is window).
var geoff = {
Test: function () {
var that = this;
return {
something: function () {
console.info(that);
}
};
}
};
var test2 = geoff.Test();
var test3 = Test();
Note: with constructor functions, you'd normally define methods on their prototype object, so that the methods don't get unnecessarily redefined for each object you create using the constructor function:
var Test = function() {
this.else = "Something Else"
}
Test.prototype.something = function () {
console.info(this);
}
Test.prototype.somethingElse = function () {
console.info(this.else);
}
var test4 = new Test();
test1.somethingElse() // Logs "Something Else"
(Note that if you return your own object from the constructor function as we mentioned above, then you won't be able to access methods on the prototype object any more.)
Also note that each time you call a constructor function, it returns a new object. You can pass parameters into a constructor function (just like you can with any other function) and use them to customise the object returned:
var Test = function(else) {
this.else = else;
}
Test.prototype.somethingElse = function () {
console.info(this.else);
}
var test1 = new Test("Something else");
var test2 = new Test("Something else again");
test1.somethingElse(); // Logs "Something else"
test2.somethingElse(); // Logs "Something else again"
The problem you have is because this refers to an object, but Test() isn't an object; it's just a function. The object that owns Test() is the Window object (because Test is in the global scope), so therefore that's what you get back when you reference this from within Test().
You may want to try something like this:
var testObj = {
Test : function() {
var that = this;
return {
something: function() {
console.info(that);
}
}
}
}
Now you can call testObj.Test(); and you'll get a reference back to the testObj object.
Hope that clarifies things a bit.