Javascript context in inner class object - javascript

Prior to using ES6 we could instantiate a "class" like so...
var Animal = function(){}
and then...
var dog = new Animal()
the context within the "class" will be the class (instance) itself
var Animal = function( name ){
this.name = name;
this.getName = function(){
// the context here (this) is the class (Animal)
return this.name; // works well
}
}
The question is, if I wouldn't want to pollute the root scope and use sub-objects, for various uses, then the context would become the object in which the function is being kept
var Animal = function( name ){
this.utilities = {
this.getName : function(){
// the context here is the 'utilities' object so...
return this.name // wouldn't work
}
}
}
of course we could always use something in the form of
dog.utilities.getName.call(dog)
but this would be kind of long and uncomfortable...
is there a way to create the 'utilities' object and apply the context to all of its functions to point back to the root scope? without having to use call and apply every time? (an answer without using ES6 would be great...)

One way to ensure that this is what you want it to be in the various utilities functions is to use arrow functions for them, since arrow functions close over the this where they're defined:
class Animal {
constructor(name) {
this.name = name;
this.utilities = {
getName: () => { // This is an arrow function
return this.name; //
} //
};
}
}
const dog = new Animal("dog");
console.log(dog.utilities.getName()); // "dog"
This is basically the ES2015+ version of the old var t = this; solution:
function Animal(name) {
var t = this;
this.name = name;
this.utilities = {
getName() {
return t.name;
}
};
}
var dog = new Animal("dog");
console.log(dog.utilities.getName()); // "dog"
In both cases, this means that you're creating new function objects for each individual instance of Animal (the code will be shared between those objects, but the objects are distinct). That's fine unless there are going to be a lot of Animal instances.
Alternately, you could have a helper that you pass the instance to:
const Animal = (function() {
class Utilities {
constructor(animal) {
this.a = animal;
}
getName() {
return this.a.name;
}
}
class Animal {
constructor(name) {
this.name = name;
this.utilities = new Utilities(this);
}
}
return Animal;
})();
const dog = new Animal("dog");
console.log(dog.utilities.getName()); // "dog"
or
var Animal = (function() {
function Utilities(animal) {
this.a = animal;
}
Utilities.prototype.getName = function getName() {
return this.a.name;
};
return function Animal(name) {
this.name = name;
this.utilities = new Utilities(this);
}
})();
var dog = new Animal("dog");
console.log(dog.utilities.getName()); // "dog"
...which lets utilities reuse its function objects via Utilities.prototype.

You could probably use the following:
var utilities = function (context) {
return {
getName: function () {
console.log(context.name)
}
}
}
var Animal = function( name ){
this.name = name
this.utilities = utilities.call(null, this)
}
var dog = new Animal('dog')
dog.utilities.getName()
But, if you are okay doing this: dog.getName() instead of dog.utilities.getName() then you might have a cleaner solution (IMO) as follows:
var Animal = function( name ){
this.name = name
}
var utilities = {
getName: function () {
console.log(this.name)
}
};
Object.assign(Animal.prototype, utilities)
var dog = new Animal('dog')
dog.getName()
Let me know if that works. Thanks.
NEW ANSWER:
var UTILITIES = {
getName: function () {
console.log(this.self.name)
}
}
var Animal = function (name) {
this.name = name
this.utilities = Object.create(UTILITIES, {
self: {
value: this
}
})
}
var dog = new Animal('dog')
dog.utilities.getName()
Variation includes the use of a 'self' attribute which points to the instance of interest. Now, this could look more intuitive.

You can use getter methods. I find them very useful for cases where I need formatted value. This way, the utilities/ logic is only known to this class and is not exposed outside.
function Person(fname, lname) {
var _fname = fname;
var _lname = lname;
Object.defineProperty(this, 'fullName', {
get: function(){
return _fname + ' ' + _lname
}
});
Object.defineProperty(this, 'firstName', {
get: function(){
return _fname
},
set: function(value) {
_fname = value;
}
});
Object.defineProperty(this, 'lastName', {
get: function(){
return _lname
},
set: function(value) {
_lname = value;
}
});
}
var person = new Person('hello', 'world');
console.log(person.fullName);
person.firstName = 'Hello';
console.log(person.fullName);
person.lastName = 'World'
console.log(person.fullName);

