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.
Related
I've been learning basic Javascript concept with class.
I'm trying it with ES5 syntax.
By the way, I'm stuck with this problem.
I defined a class with function declaration. And Defined a method in that class.
const Vehicle = function() {
this.passengers = [];
console.log('Vehicle created');
const addPassenger = function(p) {
this.passengers.push(p);
}
}
const v = new Vehicle();
v.addPassenger("Frank");
v.addPassenger("Zim");
console.log(v.passengers);
But when I call it with its instance, I got the error. I think the problem is bind...But I have no idea where and which one should I bind.
Thank you!
You are currently only declaring the addPassenger function as a constant inside the function.
To "make the function part of class", use this
this.addPassenger = function(p) {
////
}
When you create a function inside the vehicle function using const or function syntax, it doesn't belong to the instance variable and is private to the constructor function. In order to use it from the instace object you need to define it for the instance like
const Vehicle = function() {
this.passengers = [];
console.log('Vehicle created');
this.addPassenger = function(p) {
this.passengers.push(p);
}
}
const v = new Vehicle();
v.addPassenger("Frank");
v.addPassenger("Zim");
console.log(v.passengers);
You need to replace const with this
const Vehicle = function() {
this.passengers = [];
console.log('Vehicle created');
this.addPassenger = function(p) {
this.passengers.push(p);
}
}
const v = new Vehicle();
v.addPassenger("Frank");
v.addPassenger("Zim");
console.log(v.passengers);
What I would like is to use _h like $ jQuery. Every time I use _h i want to have new instance of Helper class, which would hold selected element, do stuff with them and so on.
The problem is that _h does not creates new instance of class, but rather use already created one.
I assumed if I use it like that:
var obj1 = _h('p');
var obj2 = _h('#testDiv');
I would have two different instances of class. However both instances store the same elements and seems to point to the same instance.
var _h = (function(){
var Helper = function(query){
if(window === this)
return new Helper(query);
this.Get.call(this, query);
this._allCurrentElements = [];
}
Helper.prototype.Get = function(query){
var els = document.querySelectorAll(query);
for (var i = 0; i < els.length; i++) {
_allCurrentElements.push(els[i])
};
return this;
}
Helper.prototype.AddClass = function(cl) {
// do stuff
return this;
}
Helper.prototype.RemoveClass = function(cl) {
// do stuff
return this;
}
// more methods
//return Helper;
//return Helper();
return new Helper();
})();
So if someone could point me that I am doing wrong, I would really appreciate that.
Edit: If I don't wrap it in IFFE, don't assign to _h var and call var t = Helper('p'), then it behaves as expected
(function(){})() is called an IIFE (Immediately Invoked Function Expression). Which means, you are declaring a anonymous function and invoking it in a single step. The return value of the IIFE is stored in _h.
When you return new Helper();, _h contains an instance of Helper.
When you return Helper, you are returning the constructor of the Helper class. You will have to use new _h() create new Helper object.
What you need is a wrapper function that will take the arguments, create a new Helper object and return it.
Try
var _h = (function(){
var Helper = function(query){
if(window === this)
return new Helper(query);
this.Get.call(this, query);
this._allCurrentElements = [];
}
Helper.prototype.Get = function(query){
var els = document.querySelectorAll(query);
for (var i = 0; i < els.length; i++) {
_allCurrentElements.push(els[i])
};
return this;
}
Helper.prototype.AddClass = function(cl) {
// do stuff
return this;
}
Helper.prototype.RemoveClass = function(cl) {
// do stuff
return this;
}
// more methods
return function (query) {
return new Helper(query);
};
})();
Update 1:
In Helper.prototype.Get, _allCurrentElements is a global variable. You need to use this._allCurrentElements.
Also, you need to initialize this._allCurrentElements = []; before calling this.Get
Working Example - https://jsfiddle.net/ucqgnbne/
Update 2
As #Bergi commented, returning Helper will work since you are using if(window === this) return new Helper(query);.
I have a function that looks like this:
var tempFun = function() {
return 'something';
}
tempFun.priority = 100;
Now I'm pushing it to an array and binding another object to it in the process like this:
var funArray = [];
var newObj = {};
funArray.push( tempFun.bind(newObj) );
and after this, I would like to acces the function's property like this:
funArray[0].priority
but it returns undefined. Is there some way to preserve the property on the function while binding a new object to it?
No, but you could write a function to do this yourself;
Function.prototype.bindAndCopy = function () {
var ret = this.bind.apply(this, arguments);
for (var x in this) {
if (this.hasOwnProperty(x)) {
ret[x] = this[x];
}
}
return ret;
};
... which you could then use via;
var funArray = [];
var newObj = {};
funArray.push( tempFun.bindAndCopy(newObj) );
No. Bind returns a new function, which "wraps" around the original one. All you can do is copy the properties on this new function:
var boundFun = tempFun.bind(newObj)
boundFun.priority = tempFun.priority;
funArray.push( boundFun );
If you want the properties to be in sync (changes in one visible on the other) you can do:
Object.defineProperty(boundFun, 'priority', {
get : function () { return tempFun.priority; },
set : function (val) { tempFun.priority = val; }
});
From MDN:
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
Hence, .bind() won't be useful for what you're trying to achieve. Besides using jQuery mappers or rewriting your code to use .prototype, a solution that I can think of is:
var obj = {};
for (var i in tempFun) {
if (tempFun.hasOwnProperty(i)) obj[i] = tempFun[i];
}
In Google Apps JS. I would like to implement an array of objects, each with properties and methods. One of the properties needs to be an array of objects and I would like to be able to access this array by using methods in the parent array.
So far my best efforts is:
function myFunction () {
var teamNo = 3;
var allNames =["n1","n2","n3","n4"] ;
var createnames = function () {
var names = [];
for ( var j = 0; j <=3 ; j ++) {
(function (j) {
var localNames = ["local1-names"+j,"local2-names"+j];
names[j] = (function (player){
return {
namArr: localNames,
name: allNames[j],
addName: (function (player){
localNames.push(player);
}) (player),
team: teamNo
};
});
}) (j);
}
return names;
}
var myname = createnames();
var foo = myname[0]().namArr;
var foo1 = myname[1]().namArr;
myname[1]().addName("added");
var foo2 = myname[1]().namArr;
var foo3 = myname[2]().namArr;
var debug = true;
}
As soo as I add the code to implement the sub array I get a runtime error saying that addName does not exist.
You're invoking this immediately:
addName: (function (player) {
localNames.push(player);
})(player)
instead of assigning it:
addName: function (player) {
localNames.push(player);
}
Also, each names[] function takes a player, and so does the addPlayer() function, making the names[] parameter unreachable. If you're not going to pass anything to the names[] functions, then remove the parameter.
And I'd suggest using named functions instead of inlined IIFEs.
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.