Binding "this" in JS object - javascript

I'm trying to "reverse-engineer" JS code of one of my favorite web game. I'm not beginner in web developing, but JS is not my strength (nor english, sorry), I'm more backend programmer.
The code is full of such "objects":
var Foo = {
prop: {...},
prop2: [...],
bar: function(val) {
this.prop.k = val;
Foo.doSomething();
},
doSomething: function() {
var my = Foo.prop;
...
return this.prop2;
},
};
...somehere on the page...
<input type="text" name="in" value="Pretty text" onclick="Foo.bar(this.value)" />
As far as I understand it, it's object Foo made from anonymous class, making it somewhat static. But I'm very confused by using this and Foo as object name. It seems to me randomly used, one or another. It's not related to properties or functions, both are used both ways. Even in the same method is used both. I think, that in this case are this and Foo the same. When I try to make such an objects and dump them in console, this and Foo returns the same.
Can anyone explain to me, where can be a difference, please? Or can it be just something lost in translation, because original code is minified?
Thanks.

var x = {
something: 'something',
log: function() {
return this.something
},
log2: function() {
return x.something
}
}
If you run the above code, you can see that the log() method and log2() method both return the same result. Which concludes that this and x refers to the same object in this scenario. Yours might be the same case if I'm not wrong.

Here are some cases when this and the name of the object are not referring the same object.
let Foo = {
prop: 'a',
log() {
console.log(
'this.prop is', this.prop, ', Foo.prop is', Foo.prop
);
}
},
Bar = {
prop: 'b',
mock: Foo.log
},
Baz = Object.create(Foo, {
prop: {
value: 'c'
}
});
Foo.log();
Bar.mock();
Baz.log();
// You can also bind the this reference when calling
Bar.mock.call(Baz);
Baz.log.apply(Bar);
In Baz case, the original Foo.prop preserves its value, since setting a property with the same name in an instance makes it an own property of that instance, the value of the property in the prototype is not overridden.
All of these scenarios help you to reuse already written code, hence reducing the code. Whether this was a goal in your favorite web game or not is not known, when reading the short examples only. With the given code, there's no difference.

Related

Calling Objects as Functions JavaScript

In JavaScript, functions are objects, except that they can be called and executed, this is comparable to overloading the () operator of an object in other languages.
Can I apply this functionality to any object?
I know there isn't a property like [Symbol.call] or something comparable. If there is something comparable, I would greatly appreciate it if you could inform me of what it is.
If declaring objects and assigning properties to them is my only option that's okay, but I'd prefer if this could be done using a class, like C++ for example.
I could do something like this
function func(...) {
...
}
func.foo = x,
func.bar = y;
but it would be more preferable if I could do something more like
const func = {
[`()`]: function(...) {
...
},
foo: x,
bar: y
};
Can I apply this functionality to any object?“
No. As of ECMAScript 10th Edition (2019) this is not possible.
The ()-operator/expression construct can only be used on function objects.
This is because it uses the [[Call]] internal method. Section 7.3.12 Call indicates this is only defined for function objects and the specification makes no provision to add the [[Call]] internal method to other objects.
7.3.12 Call ( F, V [ , argumentsList ] )
The abstract operation Call is used to call the [[Call]] internal method of a function object. [..]
The specification is written in such a way that a future revision might add generalized support.
Anyway, as shown in the question, function objects can be mutated as a proxy: so carry on, flavoring as desired.
So it might help to give a more concrete example of what you want, but here's a few options that can pretty much replicate, and more, the things you have mentioned.
This is like your first example. I'm guessing this isn't what you actually want. Notice foo and bar are static.
const func = Object.assign( function(in){ console.log(in, func.foo, func.bar) }, { foo: x, bar: y } );
This one is probably closer to what you'd see in a class with () overloaded
const func = ( function(in){ console.log(in, this.foo, this.bar) } ).bind({ foo: x, bar: y });
Or something like this:
o = o => o['()'].bind(o)
const func = o({
"()": function(x){ console.log(x, this.foo, this.bar) },
foo: 1,
bar: 2
});
Takes the property value of '()', and binds this to the object. You also use Object.assign to copy the properties on to the function.
Other way of expression
const func = (x,y) => ({
foo: x, bar: y,
plusone: () => func(x+1,y+1)
showfoo() { return this.foo }
})
You could do stuff like this, for example from within an outer IIFE function, returning different types of object formatted data. The IIFE can be passed as a parameter to another function in a modular script file, where controller's return data can be reached with dot notation controllerAsParameter.dostuff or controllerAsParameter.getNumbers.percentage.
Is my interpretation correct, based on your last example, that you're after something like this?
const controller = (function() {
return {
doStuff: function() {
data.everything.forEach(function(foo) {
foo.calculateStuff(data.totals.bar);
});
},
getNumbers: function() {
return {
abc: data.abc,
totalDef: data.totals.def,
totalGhi: data.totals.ghi,
percentage: data.percentage
}
}
}
})();