Related

Create dynamic prototype like jquery function chain

I try to create a dynamic prototype like jQuery and able to function chain, but I got error without use new and inside of function, return this object correct?
(function() {
var peopleDynamicProto = function(name, age, state) {
this.name = name;
this.age = age;
this.state = state;
if (typeof this.printPerson !== 'function') {
peopleDynamicProto.prototype.printPerson = function() {
console.log(this.name + ',' + this.age + ',' + this.state);
return this;
};
}
if (!this.hasOwnProperty('gender')) {
peopleDynamicProto.prototype.gender = 'male';
return this;
}
}
window.peopleDynamicProto = peopleDynamicProto;
return peopleDynamicProto;
})();
//var person1 = new peopleDynamicProto('john', 23,'CA');
//person1.printPerson();
peopleDynamicProto('john', 23, 'CA').printPerson(); //got error
Anyone know where is the problem?
You have to use "new" if you want to create a new object based off a prototype.
I am not sure what you are trying to do exactly, and why you are trying to create the prototype dynamically. I'm not going to say 100% sure, but I don't think jQuery does that (plus it looks like a very bad practice).
If you're trying to do something like jQuery, where your class is chainable and can be chained as (new peopleDynamicProto(...)).print() or peopleDynamicProto(...).print(), then you can do something like this:
function peopleDynamicProto(name) {
if (this instanceof peopleDynamicProto) {
/* initialize attributes here */
this.name = name;
} else {
return new peopleDynamicProto(name);
}
}
peopleDynamicProto.prototype.printPerson = function() {
console.log( this.name );
return this;
}
Now you should be able call it in both ways:
peopleDynamicProto('john').printPerson();
(new peopleDynamicProto('john')).printPerson();
If you don't care about supporting both ways, then you can just return an object, e.g.:
function peopleDynamicProto(name) {
return {
name: name,
printPerson = function() {
console.log( this.name );
return this;
}
};
}
peopleDynamicProto('John').printPerson();
(There are other ways of doing that)
I think the reason why you are getting such an error is because what you are returning here is a function not an object but you are trying to access a property of an object.
For eg,
If you write as:
**
var myFun = function(){
this.message = "TEST";
}
**
you cannot access myFUN.message because here myFun is a function not an object of this function constructor.
To access its property, you need to do something like
(new myFun()).message;
Similarly in you case what you return is "peopleDynamicProto" which is a function only, not an object of this function constructor.
To access method printPerson (which is a member), you need to create an instance of peopleDynamicProto and access its member
I guess you missed the new operator- see this for reference
The new operator creates an instance of a user-defined object type or
of one of the built-in object types that has a constructor function.
See demo below:
(function() {
var peopleDynamicProto = function(name, age, state) {
this.name = name;
this.age = age;
this.state = state;
if (typeof this.printPerson !== 'function') {
peopleDynamicProto.prototype.printPerson = function() {
console.log(this.name + ',' + this.age + ',' + this.state);
return this;
};
}
if (!this.hasOwnProperty('gender')) {
peopleDynamicProto.prototype.gender = 'male';
return this;
}
}
window.peopleDynamicProto = peopleDynamicProto;
return peopleDynamicProto;
})();
var person1 = new peopleDynamicProto('john', 23,'CA');
person1.printPerson();
new peopleDynamicProto('john', 23, 'CA').printPerson(); //got error
you cant create prototype inside contructor. see this explanation
defining-prototype-methods-inside-the-constructor
use this keyword instead of prototype:
(function(){
var peopleDynamicProto = function(name, age, state){
this.name = name;
this.age = age;
this.state = state;
this.printPerson = function(){
console.log( this.name + ',' + this.age + ',' + this.state );
return this;
}
if( !this.hasOwnProperty('gender') ){
peopleDynamicProto.prototype.gender = 'male';
return this;
}
}
window.peopleDynamicProto = peopleDynamicProto;
return peopleDynamicProto;
})();
peopleDynamicProto('john', 23,'CA').printPerson();
or use prototype outside constructor(this is better since function object not recreated each time object created)
(function(){
var peopleDynamicProto = function(name, age, state){
this.name = name;
this.age = age;
this.state = state;
if( !this.hasOwnProperty('gender') ){
peopleDynamicProto.prototype.gender = 'male';
return this;
}
}
if( typeof this.printPerson !== 'function' ){
peopleDynamicProto.prototype.printPerson = function(){
console.log( this.name + ',' + this.age + ',' + this.state );
return this;
};
}
window.peopleDynamicProto = peopleDynamicProto;
return peopleDynamicProto;
})();

