In a JavaScript file I saw:
function Somefunction(){
var that = this;
...
}
What is the purpose of declaring that and assigning this this to it?
I'm going to begin this answer with an illustration:
var colours = ['red', 'green', 'blue'];
document.getElementById('element').addEventListener('click', function() {
// this is a reference to the element clicked on
var that = this;
colours.forEach(function() {
// this is undefined
// that is a reference to the element clicked on
});
});
My answer originally demonstrated this with jQuery, which is only very slightly different:
$('#element').click(function(){
// this is a reference to the element clicked on
var that = this;
$('.elements').each(function(){
// this is a reference to the current element in the loop
// that is still a reference to the element clicked on
});
});
Because this frequently changes when you change the scope by calling a new function, you can't access the original value by using it. Aliasing it to that allows you still to access the original value of this.
Personally, I dislike the use of that as the alias. It is rarely obvious what it is referring to, especially if the functions are longer than a couple of lines. I always use a more descriptive alias. In my examples above, I'd probably use clickedEl.
From Crockford
By convention, we make a private that
variable. This is used to make the
object available to the private
methods. This is a workaround for an
error in the ECMAScript Language
Specification which causes this to be
set incorrectly for inner functions.
JS Fiddle
function usesThis(name) {
this.myName = name;
function returnMe() {
return this; //scope is lost because of the inner function
}
return {
returnMe : returnMe
}
}
function usesThat(name) {
var that = this;
this.myName = name;
function returnMe() {
return that; //scope is baked in with 'that' to the "class"
}
return {
returnMe : returnMe
}
}
var usesthat = new usesThat('Dave');
var usesthis = new usesThis('John');
alert("UsesThat thinks it's called " + usesthat.returnMe().myName + '\r\n' +
"UsesThis thinks it's called " + usesthis.returnMe().myName);
This alerts...
UsesThat thinks it's called Dave
UsesThis thinks it's called undefined
This is a hack to make inner functions (functions defined inside other functions) work more like they should. In javascript when you define one function inside another this automatically gets set to the global scope. This can be confusing because you expect this to have the same value as in the outer function.
var car = {};
car.starter = {};
car.start = function(){
var that = this;
// you can access car.starter inside this method with 'this'
this.starter.active = false;
var activateStarter = function(){
// 'this' now points to the global scope
// 'this.starter' is undefined, so we use 'that' instead.
that.starter.active = true;
// you could also use car.starter, but using 'that' gives
// us more consistency and flexibility
};
activateStarter();
};
This is specifically a problem when you create a function as a method of an object (like car.start in the example) then create a function inside that method (like activateStarter). In the top level method this points to the object it is a method of (in this case, car) but in the inner function this now points to the global scope. This is a pain.
Creating a variable to use by convention in both scopes is a solution for this very general problem with javascript (though it's useful in jquery functions, too). This is why the very general sounding name that is used. It's an easily recognizable convention for overcoming a shortcoming in the language.
Like El Ronnoco hints at Douglas Crockford thinks this is a good idea.
The use of that is not really necessary if you make a workaround with the use of call() or apply():
var car = {};
car.starter = {};
car.start = function(){
this.starter.active = false;
var activateStarter = function(){
// 'this' now points to our main object
this.starter.active = true;
};
activateStarter.apply(this);
};
Sometimes this can refer to another scope and refer to something else, for example suppose you want to call a constructor method inside a DOM event, in this case this will refer to the DOM element not the created object.
HTML
<button id="button">Alert Name</button>
JS
var Person = function(name) {
this.name = name;
var that = this;
this.sayHi = function() {
alert(that.name);
};
};
var ahmad = new Person('Ahmad');
var element = document.getElementById('button');
element.addEventListener('click', ahmad.sayHi); // => Ahmad
Demo
The solution above will assing this to that then we can and access the name property inside the sayHi method from that, so this can be called without issues inside the DOM call.
Another solution is to assign an empty that object and add properties and methods to it and then return it. But with this solution you lost the prototype of the constructor.
var Person = function(name) {
var that = {};
that.name = name;
that.sayHi = function() {
alert(that.name);
};
return that;
};
Here is an example
`
$(document).ready(function() {
var lastItem = null;
$(".our-work-group > p > a").click(function(e) {
e.preventDefault();
var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
if (item == lastItem) {
lastItem = null;
$('.our-work-single-page').show();
} else {
lastItem = item;
$('.our-work-single-page').each(function() {
var imgAlt = $(this).find('img').attr('alt'); //Here value of "this" is '.our-work-single-page'.
if (imgAlt != item) {
$(this).hide();
} else {
$(this).show();
}
});
}
});
});`
So you can see that value of this is two different values depending on the DOM element you target but when you add "that" to the code above you change the value of "this" you are targeting.
`$(document).ready(function() {
var lastItem = null;
$(".our-work-group > p > a").click(function(e) {
e.preventDefault();
var item = $(this).html(); //Here value of "this" is ".our-work-group > p > a"
if (item == lastItem) {
lastItem = null;
var that = this;
$('.our-work-single-page').show();
} else {
lastItem = item;
$('.our-work-single-page').each(function() {
***$(that).css("background-color", "#ffe700");*** //Here value of "that" is ".our-work-group > p > a"....
var imgAlt = $(this).find('img').attr('alt');
if (imgAlt != item) {
$(this).hide();
} else {
$(this).show();
}
});
}
});
});`
.....$(that).css("background-color", "#ffe700"); //Here value of "that" is ".our-work-group > p > a" because the value of var that = this; so even though we are at "this"= '.our-work-single-page', still we can use "that" to manipulate previous DOM element.
Related
I created a class in JavaScript as follows:
class TreeMatching
{
constructor()
{
this.thresholdPoints=0;
this.neighborWeight = 0.4;
this.totalFrequency = 0.0;
this.listSeq = [];
this.listFreq = [];
this.mapScore = new Object();
this.tree = new Trie();
}
createTree()
{
var list_Dictionary;
var loadWordList = $.get("../wordFrequencyTop5000.txt", function(data)
{
list_Dictionary = data.split("\n");
});
loadWordList.done(function()
{
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
this.tree.insert(string); //<-- Cannot read property 'insert' of undefined
}
});
}
}
which is supposed to call the insert method in class Trie as follows:
class Trie
{
constructor()
{
this.count=1;
this.root = new TrieNode();
}
insert(word)
{
var children = new Object();
for(var i=0; i<word.length(); i++){
var c = word.charAt(i);
var t;
if(children[c]){
t = children[c];
}else{
t = new TrieNode(c);
children.put(c, t);
}
children = t.children;
//set leaf node
if(i==word.length()-1)
t.isLeaf = true;
}
}
}
However, the line of code where the error is marked, the outer function's this value, is not having properties tree, mapScore, etc.
Is there a way that I can access those values from the inner callback function?
Thanks
look at 'this' - you will have to define local variable to maintain reference to "this" inside the call, as described in the link.
createTree()
{
var self = this;
var list_Dictionary;
var loadWordList = $.get("../wordFrequencyTop5000.txt", function(data)
{
list_Dictionary = data.split("\n");
});
loadWordList.done(function()
{
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
self.tree.insert(string); //<-- Now you should be able to do it
}
});
}
'this' in the inner anonymous has different scope. Try to use the advantage of closer in JS which will get access to the function caller scope.
var that = this;
loadWordList.done(function() {
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
that.tree.insert(string); // 'that' will hold 'this' in the right scope
}
});
The anonymous function inside loadWordlist.done creates a new scope with an new context.
if you want to keep the old context you can use the ES2015 arrow function:
loadWordList.done(() => {
//code here
);
or make a var inside createTree() like this:
var that = this;
and then inside the loadWordList callback you can refer to the right context using:
that.tree.insert(string);
I personally prefer the arrow function because 'that' is a lousy choice for a var name. And since your using the ES2015 classes browser support must not be an issue.
I'm making my own jQuery-like library for a very specific task. (dont want to use jquery, to be closer to native js).
I need a solution to call class with and without agruments, in my case - certain selector.
What I've ment - class has to work in both ways:
myclass.function();
and:
myclass(selector).function();
Initialization of class looks like this
(function(window){
var myclass = function( selector ){
return new myclass.model.build( selector );
};
myclass.model = myclass.prototype = {
constructor: myclass,
myfunction: function(){
alert("some function's alert");
}
};
myclass.model.build = function( selector ){
if( !selector )
return this;
/* Picking objects by selector */
};
window.myclass = myclass;
return myclass;
})(window);
jsFiddle Demo
There are a few ways to accomplish this. Here is a common way, which is to setup an instance of a local function ("class") that has its prototype configured as an object for extensibility and has properties attached to it as an object as well.
//use window and document shortcut
(function(win,doc){
//setup local function in order to extend
var jakeWeary = function(selector){
//force an instance to be returned when referenced
return new jakeWeary.fun.start(selector);
};
//allow prototype to be extended by referencing fun
jakeWeary.fun = jakeWeary.prototype = {
constructor : jakeWeary,
//use passed values to query
start : function(selector){
//query against present document
var nodes = doc.querySelectorAll(selector);
//mimic an array of matched elements
for(var i = 0; i < nodes.length;i++){
this[i] = nodes[i];
}
//return self for chaining
return this;
}
};
//extend function object in order to be used without calling instance
jakeWeary.div = function(content){
var div = document.createElement("div");
div.innerHTML = content;
return div;
};
//expose
win.jakeWeary = win.jk = jakeWeary;
})(window,document)
//call instance to match the selector `.d`
var $ = jakeWeary('.d');//lets use a fun variable like $ for this
//reference the third matched element, and then append a div created from a function on the
//jakeWeary function object
$[2].appendChild(jk.div("<p>Reinventing the wheel</p>"));
<div id="i">i</div>
<div class="d">d</div>
<div class="d">d</div>
<div class="d">d</div>
You mean you want the non-instance call to generate a default instance and then be applied to it?
var Foo = (function () {
// set up constructor
var Foo = function () {
this.fizz = 'buzz';
};
Foo.prototype = Object.create(null);
// instance version
Foo.prototype.bar = function () {return 'this.fizz is ' + this.fizz;};
// non-instance version
Foo.bar = function () {return Foo.prototype.bar.apply(new Foo(), arguments);};
return Foo;
}());
Foo.bar(); // "this.fizz is buzz"
I am trying to make a parent data access layer class that is inherited by multiple classes.
parent class:
var DataAccess = function() {
this.Save = function(){
alert(this.ListName); //works
SaveLogic(this.Id); //doesnt work
}
}
Child Class:
var Job = function(){
Job.prototype.ListName = 'MyList'; //works
this.Save = function(){
Job.prototype.Save().call(this);
//specific Job Save logic
}
}
Job.prototype = new DataAccess();
Now in my main class:
var aJob = new Job();
aJob.Id = 1;
aJob.Save(); //Does not work. Prototype can not see aJob.Id..
As you can see, I need to create a parent function with shared variables such as ID, so when I inherit the parent class, I can assign values to these variables so the shared logic of hte parents class work, then my extended class's can have specific logic
You can start with construction like this:
var DataAccess = function() {
this.Save = function(){
console.log('DataAccess Save call', this.ListName, this.Id);
}
}
var Job = function(){
this.ListName = 'MyList';
}
Job.prototype = new DataAccess();
/**
* Delete me to use parent's Save method.
*/
Job.prototype.Save = function(){
console.log('Job Save call', this.ListName, this.Id);
}
var aJob = new Job();
aJob.Id = 1;
aJob.Save();
#stivlo described how it works in his answer here: https://stackoverflow.com/a/4778408/1127848
The problem I had was I wanted to reuse the same code. I think I have worked it out this way, im still not 100% its the right way to go with prototype programming :
function DataAccess() {
//setup common variables
}
DataAccess._Save_(listname, id){
commonSaveLogic(id);
doStuff(listname);
}
function Job() {
this.ListName = 'Jobs';
DataAccess.call(this); //call DataAccess Constructor
}
Job.prototype = DataAccess;
Job.prototype.constructor = Job;
Job.ProtoType.Save = function(){
this._Save_(this.ListName, this.Id);
}
function AotherList() {
this.ListName = 'AnotherList';
DataAccess.call(this);
}
//same as above. Job and Another list both inherit off DataAccess.
Dont use .prototype inside the constructor. We define .prototype for sharing same copy to all objects.
You are missing here many things. I'm explaining one by one:
First : SaveLogic(this.Id); //doesnt work
Because You don't use this with the function so it's a global function not a constructor function. And you don't have defined it any where so there will be an error like function SaveLogic not defined
To prevent this error, define the function somewhere.
Second : You have passed this.Id as a parameter. Id using the line aJob.Id = 1; will not be accessible within the SaveLogic(this.Id); because Id is a property of aJob not of ajob.prototype. this.ListName will be available here because it's a property of prototype.
So it you want to get Id inside SaveLogic() function, define it as prototype property.
Third : when this line aJob.Save(); will be invoke it will call
this.Save = function(){
Job.prototype.Save().call(this);
//specific Job Save logic
}
Job.prototype.Save() will search for a function named as Save(). Which is not defined in Job's prototype so function not defined error will occur.
Fourth : call() can not be called anyhow excepts either DataAccess.call() or Job.call();
call() is just like the constructor call excepts it's first parameter get assigned to the constructor's this object.
Here i have improved your code. Just copy and paste it in your editor and see what is going here.
Try this :
function SaveLogic(Id)
{
alert(Id);
}
var DataAccess = function() {
this.Save = function(){
alert(this.ListName); //works
SaveLogic(this.Id);
return this; //doesnt work
}
this.call = function() {
alert('call is called here');
}
}
var Job = function(){
Job.prototype.ListName = 'MyList'; //works
this.Save = function(){
//console.log(Job.prototype.Save());
Job.prototype.Save().call(this);
//specific Job Save logic
}
}
Job.prototype = new DataAccess();
var aJob = new Job();
Job.prototype.Id = 1;
aJob.Save(); //Does not work. Prototype can not see aJob.Id..
I have a question about Javascript objects. How can I access the properties of the parent class?
function randomObj() // for example button obj
{
this.text = "this is obj";
}
function parentClass()
{
this.name = "parent";
this.subObj;
}
parentClass.prototype.generate = function()
{
this.subObj = new randomObj();
this.subObj.changeParentClassName = function() // button obj wants to change name
{
this.name = "not parent";
}
}
var sampleObj = new parentClass();
sampleObj.generate();
sampleObj.subObj.changeParentClassName (); // does not works
It seems 'this' in 'changeParentClassName' is subObj, how can I access parentclass.name?
JavaScript's this will be the object to the left of the . when calling the function. In this case, it is the subObj not the parentObj so you are setting name on subObj. You have 2 options, you can put this in a different variable inside generate so it doesn't get replaced by JavaScript's this logic. Something like:
var parentObj = this;
this.subObj.changeParentClassName = function() // button obj wants to change name
{
parentObj.name = "not parent";
};
Or you can use the bind() to create a new function that will have this bound to a known object (in this case your parent object) Something like:
this.subObj.changeParentClassName = (function() // button obj wants to change name
{
this.name = "not parent";
}).bind(this); // bind the 'this' inside the changeParentClassName to the 'this' inside generate
Check out Function bind() for more info on bind and interactive examples.
Note, if you are targeting a recent version of Javascript (ECMAScript 6 or later), you can use a => function which does not change the value of this compared to the declaring scope. So you could use:
this.subObj.changeParentClassName = () => // button obj wants to change name
{
this.name = "not parent";
};
I am using easeljs for a HTML5 game.
I am calling onClick from within a method in a class, but the event object is overwriting my 'this' object so I can no longer access the other class methods or vars. For example I have something like (obviously this is not the actual code, just a quick illustration):
function Book(){
this.text = "this is the text";
this.makeText = function(){
//Define some shapes
var character = new Container();
character.addChild(some shapes);
character.onClick = this.detectClick;
}
this.detectClick = function(){
alert(this.text);
}
}
So, if I run this, I would get an undefined alert because in my detectClick method, this is now my event object.
So how do I call the original text from within this method?
Many Thanks
You need to us closure to pass the object reference
var self = this;
character.onClick = function(){ self.detectClick() };
Or use a simple proxy method.
function proxy(method, scope) {
return function() {
return method.apply(scope, params);
}
}
character.onclick = proxy(detectClick, this);
Ok that you really need to do is
function Book(){
this.text = "this is the text";
this.makeText = function(){
//Define some shapes
var character = new Container();
character.addChild(some shapes);
character.onClick = this.detectClick.bind(this);
}
this.detectClick = function(){
alert(this.text);
}
}
scope of 'this' is the problem in your code. Change your code like the below code
function Book(){
this.text = "this is the text";
var that=this;
this.makeText = function(){
//Define some shapes
var character = new Container();
character.addChild(some shapes);
character.onClick = that.detectClick;
}
this.detectClick = function(){
alert(this.text);
}
}