Scoping issue with jQuery Function Extend

I'm experimenting with having an object extended and that object containing multiple functions. I'm having trouble explaining it, so let me just show you my code. Let's start with what is working:
(function ($) {
$.fn.extend({
Hello: function() {
console.log("Well, hello back!");
console.log($(this));
}
});
})(jQuery);
In HTML:
<script>
$(document).ready(function() {
$('.target').Hello();
});
</script>
The console log of $(this) is giving me the callee element "target", as expected.
Now, let's try it this way:
(function ($) {
$.fn.extend({
Response: {
Hello: function() {
console.log("Well, hello back!");
console.log($(this));
}
}
});
})(jQuery);
In HTML:
<script>
$(document).ready(function() {
$('.target').Response.Hello();
});
</script>
This is still spitting out "Well, hello back!" in the console, but I'm no longer getting the callee when I use $(this). I'm instead getting the Hello function.
How do I get the callee? I've tried this.parent, which does not work. Please help!
As others have pointed out, you can't really do what you are asking for, but you can do something that looks almost the same.
By capturing 'this' with a closure, you can access it further down in your object structure:
$.fn.extend({
Response: function () {
var self = this;
return {
Hello: function () {
console.log(self);
}
};
}
});
$('.target').Response().Hello();
You can't really do that -- it's quite impractical.
There are special rules in JavaScript that dictate what the value of this is when a function is called. One of them is that whenever there is an expression of the form foo.bar() then bar is called and this evaluates to foo for the call.
So when you are run $('.target').Response.Hello() what happens is that this is equal to $.fn.Response, and not the jQuery object that wraps .target.
You could get the desired behavior by overriding the assignment of this by using e.g. Function.prototype.call:
var $target = $('.target');
$target.Response.Hello.call($target);
Of course this is cumbersome, but there's not much choice. The only other solution that would theoretically work I can think of involves the idea of injecting code into jQuery so that $$.Response resolves to something different for each jQuery instance $$. This is probably overkill if the aim of the question is simply to achieve a preferred syntax.
Unfortunately, there is no parent-child relationship between objects in JavaScript. The reason for this is that it is possible for the same object to have multiple parents. This can be difficult to grasp when you are dealing with nested objects (as is the case in the code example you have provided). Consider the following example:
var foo = {
bar: {
baz: 1,
biz: function() { console.log(this) }
}
}
foo.bar.biz();
=> { baz: 1, biz: [Function] }
var y = {};
y.foobar = foo.bar
y.foobar.biz();
=> { baz: 1, biz: [Function] }
In both cases, a call to biz will return only the immediately containing object. To attempt to access the parent of the bar object through the this keyword would be ambiguous because there are multiple parents that have been specified.
In your case, your best option would be to use the call function to explicitly specify the function context:
var $target = $('.target');
$.fn.Response.Hello.call($target);

Accessing Data Member

I've create a namespace like the one below.
var Constants = {
Foo: 'bar',
SomeVar: {
key: 'value'
}
};
What I want to do is:
var Constants = {
Foo: 'bar',
SomeVar: {
key: Constants.Foo + 'value' // Notice the Constants.Foo
}
};
What I want to do is be able to use the value of Constants.Foo to create a value for SomeVar. However this throws an error in Firebug.
So I'm wondering, how do I access the value of Foo inside the Constants namespace; and also, I've been reading a few articles on encapsulation in Javascript (like this one), is it possible to access value of Foo without having to do Constants.Foo? Perhaps some sort of way to preserve a reference to the namespace's this keyword and then I can do something like thisParent.Foo.
Thanks!
EDIT:
This is the error I'm getting by the way:
TypeError: Constants is undefined
That's not possible. An object literal doesn’t have a name until you assign it to something, and by then, it’s too late. Object literals aren't functions, either, so there’s no contextual keyword you could use; this would still be whatever it was in the containing function. Adding the property afterwards is the only way.
var Constants = {
Foo: 'bar',
SomeVar: {}
};
Constants.SomeVar.key = Constants.Foo + 'value';
And no, a namespace is not a thing in JavaScript. That is an object. Nothing more.
The problem is that Constants doesn't exist until the assignment is executed, which is after the object is constructed. You could do something like this (an IIFE):
var Constants = (function() {
self = { Foo : 'bar' };
self.SomeVar = self.Foo + 'value';
return self;
}());

Run code on variable initialization