JavaScript OOP: Uncaught TypeError: is not a function

Have trouble with object creation. Console says that something wrong in the last line. Please tell how it should be, I more familar with Java, so this is little bit confusing for me.
var dog = {
name:"Dog",
age:"11",
getName : function() {
alert(this.name);
}
}
function Dog(name, age) {
this.name = name;
this.age = age;
}
var d1 = new Dog("Rex", 8);
d1.getName();
Your dog is just a simple Object literal,
that means that your getName is bound to it, not to your Dog class.
You can make that function a method of Dog instead:
/*var dog = {
name:"Dog",
age:"11",
getName : function() {
alert(this.name);
}
}*/
function Dog(name, age) {
this.name = name;
this.age = age;
}
Dog.prototype.getName = function() {
console.log( this.name );
}
var d1 = new Dog("Rex", 8);
d1.getName(); // "Rex"
Here's a variant that uses your settings "defaults"
function Dog() {
this.name = "Dog"; // Default name
this.age = 11; // Default age
}
Dog.prototype.getName = function() {
console.log( this.name );
}
var d1 = new Dog();
d1.name = "Rex"; // Override default name
d1.getName(); // "Rex"
You can use class with syntaxic sugar to properly create objects in ES6.
In your exemple that would write like this :
'use strict';
class Dog{
constructor(name, age){
this.name = name;
this.age = age;
}
getName(){
console.log(this.name);
}
}
let doggy = new Dog("krypto", 125);
doggy.getName();
Traditional OO in JavaScript
function Dog(name, age) {
this.name = name || "Dog";// if the name is not given, it defaults to "Dog"
this.age = age || "11";
}
Dog.prototype.getName = function() {
alert(this.name);
}
var d1 = new Dog("Rex", 8);
d1.getName();
More Explicit OO in JavaScript
function createDog(name, age) {
// create a new dog and return it
var dog = {
name: name || "Dog",// if the name is not given, it defaults to "Dog"
age: age || "11"
};
return dog;
}
createDog.getName = function(dog) {
// explicit dog as 1st parameter
alert(dog.name);
}
//createDog is a normal function that returns something, no "new" needed
var d1 = createDog("Rex", 8);
createDog.getName(d1);

Accessing this variable from method instance

How do you access the this object from another object instance?
var containerObj = {
Person: function(name){
this.name = name;
}
}
containerObj.Person.prototype.Bag = function(color){
this.color = color;
}
containerObj.Person.prototype.Bag.getOwnerName(){
return name; //I would like to access the name property of this instance of Person
}
var me = new Person("Asif");
var myBag = new me.Bag("black");
myBag.getOwnerName()// Want the method to return Asif
Don't put the constructor on the prototype of another class. Use a factory pattern:
function Person(name) {
this.name = name;
}
Person.prototype.makeBag = function(color) {
return new Bag(color, this);
};
function Bag(color, owner) {
this.color = color;
this.owner = owner;
}
Bag.prototype.getOwnerName = function() {
return this.owner.name;
};
var me = new Person("Asif");
var myBag = me.makeBag("black");
myBag.getOwnerName() // "Asif"
Related patterns to deal with this problem: Prototype for private sub-methods, Javascript - Is it a bad idea to use function constructors within closures?

