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?
Related
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);
How to create an object of Manager with name "joe" and create subordinate "John" and add Salary to John.
Ex. var result = new Manager("Joe").getSubordinate("John").addSalary(10000);
function Manager(name){
this.name = name;
this.getSubordinate = function(submane){
return submane;
}
}
var result = new Manager('Joe');
I am able to write down so far.
if you want to chain the functions you need to return the objects like if you have a class Manager and one of Ordinate:
function Manager(name){
this.name = name;
this.subordinate = {John : new Subordinate('John')};
this.getSubordinate = function getSubordinate(subName){
return this.subordinate[subName];
};
}
function Subordinate(name){
this.name = name;
this.salary = 0;
this.addSalary = function addSalary(salary){
this.salary = salary;
return salary;
};
}
var result = new Manager('Joe').getSubordinate("John").addSalary(10000);
but i think you want as a result a Manager object so I recomend to use this way
function Manager(name){
var self = this;
this.name = name;
this.subordinate = {John : new Subordinate('John')};
this.getSubordinate = function getSubordinate(subName){
return this.subordinate[subName];
};
this.addSalaryToSubordinate = function addSalaryToSubordinate(subName, salary){
self.getSubordinate(subName).addSalary(salary);
return self;
};
}
function Subordinate(name){
this.name = name;
this.salary = 0;
this.addSalary = function addSalary(salary){
this.salary = salary;
return salary;
};
}
var result = new Manager('Joe').addSalaryToSubordinate ("John", 10000);
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;
})();
I have tried to create an instance of object internally like the following:
var oo = function(){
return new func();
}
var func = function(){
this.name;
this.age;
};
func.prototype = {
setData: function(name, age){
this.name = name;
this.age = age;
},
getData: function (){
return this.name + " " + this.age;
}
}
When usage, I got an error oo.setData is not a function.
oo.setData("jack", 15);
console.log(oo.getData());
What's wrong in my code?
This happens because oo is not a "func", oo returns a new func. You could set the data using
oo().setData('jack',15);
But then you have no way of accessing it.
You could also use
var newfunc = oo();
newfunc.setData('jack',15);
newfunc.getData();
oo is a function to create a object.
var oo = function(){ //the oo variable is used to create func() objects
return new func();
}
var func = function(){ //function
this.name;
this.age;
};
func.prototype = { //define properties to func
setData: function(name, age){
this.name = name;
this.age = age;
},
getData: function (){
return this.name + " " + this.age;
}
}
//create instance
var myObject = oo();
//or
var myObject = new func();
//Use
myObject.setData("jack", 12);
//Get a property
console.log(myObject.getData())
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.