I just learned this way of initializing a variable in javascript:
var MyGlobalVar = {
Property1 : 1,
Property2 : "Two"
}
Which is pretty neat because is a lot like a static object. But is there any way I can run initialization code inside it, kinda like a static constructor would do?
How about using eval, such as Property1 : eval("[code to run here...]");?
I just don't want to put any initialization or part of my object's definition outside the object itself (outside the brackets).
Edit: My initialization code would actually return a filled up array, so Property2 would be this array. But I don't want to recreate this array every time is needed.
If I understood what you want:
var MyGlobalVar = {
Property1 : 1,
Property2 : (function(){ var arr=["this", "is", "an", "array"];return arr; })()
}
Property2 will be an array...you can have any code after that, and whatever you return will be its value.
EDIT: I changed the above code to return an array.
If you want a constructor, why not use a constructor?
var MyGlobalVar = new function() {
// run whatever code you want in here
// The new object being constructed is referenced via "this"
this.Property1 = 1;
this.Property2 = "Two";
};
This code uses an anonymous function as the constructor. You can run whatever code you want inside. The new object is referenced by this, and it is returned automatically, so MyGlobalVar will look like:
{
Property1: 1,
Property2: "Two"
}
Another approach, and one that is perhaps more common than using an anonymous constructor, is to use a module pattern. It is very similar, but it doesn't use the function as a constructor by calling it with new, and it just returns an object literal.
The above code would be rewritten like this:
var MyGlobalVar = (function() {
// run whatever code you want in here
// The new object is an object literal that we create and return
var _new_obj = {
Property1: 1,
Property2: "Two"
};
return _new_obj;
})();
Just like the first example, it gives you an enclosed environment where you can run whatever code you need in creating the values to be referenced by your object.
The end result is practically identical. The only real difference is that the prototype chain of the object in the first example looks like:
_new_obj ---> {} ---> Object.prototype ---> null
whereas the prototype chain in the new version looks like:
_new_obj ---> Object.prototype ---> null
So there's a slightly shorter chain. It won't make such a difference that you should base any decision on it.
Rather than use eval, you could run an immediate function:
var v = {
prop1: function() {
// whatever
return aResult;
}()
}
Or a previously-defined function:
function foo() {
// whatever
return aResult;
}
var v = {
prop1: foo(),
}
Best to avoid eval if not strictly needed.
If you really want everything inside the object, you can run a function which you also define inside the object literal, which does the following:
runs initialization stuff
returns the value that the property should have
Something like:
var data = {
prop: (function() {
// init
return "foo";
})(),
prop2: (function() {
// init
return "bar";
})()
};

How come JavaScript {this} doesn't reference the parent object?

When making objects I assumed that this would return the object's instance - instead it seems it's something else. Why is this?
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
bar : function() { return Obj.foo; } // Works
}
Update: I must be missing something because in some cases using this inside objects doesn't work. How come this only references the object instance sometimes?
It does. You have a syntax issue.
Use commas to delimit object members
var Obj = {
foo : 'value',
bar : function() { return this.foo; },
}
within member functions this refers to a reference to the object that the function is called on.
jsFiddle Example
Within a JavaScript function this is set depending on how the function was called.
With your example Obj, assuming you correct the syntax error and use commas between properties:
var Obj = {
foo : 'value', // needs comma, not semicolon
bar : function() { return this.foo; }
}
If you use the "dot" syntax on Obj to call the bar function then this will be automatically set to Obj:
Obj.bar(); // this will be Obj
That's an easy way to ensure this ends up set the way you want. But if you call the function in some other way this could be set to something else:
var nonObjBar = Obj.bar; // get a reference to the function
nonObjBar(); // this will (probably) be `window`, but depends if
// in strict mode or inside some other function, etc
var Obj2 = { foo: "other foo" };
Obj.bar.call(Obj2); // the .call() method sets this to Obj2
// so bar will return Obj2's foo, "other foo"
That last example uses the .call() method on Obj.bar to invoke the function in a way that allows you to set this to anything you like (strict versus non-strict mode affects the way this works for some cases).
It might seem strange if you're coming from languages like Java, but JavaScript functions don't belong to any given object. This behaviour is pretty well defined and on purpose.
After fixing the semi-colon after 'value', this seems to work:
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
};
alert(Obj.bar()); // alerts 'value'
See working jsFiddle here: http://jsfiddle.net/jfriend00/UFPFf/.
When you call a method on an object, the javascript engine sets the this pointer to point to the object for the duration of the method call.
You shouldn't have a semicolon after foo.
Actually when you call Obj.bar you will have this referring to Obj.
See http://jsfiddle.net/AAkbR/
var Obj = {
foo : 'value',
bar : function () { return this.foo; }
};
alert(Obj.bar() === Obj.foo);
Alerts true.

Categories