JavaScript Closures Manipulation

I'm doing some Node.js and I want to use the closure representation to create my objects. I think I'm missing something, because something simple like this isn't working:
var Room = function(foo) {
this.name = foo;
this.users= [];
return {
getName : function() {
return this.name;
}
}
}
var room = new Room("foo");
console.log(room.getName());
I also have tried without the parameter.. and still not working.
var Room = function() {
this.name = "foo";
this.users= [];
return {
getName : function() {
return this.name;
}
}
}
var room = new Room();
console.log(room.getName());
However, something like this works:
var Room = function(foo) {
this.name = foo;
this.users= [];
}
var room = new Room("foo");
console.log(room.name);
I can't understand why this isn't working.
--Edited
Thanks to Amadan I have found the right way to do it:
var Room = function() {
var name = "foo";
var users= [];
return {
getName : function() {
return name;
}
}
}
var room = new Room();
console.log(room.getName());
This way "name" and "users" are encapsulated.
return in a constructor will overwrite this. So the right way to do this is:
var Room = function(foo) {
this.name = foo;
this.users= [];
this.getName = function() {
return this.name;
}
}
or
var Room = function(foo) {
return {
name: "foo",
users: [],
getName : function() {
return this.name;
}
}
}
The first one does everything on the original this; the second one replaces this with everything you need.

Javascript prototype and issues accessing class

Family = function(name) {
this._Name = name;
}
Family.prototype = {
getName: function() {
return this._Name;
},
People: function(num) {
this._Number = num;
}
}
Family.People.prototype = {
clearNumber: function() {
this._Number = 0;
}
}
People is a nested class. Its parent class is Family.
I get the error that Family.People is undefined. Could someone correct the code above?
Working code
// function doesn't need "new" operator
var Family = function(name) { this._Name = name; };
Family.prototype = {
getName: function() { return this._Name; }, // missing comma
People: function(num) {
this._Number = num;
}
};
// work with prototypes
Family.prototype.People.prototype = {
clearNumber: function() { this._Number = 0; }
};
This will work. But you have to be aware, that when you call:
var f = new Family("Doe");
f.People is just an object constructor, and not an instance of some other object. You will have to instantiate it as well like:
f.members = new f.People(3);
Sou you have a constructor within your instance which is rather confusing.
A better approach
So it would probably be better if you'd write your prototypes this way:
var Family = function(name) {
this._Name = name;
this.getName = function() { return this._Name; };
};
Family.People = function(num) {
this._Number = num;
this.clearNumber = function() { this._Number = 0; };
};
This actually makes a class within a class (and not within instances). So upper lines would be called this way:
var f = new Family("Doe");
f.members = new Family.People(3);
Drill down of f instance would look like:
f
_Name
getName()
members
_Number
clearNumber()
Private variables
var Family = function(name) {
var _name = name;
this.getName = function() { return _name; };
};
Family.People = function(num) {
var _num = num;
this.getNumber = function() { return _num; }
this.clearNumber = function() { _num = 0; };
};
This way we make variables private and only accessible within so they can't be manipulated outside. You must always use functions to manipulate them. This makes it more robust especially when there are certain business rules related to variable values.
var f = new Family("Doe");
f._name; // this is undefined because "_name" is private closure variable
Drill down of f instance would now look more like a class object instance:
f
getName()
members
getNumber()
clearNumber()
Notice that you are assigning to Family.prototype.People then trying to access Family.People.
Family is not an instance of Family thus it does not have the properties of that class - Family is an instance of Function thus you are trying to access Function.prototype.People in that 3rd statement. (this is a bit of a simplification)
i.e. what you want to be doing is
Family.prototype.People.prototype = {
clearNumber:function(){this._Number = 0;}
}
You are also missing a comma before people, but I assume this is a typo...
You should declare the People constructor as a key in the Family object:
Family.People = function(num) {
this._Number = num;
}
The Family prototype will be in the prototype chain for new objects of type Family; not a part of Family itself.

Categories

Resources