Javascript method in Function Error with "is not a function" - javascript

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);

Related

How to call a function inside of another function in JavaScript

I looked on whole stack overflow but unfortunately, answer of this question wasn't available so I have a class and inside I have a function:
class DonutMaker {
constructor() {
this.donut_creater = document.getElementById("donut_creater");
this.donut_creater.addEventListener("click", this.donutMaker);
this.auto_clicker = document.getElementById("auto_clicker");
this.auto_clicker.addEventListener("click", this.autoClickerHandler);
}
donutMaker() {
this.selection = document.getElementById("donut_quantity");
this.quantity = this.selection.innerText;
this.updated_quantity = parseInt(this.quantity);
this.updated_quantity = this.updated_quantity + 1;
this.selection.innerText = this.updated_quantity;
}
autoClickerHandler = () => {
this.selection = document.getElementById("donut_quantity");
this.quantity = this.selection.innerText;
this.updated_quantity = parseInt(this.quantity);
this.new_quantity = this.updated_quantity - 1;
if (this.updated_quantity >= 1) {
this.selection.innerText = this.new_quantity;
this.quantity = this.new_quantity;
this.selection2 = document.getElementById("auto_clicker_quantity");
this.auto_clicker_quantity = this.selection2.innerText;
this.auto_clicker_quantity = parseInt(this.auto_clicker_quantity);
this.auto_clicker_quantity_updated = this.auto_clicker_quantity + 1;
this.selection2.innerText = this.auto_clicker_quantity_updated;
this.autoClicker;
} else {
console.log("Not Eligible");
}
};
autoClicker = () => {
console.log("Hello");
// console.log("Auto clicker");
};
}
let obj = new DonutMaker();
//This line at the end of if supposed to call autoClicker but it isnt
this.autoClicker;
It works for me. Two things:
I changed Class to class.
I created an instance of the Student class.
class Student{
a(){
this.b();
}
b(){
console.log("B");
}
}
var student = new Student()
student.a(); // B
JavaScript is case-sensitive so class and Class are considered to be different. The keyword is class. Class would be an unexpected identifier at this position.
A class can be imagined as a blueprint for an instance of that class, similar to how you have a plan for a house. However, for you to be able to work with a class you need to create an object (= an instance of a class) based on said blueprint (= building the actual house using the plan). You will need to use the new keyword for this.
On that instance you can then call the methods you have defined.
// Student class/ blueprint
class Student {
a() {
console.log("a");
this.b();
}
b() {
console.log("b");
}
}
// Create an instance/ object of type Student
const myStudent = new Student();
// Now call method on that instance
myStudent.a();
You should probably get your head around the basic concepts of object oriented programming as explained in this blog post.

How to bind "this" automatically while calling a function?

My example code:
var Person = (function () {
var __sym = Symbol('Person');
class Person {
constructor(name) {
this[__sym] = { name: name };
}
getName() {
let _this = this[__sym];
return _this.name;
}
}
return Person;
}());
var person = new Person('Hermione');
console.log(person.name); // undefined
console.log(person.getName()); // Hermione
In this example, I'd use __sym as a key to assign to the private data.
My question is: How to bind this[__sym] to every method inside the Person class?
My real project:
let Chatwindow = (function () {
let __sym = Symbol('Chatwindow');
let __data = {};
// for typo
let __prop = {
targetUserId: 'targetUserId'
};
__data.init = function (...args) {
let _this = this[__sym];
let options = args[0];
// validating the type of 'options' and the properties...
// just get what I need
_this[__prop.targetUserId] = options[__prop.targetUserId];
(async () => {
let messages = await __data.getMessagesAsync.call(_this);
// my goal:
// let messages = await __data.getMessagesAsync();
})();
};
__data.getMessagesAsync = function () {
let _this = this;
let promise = new Promise(function (done) {
// create model before sending
let model = { [__prop.targetUserId]: _this[__prop.targetUserId] };
// sending...
done();
});
return promise;
};
class Chatwindow {
constructor() {
this[__sym] = {};
}
set init(value) {
return __data.init;
}
get init() {
return (...args) => __data.init.call(this, ...args);
}
}
return Chatwindow;
}());
Everytime I call a method, I have to use call(_this) function to bind the key, like this:
let messages = await __data.getMessagesAsync.call(_this);
After that, inside the getMessagesAsync method, I can assign to the private data using this property.
What I want to achieve: I want to bind all of the methods just one time inside the init method. How can I do that?
Something like this:
__data.getMessagesAsync.oncall = function () {
// bind this with this[__sym]
};
Then,
__data.getMessagesAsync(); // no need to pass anymore
Thank you!
You can use arrow functions, so you will be sure that context (this) will be same every time (this will be pointing to parent this no matter from where arrow function is called)
__data.getMessagesAsync = () => {
let promise = new Promise((done) => {
// create model before sending
let model = { [__prop.targetUserId]: this[__prop.targetUserId] };
// sending...
done();
});
return promise;
}
In javascript, using function_name.bind(o) allows you to create a new function whose context this is bound to the object o.
What you want is to create a new function:
__data.boundMessagesFunction = __data.getMessagesAsync.bind(_this);
Now you can call:
let messages = await __data.boundMessagesFunction();

access fields in javascript functions

