I have one class with 2 privileged methods :
function ABC() {
this.methodA = function(){
}
this.methodB = function(){
}
}
Is it possible to call methodA inside methodB, if so how to call that?
Yes, but you need a reference to it. If methodB will always be called with the same context, then you can just call methodA from methodB using this.methodA();:
var a = new ABC;
a.methodB(); // Correctly calls methodA();
var func = a.methodB;
func(); // Fails because `this` is not referring to `a` anymore
It will work both ways if you do something like this:
function ABC() {
var methodA = this.methodA = function(){
}
this.methodB = function(){
methodA();
}
}
function ABC() {
var self = this;
this.methodA = function(){
}
this.methodB = function(){
self.methodA();
}
}
Related
Let's assume we have the following code:
var MyClass = (function(){
var _this;
function MyClass(inputVal){
_this = this;
this.value = inputVal;
}
MyClass.prototype.getValue = function(){
return this.value;
}
MyClass.prototype.getValue2 = function(){
return _this.value;
}
return MyClass;
})();
Let's make two instances of the class:
var instance1 = new MyClass(10);
var instance2 = new MyClass(20);
Now if we console.log() the values we see that:
instance1.getValue(); // 10
instance1.getValue2(); // 20
var MyClass = (function(){
var _this;
function MyClass(inputVal){
_this = this;
this.value = inputVal;
}
MyClass.prototype.getValue = function(){
return this.value;
}
MyClass.prototype.getValue2 = function(){
return _this.value;
}
return MyClass;
})();
var instance1 = new MyClass(10);
var instance2 = new MyClass(20);
console.log(instance1.getValue());
console.log(instance1.getValue2());
Why is that happening? It looks obviously that the _this variable gets the latest created instance properties. How to fix that? I need to keep a copy of this. Thanks!
Edit:
Here's the real situation
var HoverEffects = (function(){
var _this;
function HoverEffects($nav){
_this = this;
this._$activeNav = $nav.siblings('.active_nav');
this._$hoverableLis = $nav.find('>li');
this._$activeLi = $nav.find('>li.active');
if(!$nav.length || !this._$hoverableLis.length || !this._$activeNav.length || !this._$activeLi.length) return;
if(this._$activeNav.hasClass('bottom')){
this._$activeNav.align = 'bottom';
this._$activeLi.cssDefault = {
left: this._$activeLi.position().left,
width: this._$activeLi.width()
};
}
else if(this._$activeNav.hasClass('left')){
this._$activeNav.align = 'left';
this._$activeLi.cssDefault = {
top: this._$activeLi.position().top,
height: this._$activeLi.height()
};
}
else{
return;
}
this._$hoverableLis.hover(
function(){
// How to set the correct this inside this function?
if(this._$activeNav.align === 'bottom'){
this._$activeNav.css({
left: $(this).position().left,
width: $(this).width()
});
}
else if(this._$activeNav.align === 'left'){
this._$activeNav.css({
top: $(this).position().top,
height: $(this).height()
});
}
},
function(){
// Same here, wrong this
this._$activeNav.css(this._$activeLi.cssDefault);
}
);
}
return HoverEffects;
})();
var sideNavHoverMagic = new HoverEffects($('#side-navigation'));
var primaryNavHoverMagic = new HoverEffects($('#primary-navigation'));
Why is that happening?
Every time you call new MyClass, _this = this gets run. The second time overrides the first time.
So _this refers to new MyClass(20), which means that when you call getValue2 from any MyClass instance, 20 will be returned because all MyClass instances are referring to the same _this value.
Based on commentary on the Question:
If you're attempting to pass a function bound to the appropriate context there are a variety of ways to make sure that this refers to the right object. Before continuing, please read "How does the 'this' keyword work?", because there's no reason for me to repeat all of it here.
If you're binding event callbacks such as in a constructor:
function Example(something) {
something.addEventListener(..event.., this.callback, false);
}
Example.prototype.callback = function () {
this.doStuff();
this.doMoreStuff();
};
The callback will have the wrong this value because it's not being called as this.callback, it's just being called as:
fn = this.callback;
fn(); //no reference to this
You can get around this in a number of ways.
Function.prototype.bind
You can bind the callback for every instance on their respective instance. This is very concise:
function Example(something) {
//generate a new callback function for each instance that will
//always use its respective instance
this.callback = this.callback.bind(this);
something.addEventListener(..event.., this.callback, false);
}
Example.prototype.callback = function () {
this.doStuff();
this.doMoreStuff();
};
that = this
You can create the callback (closure) within the constructor and reference a variable inside the constructor.
function Example(something) {
//every Example object has its own internal "that" object
var that = this;
this.callback = function () {
//this function closes over "that"
//every instance will have its own function rather than
//a shared prototype function.
that.doStuff();
that.doMoreStuff();
}
something.addEventListener(..event.., this.callback, false);
}
() => {} (Fat Arrow Syntax)
If you're using ES2015 you can use "fat arrow" syntax for creating lambdas that don't create a new context:
function Example(something) {
this.callback = () => {
//the callback function doesn't create a new "this" context
//so it referes to the "this" value from "Example"
//every instance will have its own function rather than
//a shared prototype function.
that.doStuff();
that.doMoreStuff();
}
something.addEventListener(..event.., this.callback, false);
}
This is my IIFE function
var test = function(){
console.log('fire');
}();
It invokes at start. But how do I call this again?
var fireTestFn = function(){
test();
}
fireTestFn(); // test() is not a function
JSbin https://jsbin.com/cesuzimogu/edit?js,console
You could return test from inside using a named function expression.
var test = function fn(){
console.log('fire');
return fn;
}();
The result of the IIFE will be assigned to test, which is obviously not a function, because you're not returning a function from the IFEE (or anything for that matter). Keep it simple; what you want is a named function you can call anytime as many times as you want:
function test() {
console.log('fire');
}
test(); // call as often as you want
Something like this will work
var myNamespace = {};
(function(ns) {
ns.test = function(){
console.log('fire');
};
/*ns.myOtherFunction = function(var1) { }*/
})(myNamespace);
var fireTestFn = function(){
myNamespace.test();
};
fireTestFn();
See example here: https://jsbin.com/helumoveqe/edit?js,console
As the error says
test() is not a function
When you self-invoked the function, the result was stored into test.
In order to be able to use test as a function and call repeatedly elsewhere, do not self-invoke
var test = function(){
console.log('fire');
};
or have the function return an inner function
var test = function () {
return function () {
console.log('fire');
}
};
I have following function:
function outer(){
index = 1;
inner();
}
function inner(){
alert(this.index);
}
I want to see 1 in alert. Also I want that function inner would without arguments.
Is it possible?
You can specify the context manually when invoking a function with Function.prototype.call
function outer () {
this.index = 1;
inner.call(this);
}
If you define the index variable outside the scope of the outer() function, it will be available to both functions.
var index = 0;
function outer() {
index = 1;
inner();
}
function inner() {
alert(index);
}
outer(); // alerts "1"
You could use apply the functions to an object, which would control the context:
function blarg() {
var self = this;
self.outer = function(){
self.index = 1;
self.inner();
};
self.inner = function(){
alert(self.index);
};
self.outer(); // Call it in the constructor
self.go = function() {
self.outer(); // Or in another function
};
}
var blargObj = new blarg();
blargObj.go();
Why I cannot do this?
var MyObject = {}
MyObject.foo = function(){
this.sayhello = function(){
alert('Hello');
}
}
MyObject.foo.sayhello();
Any ideas on how it could be done?
within foo, this references MyObject, which means that after:
MyObject.foo();
you can call:
MyObject.sayhello();
If you want to be able to call MyObject.foo.sayhello(), you need sayhello to be a function on MyObject.foo:
var MyObject = {}
MyObject.foo = function () {...};
MyObject.foo.sayhello = function () {
alert('hello');
}
If you don't need foo to also be a function, you could simply declare:
var MyObject = {
foo: {
sayhello: function () {
alert('Hello');
}
}
}
which would allow you to call:
MyObject.foo.sayhello();
You have to call MyObject.foo() first so that the this.sayhello function actually gets added. Then you should beable to call MyObject.foo.sayhello();
var MyObject = {}
MyObject.foo = {
sayhello: function(){
alert('Hello');
}
}
MyObject.foo.sayhello();
That is because the function does not yet exist. Therefore you must call foo first. What you do is calling the function sayhello from the property foo. But you don't have a property foo. You have a function foo.
But you can also do this and make it chain, like jQuery does:
var MyObject = {}
MyObject.foo = function(){
this.sayhello = function(){
alert('Hello');
}
return this;
}
MyObject.foo().sayhello();
Make a function sayhello inside object foo, so not a chained function. But an object inside an object with a function.
var MyObject = {
foo : {
sayhello : function(){
alert("hello");
}
}
}
MyObject.foo.sayhello(); // Now does work!
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.