This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
in App:
var bootstrap = new Bootstrap();
bootstrap.init( this, this.onBootstrapComplete );
in Bootstrap:
this.init = function( app, completeHandler ){
_app = app;
_completeHandler = completeHandler;
...
}
...
var _allReady = function(){
_completeHandler( _app );
}
back in App:
this.onBootstrapComplete = function( app )
{
app.something();
app.someValue = ...
}
I wanted to get this context inside onBootstrapComplete.
It works but it doesn't look right :)
If let's say I wanted to call onBootstrapComplete directly from App, I would have to call it this.onBootstrapComplete( this ).
How can I do it so my onBootstrapComplete looks like this:
this.onBootstrapComplete = function()
{
this.something();
this.someValue = ...
}
I would recommend using underscore.js. See http://underscorejs.org/#bind for further information.
this.onBootstrapComplete = _.bind( function() {
...
this.someFunction(); // this context is available now
...
}, this );
this is evaluated when the function is called. Say you use this inside a function f.
There are basically two ways to call f:
(expr).f() if f is called as a property on some object, this will evaluate to that object expr.
f() In this case, this will evaluate to window.
Since you pass the function to bootstrap, it can only call the function as f().
You could use a closure:
var self = this;
this.onBootstrapComplete = function()
{
self.something();
self.someValue = ...
}
Alternatively, you can use a function to f.apply() the function appropriately:
function bind(context, f){
return function() {
return f.apply(context, arguments);
}
}
this.onBootstrapComplete = bind(this, function()
{
this.something();
this.someValue = ...
});
Or with ECMAScript 5, there is already a bind function[MDN]:
this.onBootstrapComplete = function()
{
this.something();
this.someValue = ...
}.bind(this);
Related
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 6 years ago.
I have the following code (simplified)
var Page = new UI('page.html');
Page.onLoad = function(html){
this.name = 'Page 1';
Util.test(this.run);
};
Page.run = function(){
console.log(this.name); // undefined
console.log(Page.name); // correct
};
var Util = function(){};
Util.prototype.test = function(callback){
// when finished run the callback
callback();
};
My question is why I can't use the this keyword if the execution leaves the object then comes back? Please explain what should I change to be able to access this again.
You can bind "this" to function run, like this.
Page.onLoad = function(html){
this.name = 'Page 1';
Util.test(this.run.bind(this));
};
You can find more information for function "bind". https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
The top of the article references another post regarding 'this', so, I thought I'd just provide the code. If you read the other post and the articles linked within, you should be able to follow this code.
function UI(html) {
this.name = html;
}
UI.prototype = {
onLoad: function () {
util.test(this);
},
run: function () {
console.log(this.name);
}
}
var util = (function () {
return {
test: function (ui) {
if (ui && ui.run) {
ui.run();
}
}
}
})();
var page = new UI("index.html");
I have a class where I bind a method on initialization as follows -
function MyClass() {
this.onHttpCallback = _.bind(onHttpCallback, this);
}
function onHttpCallback(){
//...
}
How do I test if the onHttpCallback when called, is always called with an object of MyClass as context?
I am using sinon.js to mock and the following code doesn't work -
it('should be binded', function () {
//ctrl is an object of MyClass
var dummy_obj = {};
var spy = sinon.spy(ctrl.onHttpCallback);
spy.call(dummy_obj);
spy.alwaysCalledOn(ctrl).should.be.ok;
});
Update
As per the comments in the following answer, it seems like it is impossible to test the binding for a method.
My Take on the problem
//Source.js
function MyClass() {
}
MyClass.prototype.init = function(){
this.onHttpCallback = _.bind(MyClass.onHttpCallback, this);
}
MyClass.onHttpCallback(){
//...
}
//Test.js
it('should bind onHttpCallback', function () {
sinon.spy(_, 'bind');
ctrl.init();
_.bind.calledWith(ctrl.constructor.onHttpCallback, ctrl).should.be.ok;
_.bind.restore();
});
Works like a charm!
In case you wonder why this changes even though you clearly bound it to be MyClass before, that's because you use call with dummy_obj on the spy.
The spy wraps the original function, so it has no concept of that function's binding. It will still accept a different binding on the wrapper function, then try to call the original with that this, which is then ignored by the original function.
var context = {foo: 'bar'};
var original = _.bind(function () { console.log(this); }, context);
var spy = function (original) {
var spyFn = function () {
var _this = this;
spyFn.calledOn = function (ctx) { return ctx === _this; };
return original.apply(this, arguments);
};
return spyFn;
};
var originalSpy = spy(original);
// Will call the spyFn with a different `this`, but will not affect the original binding
originalSpy.call({something: 'else'});
>> {foo: 'bar'}
// Since the spy cannot know the original binding, it will think the assumption is false.
originalSpy.calledOn(context) === false;
This question already has answers here:
OOP. Calling methods from within methods
(4 answers)
Closed 8 years ago.
I'm just trying to figure out how I can call a javascript object method from within a method of the same object as below..
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003";
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
method2();
}
},
method2: function() {
this.method1();
}
}
Changed my question as I realise when using this.method2() it is refering to the WebSocker object.
There are a lot of answers in SO for problems like this, you should do a little research(on SO or on Google) before asking here.
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003",
self = this;
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
self.method2();
}
},
method2: function() {
this.method1(); //something like this would cause an infinite call stack, you should change this code
//this refers to the current object, so has properties method2 and method2
}
}
You need to reference to the current object using this, otherwise the JS Engine will look for a function named method1 in any of the higher scopes, all the way up to the global namespace. If such a function object (or such a name doesn't exist), method1 will be evaluated to undefined.
try this
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003";
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
testObject.method2();
}
},
method2: function() {
testObject.method1();
}
}
updated to match your current question: good part is you can add additional functions and call any one of them with this method;
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003",
self = this;
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
self['method2']();
}
},
method2: function() {
this['method1']();
}
}
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();
});
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I have the following Javascript code, and I'm trying to get a callback to work as shown below. I want to see an alert with "123" in it.
var A = function(arg){
this.storedArg = arg;
this.callback = function(){ alert(this.storedArg); }
}
var B = function() {
this.doCallback = function(callback){ callback(); }
}
var pubCallback = function(){ alert('Public callback') };
var a = new A(123);
var b = new B();
b.doCallback(pubCallback); // works as expected
b.doCallback(a.callback); // want 123, get undefined
I understand what is happening but I'm not sure how to fix it. How can I get a callback function that references my a object? In my case, I can make changes to A but not B.
So what you want is to pass the context to the doCallBack.
E.g.
doCallBack = function (callback, callee) {
callback.apply(callee);
}
So then you would do:
b.doCallBack(a.callback, a);
If you cannot modify the B then you can use closure inside A:
var A = function (arg) {
var self = this;
this.storedArg = arg;
this.callback = function () { alert(self.storedArg); }
}
You can create a variable that holds the wanted scope for this by putting it into variable that
var A = function(arg){
this.storedArg = arg;
var that = this; // Add this!
this.callback = function(){ alert(that.storedArg); }
}
Working demo here: http://jsfiddle.net/vdM5t/
I understand what is happening (during the 2nd callback, "this" is b and not a)
No, JS is no class-based language where something could happen. If function(){ alert(this.storedArg); is just called as callback(); (like in b.doCallback), the this keyword points to the global object (window).
To get around that, you'd have to change A to
var A = function(arg){
var that = this; // store reference to the current A object
this.storedArg = arg;
this.callback = function(){
alert(that.storedArg); // and use that reference now instead of "this"
};
}
If you don't expect the storedArg property to change, you could even make it more simple:
var A = function(arg){
this.storedArg = arg;
this.callback = function(){
alert(arg); // just use the argument of the A function,
// which is still in the variable scope
};
}
You need to pass the context you want the callback to execute in:
var B = function() {
this.doCallback = function(callback, context) {
callback.apply(context);
};
};
b.doCallback(a.callback, a); // 123
http://jsfiddle.net/a9N66/
Because inside A.callback function, this does not refer to A but to window object.
var A = function(arg){
this.storedArg = arg;
this.callback = function(){ alert(this.storedArg); }
-----------------------------------^-----------------
}
You can try this,
var A = function(arg){
this.storedArg = arg;
var that = this;
this.callback = function(){ alert(that.storedArg); }
}
var B = function() {
this.doCallback = function(callback){ callback(); }
}
var pubCallback = function(){ alert('Public callback') };
var a = new A(123);
var b = new B();
b.doCallback(pubCallback); // works as expected
b.doCallback(a.callback); // alerts 123
When you do this:
b.doCallback(a.callback);
that just calls a's callback function without telling it to use a for this; so the global object is used for this.
One solution is to wrap that callback up:
b.doCallback(function() { a.callback(); });
Other solutions include binding the callback to a, using jQuery.proxy() (which is just a fancy way of doing my first solution), or passing in a to doCallback and invoking callback on a using apply.