I am having trouble accessing fields(variables) in a function definition. My question is with this code:
<script>
function test(){ var book= [];}
var arrs = test();
alert(arrs.book);
</script>
It gives me 'undefined', why? I would expect it to be an empty value. Is there any way I can access this book array variable in arrs?
arrs is undefined because test() doesn't return anything. All the function does is locally declare a variable and then end, so the local variable just falls out of scope and nothing happens.
Based on the usage, it looks like you want your function to maybe return an object?:
function test() {
return { book: [] };
}
Or maybe the function should itself construct an object and you meant to call it with new?:
function test() {
this.book = [];
}
var arrs = new test();
Like Carcigenicate said, you are missing the return statement inside test().
function test(){
var book= [];
// missing return...
return book;
}
var arrs = test();
alert(arrs);
Try it now.
// old way
function Test1() {
this.books = ['t1']
}
var t1 = new Test1()
alert(t1.books)
//es6 class
class T2 {
constructor() {
this.books = ['t2']
}
}
var t2 = new T2()
alert(t2.books)
// plain object
var t3 = {
books: ['t3']
}
alert(t3.books)
// static field
function T4() {}
T4.prototype.books = []
var t4a = new T4(),
t4b = new T4()
t4a.books.push('t4')
alert(t4b.books)

inner function variable losing 'this' value of outer function

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.

How do I augment a method from the superclass in javascript

I have a method in a base class that I want to keep in a subclass, but just add to it. I've found lots of stuff on augmenting classes and objects with properties and methods, but I can't find, or don't understand, how to just augment the method. The worst case scenario is that I would have to paste the entire method of the parent class into the subclass, but that seems like duplicate code... please help
function someObject (){
this.someProperty = 1;
this.incrementProperty = function incrementProperty(){
this.propertyOfSomeObject += 1;
}
}
function newObject (){
someObject.call(this);
this.incrementProperty = function incrementProperty(){
//do everything the super class has for this property already
return this.someProperty;
}
}
var incrementer = new newObject;
alert (incrementer.incrementProperty()); //I want output to be 2
// parent object
function someObject () {
this.someProperty = 1;
}
// add incrementProperty to the prototype so you're not creating a new function
// every time you instantiate the object
someObject.prototype.incrementProperty = function() {
this.someProperty += 1;
return this.someProperty;
}
// child object
function newObject () {
// we could do useful work here
}
// setup new object as a child class of someObject
newObject.prototype = new someObject();
// this allows us to use "parent" to call someObject's functions
newObject.prototype.parent = someObject.prototype;
// make sure the constructor points to the right place (not someObject)
newObject.constructor = newObject;
newObject.prototype.incrementProperty = function() {
// do everything the super class has for this property already
this.parent.incrementProperty.call(this);
return this.someProperty;
}
var incrementer = new newObject();
alert (incrementer.incrementProperty()); // I want output to be 2
See: http://jsfiddle.net/J7RhA/
this should do, you have to use prototype to have a real concept of oo with javascript
function someObject (){
this.someProperty = 1;
this.propertyOfSomeObject = 0;
this.incrementProperty = function incrementProperty(){
this.propertyOfSomeObject += 1;
return this.propertyOfSomeObject;
}
}
function newObject (){
someObject.call(this);
this.incrementProperty = function incrementProperty(){
this.__super__.incrementProperty.apply(this);
return this.propertyOfSomeObject + 1;
}
}
newObject.prototype = new someObject()
newObject.prototype.__super__ = newObject.prototype
var incrementer = new newObject();
alert(incrementer.incrementProperty()); //I want output to be 2
experiment removing incrementProperty from newObject and it will return 1
I usually use the augment library to write classes in JavaScript. This is how I would rewrite your code using augment:
var Foo = Object.augment(function () {
this.constructor = function () {
this.someProperty = 1;
};
this.incrementProperty = function () {
this.someProperty++;
};
});
var Bar = Foo.augment(function (base) {
this.constructor = function () {
base.constructor.call(this);
};
this.incrementProperty = function () {
base.incrementProperty.call(this);
return this.someProperty;
};
});
As you can see since Bar extends Foo it gets Foo.prototype as a parameter (which we call base). This allows you to easily call the base class constructor and incrementProperty functions. It also shows that the constructor itself is just another method defined on the prototype.
var bar = new Bar;
alert(bar.incrementProperty());
The output will be 2 as expected. See the demo for yourself: http://jsfiddle.net/47gmQ/
From this answer:
Overriding functions
Sometimes children need to extend parent functions.
You want the 'child' (=RussionMini) to do something extra. When RussionMini can call the Hamster code to do something and then do something extra you don't need to copy and paste Hamster code to RussionMini.
In the following example we assume that a Hamster can run 3km an hour but a Russion mini can only run half as fast. We can hard code 3/2 in RussionMini but if this value were to change we have multiple places in code where it needs changing. Here is how we use Hamster.prototype to get the parent (Hamster) speed.
// from goog.inherits in closure library
var inherits = function(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
};
var Hamster = function(name){
if(name===undefined){
throw new Error("Name cannot be undefined");
}
this.name=name;
}
Hamster.prototype.getSpeed=function(){
return 3;
}
Hamster.prototype.run=function(){
//Russionmini does not need to implement this function as
//it will do exactly the same as it does for Hamster
//But Russionmini does need to implement getSpeed as it
//won't return the same as Hamster (see later in the code)
return "I am running at " +
this.getSpeed() + "km an hour.";
}
var RussionMini=function(name){
Hamster.apply(this,arguments);
}
//call this before setting RussionMini prototypes
inherits(RussionMini,Hamster);
RussionMini.prototype.getSpeed=function(){
return Hamster.prototype
.getSpeed.call(this)/2;
}
var betty=new RussionMini("Betty");
console.log(betty.run());//=I am running at 1.5km an hour.

Categories