Accessing "this" inside an object - javascript

I'm having some trouble in a pretty big body of code I've been writing, and I've boiled it down to this:
var Thing = function(argument){
this.property = argument;
this.obj = {
func: function(){
return this;
}
};
this.func = function(){
return this;
};
};
I need to access this.property inside obj.func(). However, the value of this isn't what I expect it to be:
> var thing = new Thing("value")
> thing.func()
Thing {property: "value", obj: Object, func: function}
> thing.obj.func()
Object {func: function}
When I instantiate an instance called "thing" and call thing.func(), this holds the current instance of Thing, as it should. But when I call thing.obj.func(), it holds the value of thing.obj. What's going on? How do I access that value?

var thing = new Thing('value')
thing.obj.func() // this inside func will be the object before .func, which is thing.obj
One solution is to bind the function to your object:
var Thing = function(argument){
this.property = argument;
this.obj = {
func: function(){
return this;
}.bind(this); // bind it to the current object
};
this.func = function(){
return this;
};
};
Alternatively you could use a closure with a private copy of this:
var Thing = function(argument){
var that = this;
this.property = argument;
this.obj = {
func: function(){
return that;
}
};
this.func = function(){
return this;
};
};

Declare a local variable in the constructor, and assign it the value of this:
var Thing = function(argument){
var theThing = this;
You can then use "theThing" to refer to the constructed object in those functions.
In your existing code, when you call thing.obj.func(), the value of this in the function will indeed be this.obj. That's because the value of this is determined based on the circumstances of the function call. In that case, the reference to the function ("func") is obtained by traversing its property name on the "obj" object. Therefore, the "obj" object is what this references.

That's the way the this context works in Javascript. You'd need to store an intermediate reference to Thing:
var Thing = function(argument){
var self = this;
this.property = argument;
this.obj = {
func: function(){
return self;
}
};
this.func = function(){
return self;
};
};

this refers to the object which owns the function by default, in this case obj. Here is a workaround :
thing.obj.func.call(thing);

Related

Accessing properties in nested function returning another function

Is there a way to access properties in nested functions like that :
function func(){
this.new_func=function(){
console.log("something");
return 'something';
}
this.someValue=7;
return function(){
return "something_diff";
};
}
var obj=new func();
obj(); //works with returning "something diff"
obj.new_func(); // TypeError: obj.new_func is not a function
obj.someValue; // undefined
I need to delete whole "return function()..." part in order to access "someValue" and "new_func()". Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
When you have a constructor that returns an object, that object replaces whatever you assigned to this. So indeed, the members new_func and someValue are lost.
To combine the returned function together with the other members, you can do this:
function func() {
var f = function() {
return "something_diff";
};
f.new_func = function() {
console.log("something");
return 'something';
}
f.someValue = 7;
return f;
}
var obj = new func();
console.log(obj());
obj.new_func();
console.log('someValue:', obj.someValue);
You can do it like this:
var parentFunction = function() {
var nestedFunction = function() {
var value = "nestedValue";
var moreValues = "more values";
return {
value: value,
moreValues: moreValues
}
}
var anotherNestedFunction = function() {
var anotherValue = "nestedValue";
return anotherValue;
}
return {
nested: nestedFunction,
another: anotherNestedFunction
}
}
Then:
var newFunction = new parentFunction();
var nested = newFunction.nested();
console.log("nested value: ", nested.value);
console.log("another nested value: ", newFunction.another);
Here is a working example:
Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
Because of the pharenteis:
var obj=new func();
Basically you're firing your function and what is stored the variable obj is what the "func" returns.
In order to access to private properties, you should look at the Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

About javascript prototype

A strange problems about javascript prototype :
(function(w){
if(!w)
return;
var TestJS = function(){
};
TestJS.prototype = {
data:{},
initData:function(){
this.data={
val_name_1 : 1,
val_name_2 : 2,
val_name_3 : "hello-3"
};
console.log(this.data);
return this;
},
TestChildJS:{
initChild:function(){
console.log(TestJS);
console.log(TestJS.data);
console.log(new TestJS().data.val_name_1);
console.log(TestJS.data.val_name_1);
}
}
};
window.TestJS = new TestJS();
})(window);
why 'TestChildJS' can not get 'val_name_1'?
TestJS.initData();
console.log(TestJS.TestChildJS.initChild());
console pic
so I have to write my code like that:
(function(w){
if(!w)
return;
var TestJS = function(){
};
TestJS.prototype = {
data:{},
initData:function(){
this.data={
val_name_1 : 1,
val_name_2 : 2,
val_name_3 : "hello-3"
};
console.log(this.data);
this.TestChildJS.initParentData(this);
return this;
},
TestChildJS:{
parentData:{},
initParentData:function(parent){
this.parentData = parent.data;
return this;
},
initChild:function(){
console.log(this.parentData);
}
}
};
window.TestJS = new TestJS();
})(window);
How to use the first way can get the content of the second way?
why 'TestChildJS' can not get 'val_name_1'?
when:
TestJS.initData();
is run, it adds a data property to the TestJS object (the one assigned by window.TestJS = new TestJS()). That property isn't inherited by any other object.
When:
console.log(new TestJS().data.val_name_1);
is run, the object returned by new TestJS() has not had it's initData method called yet, so it doesn't have a data property and it doesn't inherit it from the constructor (because the property is directly on the constructor itself, not its prototype).
Note also that assigning a new object to this.data creates a property directly on the instance, so adding to this.data is modifying the instance's data object, not the one on the constructor's prototype.
The patterns in your code (especially the second one) seem unnecessarily convoluted.
It has to do with the scope of the IIFE. A variable declared inside a closure shadows any outer variable with the same name. Since after the IIFE executes you no longer have access to its scope, TempJS inside it will always be a function constructor -- not an instantiated object.
Consider this example:
var i;
var func = (function(){
i = 1;
return function() {
console.log(i)
};
})();
func(i); // 1
i = 2;
func(i); // 2
If I re-declare the i variable inside the closure, look what happens:
var i = 1;
var func = (function(){
var i = 1;
return function() {
console.log(i)
};
})();
func(i); // 1
i = 2;
func(i); // 1
So one solution to your problem would be to declare TestJS once before the IIFE.
var TestJS;
(function(w){
if(!w)
return;
TestJS = function(){
};
// ...
TestChildJS:{
initChild:function(){
console.log(TestJS.data.val_name_1);
}
// ...
window.TestJS = new TestJS();
})(window);
TestJS.initData();
console.log(TestJS.TestChildJS.initChild()); // 1
Notice that I removed console.log(new TestJS().data.val_name_1);. TestJS is no longer a constructor function, so that line will throw.
Another solution is to assign the empty function expression to window.TestJS inside the closure, instead of var TestJS. Doing so will not create a local TestJS name and will therefore prevent the ambiguity.

How to create an object in which the object itself is a function?

I have a javascript object from which I created from a constructor.
var obj = new Obj();
It has several functions but I wish to also use it as follows;
obj();
Obj is defined as:
function obj() {
this.blah = function () {
};
}
How can I do this?
It would be easier to have a function you call which returns an arbitrary function that is treated as both an object and a function. Each one would be unique, and you would not need to use new.
function Obj() {
var ret = function(){
console.log('Hello');
};
ret.blah = function () {
console.log('World');
};
return ret;
}
var obj = Obj();
obj();//Hello
obj.blah();//World
You can create properties on function objects. For instance, if you have the function
function foo() {
return "bar";
}
You can set a property on foo.
foo.baz = 42;
So now you can call foo and get the result "bar", and you can access its baz property.
I think you are looking for 'closure' here.
Try:
function MyObject(/* constructor params */) {
//assign properties to 'this'
return function() {
return this; //<== placeholder implementation
}.bind(this);
}
var obj1 = new MyObject();
Now if you do console.log(obj1) you will see function() { [...] } and you will be able to do obj1() to execute your function.
As a bonus in the code above, I also added this binding in the 'closure' as you will need it in most cases that you are doing anything interesting.

How to set var self = this; from outside the function?

I have a class-like function
var myapp = function() {
this.method = function() {
//Do something...
}
}
To reference myapp from within methods, the first line in the myapp function is
var self = this;
So a method in myapp can reference the "class" safely
this.anothermethod = function() {
self.method();
}
The full code:
var myapp = function() {
var self = this;
this.dosomething = function(Callback) {
Callback();
}
this.anothermethod = function() {
//Pass a callback ("self" is required here)...
this.dosomething(function() {
self.complete();
)};
}
this.complete = function() {
console.log('All done!');
}
}
My question is: can I assign var self = this; from outside the declaration of myapp? I don't want to set self every single time I write a "class".
Kind of like this:
var library = function() {
this.loadclass = function(Name) {
var tempclass = window[Name];
library[Name] = new tempclass();
library[Name].self = library[Name];
}
}
var myapp = new library();
myapp.loadclass('myapp');
myapp.myapp.dosomething();
It doesn't work as expected. self equals window for some reason.
I know it's a little abnormal programming, but can it be done?
Note about using self: I remember why I started using it. I wanted to reference the base class (this) from within callbacks inside methods. As soon as you try to use this within a function within a method, it then references the method, not the base class.
Unless you are detaching the methods from the object and calling them as plain functions, you don't need a self variable at all. The method can reach its object using the this keyword:
var myapp = function() {
this.method = function() {
//Do something...
}
this.anothermethod = function() {
this.method();
}
}
No, you can't really; not the way you're creating objects at least.
You can sort of do this, by enumerating all the functions on the object and binding them to the object itself. Something like this:
Object.keys(obj)
.filter(function(n) { return typeof obj[n] == "function" })
.forEach(function(n) { obj[n] = obj[n].bind(obj) })
This function will go over the public, enumerable properties of obj and make sure that any functions on it are bound to obj; i.e. this is now bound to obj.
A primer on this
When you call new, this within the constructor gets bound to the newly created object. If you do need a reference to this as it was bound at constructor time, you do need to keep away a reference to it.
Functions in JavaScript are bound to wherever it is called. Here's an example:
var foo = new function() {
this.bar = function() {
return 'bar'
}
this.baz = function() {
return this.bar()
}
}
console.log(foo.bar()) // bar
console.log(foo.baz()) // bar
var bar = function() {
return "window"
}
var baz = foo.baz
console.log(baz()) // window
When we call foo.baz() it'll look to foo for the implementation of bar, but when calling foo.baz through a "detached" reference, it'll look to whatever the global object is (in this case the browser window object) and call bar from there. Because we defined bar in the global context, it then returns window.
The practice of assign a variable called self is so that it doesn't matter how you call your methods, because you always reference the this at the time of creation through the self variable. You don't have to write things this way, but then you should understand that references to this may change under your feet.

Using _self so I always can have object context

What is the proper way to use _self to always have access to an object? Is using _self ok, or is it bad practice?
I want a good way to get at myObject's attributes and methods, even from functions that aren't called in myObject's context. There are solutions like .bind(this), using _self, and jQuery's $.proxy().
For example:
var myObject = {
name: 'Tyrion',
alias: 'imp',
_self: function() {
return this;
},
// I know this context is fine, but let's pretend it's being called from elsewhere.
getAlias: function() {
var _self = myObject._self();
return _self.alias;
}
}
In order to do what you're looking to do, you'd have to change a few things. #elclanrs is right about what your this context is. I'll put two options below.
var myObject = {
name: 'Tyrion',
alias: 'imp',
// I know this context is fine, but let's pretend it's being called from elsewhere.
getAlias: function() {
// you'd have to do this in every method.
var _self = this;
return _self.alias;
}
}
The other option is a bit different, and not as usable, but I'm adding it so you can see it:
var myObject = function() {
var _self = this;
_self.name = 'Tyrion';
_self.alias = 'imp';
_self.getAlias = function() {
return _self.alias;
};
};
in the second instance, getAlias would be better as a prototype method, but you won't have access to the _self variable, only this.
You can do this too but I wouldn't necessarily recommend it.
var obj = {
_self: this.obj, // if you don't have .obj it points to window
thing: 'thingy',
alsoThis: function() {
return 'another thing'
}
};
obj._self;
It's also possible that since it's not within a closure, or a function in otherwords, the context of this._self may be incorrect if the context is changed by the scope which its referenced in.
Generally, I just do var _self = this; right before a function which I am nesting another function within that requires the this context outside the parent function since the nested one will not have the ability to access the value of this being nested.
That's usually not too common in my experience, and you really shouldn't be declaring such a property/var that needs to be used for the purpose that _self vars serve. It isn't a good practice and would be best to not do.
What if you run into a situation where you need to have _self = some other context?
thisis determined by the invocation of the function. (aka, the way the function is called) See my other answers for more details.
var myObject = {
name: 'Tyrion',
alias: 'imp',
_self: function () {
return this;
},
// I know this context is fine, but let's pretend it's being called from elsewhere.
getAlias: function () {
var _self = myObject._self();
return _self.alias;
}
};
//member invocation
console.log(myObject._self() === myObject); // true
var aFucntion = myObject._self;
//functional invocation
console.log(aFucntion() === myObject); // false
console.log(aFucntion() === this); //true
Instead of worrying about the context of this, a workaround is to assign this to a value in an outer function, and then access that value in an inner functions. This is called closure
var MyObject = function (title) {
var _self = this,
helper = function () {
return _self.title + " " + _self.name;
};
this.title = title;
this.fullName = function () {
return helper(); //functional invocation
//if helper used this, this would be global
};
this.name = 'Tyrion';
this.alias = 'imp';
this.getAlias = function () {
//access to _self through closure
return _self.alias;
};
};
//constructor invocation
var aObject = new MyObject("Mr.");
console.log(aObject.getAlias()); //imp
console.log(aObject.fullName()); //Mr. Tyrion
FYI:
If _self returns myObject, context would not mater.
_self: function () {
return myObject;
}

Categories