I am trying to understand better the use of that and this in JavaScript. I am following Douglas Crockford's tutorial here: http://javascript.crockford.com/private.html
but I am confused regarding a couple of things. I have given an example below, and I would like to know if I am making a correct use of them:
function ObjectC()
{
//...
}
function ObjectA(givenB)
{
ObjectC.call(this); //is the use of this correct here or do we need that?
var aa = givenB;
var that = this;
function myA ()
{
that.getA(); //is the use of that correct or do we need this?
}
this.getA = function() //is the use of this correct?
{
console.log("ObjectA");
};
}
function ObjectB()
{
var that = this;
var bb = new ObjectA(that); //is the use of that correct or do we need this?
this.getB = function()
{
return bb;
};
that.getB(); //is the use of that correct or do we need this?
}
Note this is just an example.
this in JavaScript always refers to current object, method of which was called. But sometimes you need to access this of your object in deeper. For example, in callbacks. Like so:
function MyClass() {
this.a = 10;
this.do = function() {
http.get('blablabla', function(data) {
this.a = data.new_a;
});
};
}
It will not work, because this in callback may refer to http, to some dom element or just window(which is really common). So, it is common solution to define self or that, an alias for this or your object, so you can refer it anywhere inside.
function MyClass() {
var self = this;
this.a = 10;
this.do = function() {
http.get('blablabla', function(data) {
self.a = data.new_a;
});
};
}
This should give you vision why it is used and how it should be used.
There is no other reasons(currect me if I'm wrong) to create special variable, you can use this to send your object to other objects and do things, many assignments, such logic, wow...
ObjectC.call(this); //is the use of this correct here or do we need that?
The first thing you need to understand is how the this keyword works. It's value depends on how the function/method/constructor is called.
In this case, function ObjectA is a constructor, so you can just use this inside the code of it. In fact, with var that = this; you declare them to be absolutely identical (unless you use that before assigning to it).
function myA() {
that.getA(); //is the use of that correct or do we need this?
}
Again, it depends on how the function is called - which you unfortunately have not show us. If if was a method of the instance, this would have been fine; but but it seems you will need to use that.
this.getA = function() //is the use of this correct?
As stated above, using that would not make any difference.
var bb = new ObjectA(that) //is the use of that correct or do we need this?
var that = this;
that is undefined when it is used here. And it would be supposed to have the same value as this anyway. Better use this.
that.getB(); //is the use of that correct or do we need this?
Again, both have the same effect. But since you don't need that, you should just use this.
Everything is correct except for :
function ObjectB()
{
var bb = new ObjectA(that) //this is wrong
var that = this;
this.getB = function()
{
return bb;
};
that.getB();
}
You are missing ; and that isn't declare.
You need that (in your case, this is the variable name you use) when you want to use this in another scope :
function ObjectB()
{
var that = this;
// here 'this' is good
function()
{
// Here 'this' doesn't refer to the 'this' you use in function ObjectB()
// It's not the same scope
// You'll need to use 'that' (any variable from the ObjectB function that refers to 'this')
};
// Here 'that' = 'this', so there is no difference in using one or another
}
What "that" is in this context is simply a variable that is equal to "this". That means saying "that" is exactly the same as saying "this", which makes in unnecessarily complicating.
This code:
var that=this;
that.getA();
Will yield the same result as this code:
this.getA();
Having a variable to represent "this" just complicates things when you can just say "this".
Related
In the below code, which one is the right and how these two are different
Using call method
var obj = {
num: 10
};
var add = function(a) {
return this.num + a
}
console.log(add.call(obj,4))
Passing object in parameter
var obj = {
num: 10
};
var add = function(obj,a) {
return obj.num + a
}
console.log(add(obj,4))
Your second code block is just a regular function. The first one however is a bit more tricky. So the question is basically:
When to work with context in javascript?
In javascript, the term context basically means this. It is usually used when you call a method of an object, so that you can refer to the object. That's one of the core concepts of OOP, were we only define a function once inside the prototype, and every object of that class which inherits from it exposes this method, it won't work without context. So that's what this was invented for. However there are some cases, were context is useful without inheritance. E.g. Eventhandlers are usually contextless, as they are not part of any object:
window.addEventListener("load", function(evt){
const el = evt.target;
};
However as it is an Eventhandler of window, wouldn't it make sense that it is executed in the context of window? If you now say "YES", then you (will) probably love JS:
window.addEventListener("load", function(){
this.document.body.innerHTML = "Dynamic context can be cool!";
});
So in JS this is the way of refering to the object, the function refers to. Through Function.prototype.call we can make use of this everywhere. However that does not mean that we should use it everywhere. this should stay in the sense of context, as using it somewhere else will create confusion / uglify your code / make your code buggy.
var add = function(a) {
return this.num + a;
}
In your codesnippet i think its unclear what thisrefers to. So its rather a misuse of this. However it could get a meaning if you make it a method of obj, so its context becomes clear from the code:
const num = {
value:10,
add(a){ return this.value + a }
};
It gets even more beautiful if you use inheritance to make it reusable:
class CustomNumber {
constructor(n = 0){
this.value = n;
}
add(a){ return this.value + a; }
}
const num = new CustomNumber(10);
I have a library which has defined a class as this. I would like to call getMe() function outside class A with my own me.
var me;
var A = function(_me){
me = _me;
}
A.prototype.getMe = function(){
me();
}
I have class B where I call it like
A.prototype.getMe.call(this) but this would throw an error as me is not defined. How can I pass me into this ? I would like to make minimum changes to getMe function.
The only way to do this is, I guess to use an if .
A.prototype.getMe = function(){
if(!me){
me = this.myMe
}
me();
}
So I did A.prototype.getMe.call({myMe: myMe}, args) but would it define the variable in global space ?
You can't reasonably do this, not least because A is fundamentally broken (see below).
Whether you can do it at all depends entirely on where the A code is: If it's at global scope, you can do what you want but shouldn't. If it isn't at global scope, you can only do it from code within the scope where me is declared (or a scope within it).
If it's at global scope, you'd do it like this (but don't :-) ):
// The "A" Code
var me;
var A = function(_me){
me = _me;
}
A.prototype.getMe = function(){
me();
}
// Your code using it
new A(); // Create `A.prototype.getMe`, because unless
// `A` is called at least once, it doesn't exist
var myMe = function() {
console.log("myMe called");
};
me = myMe; // Set the global `me`
A.prototype.getMe(); "myMe called"
The only way to do this is, I guess to use an if .
if(!me){
me = this.myMe
}
So I did A.prototype.getMe.call({myMe: myMe}, args) but would it define the variable in global space ?
Yes, it would. That's what me = this.myMe does.
From that observation, it sounds like you can modify A's code. If so, fix it so that it doesn't define a prototype function in terms of a global variable. Perhaps isolate the code from the function into a function that doesn't expect to be called on an instance and pass in me as a parameter.
I am working on an existing library to make a small change. So I can only change inside getMe function definition. And I cant change var me definition.
In that case, you can add a new optional parameter at the end, and use that if provided and me if not provide:
A.prototype.getMe = function(myMe){
var meToCall = myMe || me;
meToCall();
};
Live Example:
// The "A" Code
var me;
var A = function(_me){
me = _me;
}
A.prototype.getMe = function(myMe){
var meToCall = myMe || me;
meToCall();
}
// Your code using it
A.prototype.getMe(function() {
console.log("My me!");
});
And, separately, A is fundamentally broken if it's really as shown (barring an extremely specific use-case). Having constructor code set a global (global at least to A's code) that's then used by a prototype function is a huge design and maintenance red flag.
Consider the cross-talk horror:
// The "A" Code
var me;
var A = function(_me){
me = _me;
}
A.prototype.getMe = function(){
me();
}
// Using it
var a1 = new A(function() {
console.log("me1");
});
a1.getMe(); // "me1" -- so far so good
var a2 = new A(function() {
console.log("me2");
});
a2.getMe(); // "me2" -- yup, still fine
a1.getMe(); // "me2" -- what the...?!?!?!!
I wanted to rearrange my Code to OOP, but I am not able to figure out my errors here, especially since they appear correct according to different Tutorials and examples.
I suppose I misunderstand something of JS´s, object-instantiation and Call stack.
I will provide some examples, which I don´t understand.
What I want here is to do some operations on an Array and then get it to an other class.
https://jsfiddle.net/8g22nj8y/1/
<script>
$(document).ready(function() {
var p = new Parser();
p.init();
p.getArray();
p.getArray2();
p.get3();
}</script>
function Parser() {
var myArray = [];
this.myArray2 = [];
thisReference = this;
this.myArray3=[];
return {
init: function () {
alert("huhu");
this.parse2();
parse();
},
getArray: function () {
alert(thisReference.myArray2.length);
},
getArray2: function () {
alert(myArray);
}
}
function parse() {
var arr = [1, 2, 3];
myArray.push(arr);
myArray2.push(arr);
for(var i =0;i<10;i++){
a=[];
a.push(i);
thisReference.myArray3.push(a);
}
}}Parser.prototype.parse2 = function () {
var arr = [1, 2, 3];
myArray.push(arr);
this.myArray2.push(arr);};
Independent how I run it, it always says that this.parse2() is not a function.
When I am only using parse(), it says that myArray2 is undefined, althought it´s clearly there - just as "class variable". If I change myArray2 in parse() to thisReference.myArray2 it´s working.
Why? I thought an inner Function - which parse() clearly is, is able to grab all the variable in the outer function - in this case Parser(). When I am now using myArray3 either if it´s used with thisReference or with this. "it is not defined".
If I call parse2 with thisReference it´s working, but then "myArray is not defined", yes it´s an local variable but it´s in the same class and if I call parse() I am able to use it without problems.
Furthermore:
simplified: https://jsfiddle.net/Lzaudhxw/1/
function Syntax(){
var mine = new Lex();
myRef=this;
}
Class1.prototype.foo=function(){
myRef.mine.setFunc(5);
myRef.mine.publicFunc();}
function Lex(){
this.x, this.h=1;
return{
publicFunc: function(param){
this.h;
this.x;
},
setFunc: function(x){
this.x=x;
}
}
Initially I set h to be 1. If I now instantiiate Syntax and call from that the publicFunc from Lex both are undefined. But if I run foo() from Syntax and call the publicFunc again, x is set to the value and h is undefined. Why is it not possible to predefine an varriable (in this case h) like that and then use it?
EDIT to Jan´s Answer:
I read in many Tutorials and some production code that you should store "this" into an variable. Why should myRef point to anything else than the Syntax Object? :O
Why is myRef not an variable of Syntax? Does it have to be this.myRef?
Ahh right, var means local, so mine is only accessiable in the constructor?!
I didn´t wanted to init "x", only define it.
Ahh with return{} I am creating a new class/object, then it´s clear that this. does not point to the above vars. But to introduce an myRef=this should do the job, right?
...So, it´s wiser to use prototype to add functions instead of an inner function?
Yeah you've got a bunchload of JS concepts wrong. I suggest you read the documentation. Tried adding a few explanations.
function Syntax(){
// Since you're returning a new object from "Lex" straight away, there's
// little point of using "new" here
var mine = new Lex();
// Why store "this" here? "this" will be accessible from your prototype
// methods pointing to your object instance... Provided you use "new Syntax()",
// Otherwise "myRef" will (probably) point to the global object.
myRef=this;
}
// Where's "Class1"? You don't have a Class1 function anywhere. You probably mean "Syntax"
Class1.prototype.foo=function() {
// "myRef" is not a property of "Syntax", so it wouldn't be accessible here.
// Furthermore, "mine" is declared as a variable above, so it wouldn't be
// accessible in this manner even if "myRef" pointed to "this" (which it doesn't).
myRef.mine.setFunc(5);
myRef.mine.publicFunc();
}
function Lex(){
// This is a correct property declaration of h. You're not setting the
// value of x here though, just calling it. Javascript allows "trying"
// to call ANY property of ANY object without giving neither a compilation
// nor runtime error, so calling the undefined "this.x" here is valid.
// It just won't do anything.
this.x, this.h=1;
// Here you return a new object straight off, so the below "this" will point
// to the object below, not the "Lex" object defined above. So your above
// defined "this.h" will not be used, it's a property of a different object.
return {
publicFunc: function(param){
this.h;
this.x;
},
setFunc: function(x){
this.x=x;
}
}
// You're missing a closing bracket here.
What you're probably trying to do would look something like this with correct Javascript syntax
function Syntax(){
this.mine = Lex();
}
Syntax.prototype.foo=function() {
this.mine.setFunc(5);
this.mine.publicFunc();
}
function Lex() {
return {
h:1,
publicFunc: function(){
console.log(this.h);
console.log(this.x);
},
setFunc: function(x){
this.x=x;
}
}
}
var s = new Syntax();
s.foo();
But returning an object from Lex would be pretty impractical in most cases. So what you really REALLY want to do is probably
function Syntax(){
this.mine = new Lex();
}
Syntax.prototype.foo = function() {
this.mine.setFunc(5);
this.mine.publicFunc();
}
function Lex() {
this.h = 1;
}
Lex.prototype = {
publicFunc: function(){
console.log(this.h);
console.log(this.x);
},
setFunc: function(x){
this.x=x;
}
};
var s = new Syntax();
s.foo();
var steve = function() {
var bob = {};
bob.WayCoolTest = function () {console.log('done deal');};
return bob;
}
window["steve"]["WayCoolTest"]
running this in chrome console, my test app, anywhere results with undefined. I do not understand why, can someone explain why this does not work and help me fix it. Thank you very much!!
Using window is usually redundant - let me demonstrate:
var foo = '123';
alert(foo); //123
alert(window.foo) //123
alert(window['foo']) //123
It is evident which is more convenient. There is a circumstance in which the use of window makes sense, but that circumstance is almost always because of poor architecture. Using window allows us to access a variable global variable - funny wording :) This should clear it up:
var foo = '123';
var bar = 'abc';
var prop;
if (blah) { //some condition here
prop = 'foo';
}
else {
prop = 'bar';
}
Now...how can we use prop to get the value of a corresponding variable?
console.log(window[prop]); //123 or abc - bracket notation lets us use variable property names
This sort of thing is very common within objects, but not with window. The reason is that we should be avoiding global variables (properties of window) as much as possible. Therefore, any logic that needs a variable property name should be inside of an object, dealing with objects - NOT window. Now window can be out of the picture.
It is usually bad practice to create functions inside of other functions. That would mean that each time you call function A, you recreate function B. Most of the time, people do that because they don't know better, not because they need to.
It appears to me that you intended to give steve a property called WayCoolTest, both being functions. That can be done.
var steve = function() {
console.log("I'm Steve!");
}
steve.WayCoolTest = function() {
console.log("I'm a Way Cool Test!");
}
steve(); //I'm Steve!
steve.WayCoolTest(); //I'm a Way Cool Test!
This works because functions are objects in javascript. Therefore, you can add properties to them.
Let's step it up!
To emphasize good practices, I'm going to wrap this example in an object testApp (it acts like a namespace..we're using that instead of window).
I will create a property of testApp called steve, which will be a function.
Rather than creating steve directly as a function, I will use an IIFE (immediately invoked function expression), which is a function that will return something to be set as steve.
Inside the IIFE, I will create the function for steve and also attach WayCoolTest to it, as demonstrated in the previous example, then that function is returned and assigned to steve.
var testApp = {
steve : (function() {
var steve = function() { //the name here doesn't matter, just being consistent, since this will be the value of the property `steve`.
console.log("I'm Steve!");
}
steve.WayCoolTest = function() {
console.log("I'm a Way Cool Test!");
}
return steve;
}());
};
testApp.steve(); //I'm Steve;
testApp.steve.WayCoolTest(); //I'm a Way Cool Test!
Now, let's consider another variation.
var testApp = {
steve : (function() {
var steve = function() { //the name here doesn't matter, just being consistent, since this will be the value of the property `steve`.
console.log("I'm Steve!");
WayCoolTest(); //Steve can use this, but nothing else can! Encapsulation.
}
var WayCoolTest = function() { //THIS PART!! No longer a property of "steve"
console.log("I'm a Way Cool Test!");
}
return steve;
}());
};
testApp.steve(); //I'm Steve! I'm a Way Cool Test
testApp.steve.WayCoolTest(); //undefined, of course
That is very useful if for example steve is a complicated function and you want to break it up into some other small functions that only steve will know about.
To complement the other answers, your code would work this way:
var steve = function() {
var bob = {};
bob.WayCoolTest = function () {console.log('done deal');};
return bob;
}
window["steve"]()["WayCoolTest"]();
or
var steve = (function() {
var bob = {};
bob.WayCoolTest = function () {console.log('done deal');};
return bob;
})();
window["steve"]["WayCoolTest"]();
or, the dirtiest way...
steve=(function() {
var bob = {};
bob.__defineGetter__("WayCoolTest", function () {console.log('done deal');});
return bob;
})();
window["steve"]["WayCoolTest"];
Assuming steve is in the global scope and you dont seem to use steve as a constructor you can use immediate function and return the new object that you have created inside of it.
var steve = (function () {
var bob = {};
bob.WayCoolTest = function () {
console.log('done deal');
};
return bob;
})();
window["steve"]["WayCoolTest"]();
If it is in a closure then you would have to either remove var for hoisting to happen so it becomes a part of window or set it to the window object as a property.
window.steve = (function () {
var bob = {};
bob.WayCoolTest = function () {
console.log('done deal');
};
return bob;
})();
If you declare using var it doesn't get associated with the global window scope. It's instead a local variable. Also bob is not a property on the steve object they way you have it set up
I need to edit the function which locates inside of the constructor.
Example:
some.thing = function() {
this.somefn = function() { // this is the function that I need to fix
...
}
}
But function should be edited not just only for a single object (new obj = some.thing();) but also for any created objects by this constructor.
So is there any way to edit such inner-functions?
Here is a solution based on prototype:
var Something = function () {
this.f = function () {
console.log("Something");
};
};
var Old = Something;
var Something = function () {
Old.apply(this);
this.f = function () {
console.log("New");
};
};
Something.prototype = new Old();
var s = new Something();
s.f(); // prints "New"
The solutions seem just a little too obvious, so I'm wondering if the trouble is that you don't have access to the original code, and you need a more dynamic solution.
If so, one option may be to override the constructor with your own constructor, and have it call the original, and then update the object.
Original code:
some.thing = function() {
this.somefn = function() { // this is the function that I need to fix
...
}
}
Your code:
// cache a reference to the original constructor
var _thing = some.thing;
// your constructor
some.thing = function() {
// invoke the original constructor on the new object.
_thing.apply(this, arguments);
this.somefn = function() { /*your updated function*/ };
};
// maintain inheritance
some.thing.prototype = Object.create(some.thing.prototype);
// make an instance
var theThing = new some.thing();
Now you're getting the benefit of the original constructor and prototype chain, but you're injecting your own function on to the objects being created.
Only trouble may be that the original function you replaced could make special use of the original constructor's variable scope. If that's the case, there would be an issue to resolve.
It would be possible to retain and invoke the original method that you overwrote before invoking yours. Not sure if this situation calls for that or not.
I exactly know your need cause last week I passed through it. I just implemented a complete inheritance model in javascript and as far as I remember, I had a problem with overriding constructors and calling the parent class's ctor when child class is initializing.
So I just solved the problem with modifing some points in my design and it's now working like a charm! (something like C# but in Javascript)
By the way, I don't suggest you to change a method contents this way, but here is a way to do that (I myself did not do that this way and AGIAIN I DO NOT RECOMMEND IT. THERE ARE MANY OTHER WAYS, BUT THIS IS THE EASIEST):
var test = function() { /*InjectionPlace*/ };
eval("var newTest = " + test.toString().replace(
"/*InjectionPlace*/",
"var i = 10; alert(i);"
));
test();
newTest();
Cheers