if in a literal object i try to reference a function using "this" inside a nested property/function, this don't work. Why? A nested property have it's own scope?
For example, i want to call f1 from inside d.f2:
var object = {
a: "Var a",
b: "Var b",
c: "Var c",
f1: function() {
alert("This is f1");
},
d: {
f2: function() {
this.f1();
}
},
e: {
f3: function() {
alert("This is f3");
}
}
}
object.f1(); // Work
object.d.f2(); // Don't Work.
object.e.f3(); // Work
Thanks, Andrea.
this refers to d inside f2 and not object. You could store a reference to object, or call object directly, or use call/apply to call the function and explicitly tell it what this means inside that function:
object.d.f2.call(object); // now this refers to object inside f2
Here's an alternative approach which doesn't change the context of this inside f2(), based on #slaver113's idea:
var object = (function() {
var _this = {
f1: function() {
alert('This is f1');
},
d: {
f2: function() {
_this.f1();
}
}
}
return _this;
})();
object.d.f2(); // Alerts 'This is f1'
Related
Given the following Javascript example:
var obj = function() {
var _c = function() {
console.log("c")
}
return {
a: function() {
b();
},
b: function() {
console.log("b");
},
c: function() {
_c();
}
}
}();
calling obj.a() gives an error that b is not defined. This error can be solved by changing b() to this.b(). Can anyone help explain why this is necessary while c can access _c?
_c is a local variable that is in scope when _c() is executed. b is not; it is only found on the object itself, so it can be found by this.b.
This confusion is indicative of transplanting object-oriented concepts from other languages to JavaScript. There are no private and public members in JS, and you can't call methods on the same object by leaving the object prefix. There's just local variables (v when var v or let v), global variables (v when no var v or let v) and object properties (obj.prop). A method is object property that contains a function; no more, no less.
Need to use this.b because you are returning an object and 'b' in this context is the property of that object. you need to explicitly stated that using this
While _c is a local function defined inside the closure, you can access access it directly in the return object and does not refer to the return object.
You should not call b() directly.
var obj = function() {
var _c = function() {
console.log("c")
}
return {
b: function() {
console.log("b");
},
a: function() {
this.b();
},
c: function() {
_c();
}
}
}();
obj.a();
Explanation added :
You are calling b() inside a function where b() is defined on the object. So to call the function b() inside any other function, you should call b with reference to the object like
this.b();
I have something similar to this:
var a = (function () {
return {
b: 1,
c: function () {
console.log(this.b);
}
};
})();
So,
a.c(); // = 1
But if I do
b = 2;
a.c.apply(this); // = 2
Is it possible to preserve the context of "this" inside "a.c()" without changing (too much) the structure of "a" object? I don't have the control of the function's call, so I'd need a workaround to deal with this inside the object itself.
UPDATE:
To be more specific, this is the structure of my files:
Structure 1 (singleton like pattern):
var a = (function () {
var _instance;
function init() {
return {
b: 1,
c: function () {
console.log(this.b);
}
};
}
return {
getInstance: function () {
if (_instance === undefined) {
_instance = init();
}
return _instance;
}
}
})();
Structure 2:
var b = {
c: 1,
d: function () {
console.log(this.c);
}
};
SOLUTION:
I have implemented a solution based on Mahout's answer, spliting the return statement inside init(), so it remains safe for the object context (and the instance) under any situation.
For singleton pattern:
var a = (function () {
var _instance,
self;
function init() {
return self = {
b: 1,
c: function () {
console.log(self.b);
}
};
}
return {
getInstance: function () {
if (_instance === undefined) {
_instance = init();
}
return _instance;
}
};
})();
For object literal:
var b = (function () {
var self;
return self = {
c: 1,
d: function () {
console.log(self.c);
}
};
})();
So
a.getInstance().c(); // 1
a.getInstance().c.apply(this); // 1
setTimeout(a.getInstance().c, 1); // 1
$.ajax({ complete: a.getInstance().c }); // 1
You can slightly change the way you are returning the object from the anonymous function:
var a = (function () {
var result = {};
result.b = 2;
result.c = function() {
console.log(result.b);
};
return result;
})();
This should have the same effect, however it does remove the use of this.
If you can't afford to change the structure of a this much, then alternately you can (much) more dangerously use:
a.c.apply = function() { // Stops the apply function working on a.c by overriding it
return a.c();
}
If you choose this though you must be wary that anytime a.c.apply is used it will no longer work 'as expected' - it will fix the issue presented here though.
I made this pen to illustrate the differences,I hope it helps:
http://codepen.io/dieggger/pen/BNgjBa?editors=001
var a = (function () {
return { b: 1,
c: function () {
console.log(this.b);
}
};
})();
a.c(); //prints 1
b = 2; // global variable "b" which is being hoisted BTW
// The following will act like this:
//it throws "cannot read property 'apply' from undefined"
//though it prints "1" since the first part invokes the "c" function
//inside of the"a" module which has the console.log
a.c().apply(this);
//In this case "this" is the window object which has the variable "b"
a.c.apply(this); // it'll print 2
You can do this:
var a = (function ()
{
return {
b: 1,
c: function ()
{
console.log(this.b);
}
}
})();
var decorator = function() { return a; };
var b = 2;
decorator.call(this).c(); // => 1
Basically it looks like you want to bind the IIFE, and not the object that it returns, to the outside scope, so that the nested returned object preserves the value of the interior b.
Given the following object literal how do I call c from within b?
Update 1 - missed one thing I use JQuery load, which changes the context:
var a = {
b: function (e) {
$o.load(path, function (e) { // need to call c from here });
},
c: function (e) {
}
};
You should be able to do a.c() inside .b:
var a = {
b: function(e) {
a.c();
},
c: function(e) {}
};
a.b(); // calls c
Also, this will be bound to the a object which will allow you to access its properties using this.property:
b: function(e) {
this.c();
},
Try this:-
var a = {
b: function(e) {
a.c();
},
c: function(e) {}
};
a.b();
var a = {
b: function (e) {
a.c();
},
c: function (e) {
// magic goes here
}
};
a will be a closure so it's accessible in the functions (that is, a is defined in an wide, outer scope, which the narrower, inner scopes in each function inherit). Calling context is irrelevant; closures are formed when and where the functions are defined, so inside b, object a will always stay the same (unlike this, which can change).
From the method b you may call c using this.c as long as they are on the same object. However for the function expression being passed to $o I would suggest you bind the this pointer of b to it. Thus you do:
var a = {
b: function (e) {
$o.load(path, function (e) {
this.c();
}.bind(this));
},
c: function (e) {
}
};
Edit: The way you're creating and using this object is fragile. The this pointer may point to anything, such as when you unbind the methods or call it with a different context. Even using a.c isn't foolproof as other code may change a and when the method b is called a will point to something else.
I would do something like this:
var a = function (a) {
a.b = function (e) {
$o.load(path, function (e) {
a.c();
});
};
a.c = function (e) {
};
return a;
}({});
This code can not be tampered with and allows you to create private variables and closures.
The easiest solution which requires the minimum changes is to use jQuery.proxy():
var a = {
b: function (e) {
$o.load(path, $.proxy(function (e) { this.c(); }, this));
},
c: function (e) {
}
};
A = {
f1: function() {
return {
a: function(){ alert('sss'); }
}
}
}
A.f1().a();
Why is it used in this way?
Why is the method a() bound to A.f1()?
Member function f1 of A returns an object literal with its member a set to a function. It could've also been written as:
A = {
f1: {
a: function() { alert('sss'); }
}
}
A.f1.a();
Returning an object could in this case be personal preference. There is no functional difference between these two examples.
When you do as below:
var x = A.f1();
What you get on to x is the object returned by the f1 function. which is:
{
a: function(){ alert('sss'); }
}
now the object 'x' has function a() on it. You can call that function as:
x.a();
Which is exatly similar to:
A.f1().a();
I am having a JavaScript namespace say
A={
CA: function() {
this.B();
},
B: function() {
var test='test';
var result='t1';
C: function() {
this.test='test1';
.....
.....
return 'test1';
}
result=this.C();
return result;
}
}
Now when I am executing such code it is giving that TypeError: this.C is not a function. Can somebody tell me why it is so. I know it is something related with lexical scoping but am unable to understand this.
You have to be careful when you use this to identify anything in Javascript because each time you change scope "this" changes.
Assigning the 'this' reference to it's own variable helps get around this.
var a = new function() {
var self = this;
self.method = function() { alert('hiya'); };
var b = function() {
this.method(); // this isn't 'a' anymore?
self.method(); // but 'self' is still referring to 'a'
};
};
I think the problem is that when this.C() is executed inside the function referred to by B, this refers to the object that contains B, that is, object A. (This assumes B() is called within the context of A)
The problem is, C does not exist on the object A, since it's defined within B. If you want to call a local function C() within B, just use C().
EDIT:
Also, I'm not sure what you've posted is valid JavaScript. Specifically, B should be defined this way, since you can't use the object:property syntax within a function.
B: function()
{
var test='test';
var result='t1';
var C = function()
{
this.test='test1';
return 'test1';
}
result=C();
return result;
}
I am actually surprised that your code doesn't give you error on the 'C:' line.
Anyway, your syntax to define a function is not correct. Define it using the var keyword. Also, notice that I created the 'closure' so that the function C can access 'this'. See the code below:
A={
CA: function()
{
this.B();
},
B: function()
{
var test='test';
var result='t1';
var self = this;
var C = function()
{
self.test='test1';
.....
.....
return 'test1';
}
result=C();
return result;
}
}
If you want to assign C to 'this' object, you can also do:
A={
CA: function()
{
this.B();
},
B: function()
{
var test='test';
var result='t1';
var self = this;
this.C = function()
{
self.test='test1';
.....
.....
return 'test1';
};
result= this.C();
return result;
}
}
Solution for calling methods from another method. (Essentially the pointer "this" must be assigned to a variable and the new variable used in place of this.)
function myfunction(){
var me = this;
me.method1 = function(msg){ alert(msg); }
me.method2 = function(){
me.method1("method1 called from method2");
}
}
var f as new myfunction();
f.method2();
This example shows how one can call a method from within another method or from outside using an instance of the function.