JavaScript: Binding the context of object's methods to parent - javascript

This example works as expected:
class A {
constructor(name) {
this.name = name
}
}
A.prototype.someMethod1 = function() { console.log("1: " + this.name) }
A.prototype.someMethod2 = function() { console.log("2: " + this.name) }
let a = new A("John")
a.someMethod1()
a.someMethod2()
Sometimes we need methods to be grouped by it's meaning into child objects, to call it like: a.some.method1(), a.some.method2(). Let's try to write:
class A {
constructor(name) {
this.name = name
}
}
A.prototype.some = {
method1: function() { console.log("1: " + this.name) },
method2: function() { console.log("2: " + this.name) }
}
Now we able to call a.some.method1() and a.some.method2(), but "this" inside of it points to "a.some" and not to "a" as desired. So, "this.name" inside these metods now will be "undefined".
So can I bind context of methods to main object?
Currently I know only one method to do that. Something like this in constructor:
class A {
constructor(name) {
this._name = name
// bind context to this
Object.keys(this.name).forEach(n => {
this.name[n] = this.name[n].bind(this)
})
}
}
But are there more elegant methods?
Note: it should be specified at the declaration time. Modifications of method calls like a.some.method1().call(a) are not acceptable.

This may not be at all what you're looking for, but you could consider doing something like this:
class A {
constructor(name) {
this.name = name;
this.some = new Some(this);
}
}
class Some {
constructor(a) {
this.a = a;
}
method1() { console.log("1: " + this.a.name) }
method2() { console.log("2: " + this.a.name) }
}
var x = new A('x name');
x.some.method1();
x.some.method2();

Yes it's possible you can define the instance of class and then add method on prototype by binding it to a
class A {
constructor(name) {
this.name = name
}
}
let a = new A("Hello")
A.prototype.some = {
method1: function() { console.log("1: " + this.name) }.bind(a),
method2: function() { console.log("2: " + this.name) }.bind(a)
}
a.some.method1()

You could make some a function that returns a object where you can specify what methods to have and then you can call those methods from that object.
class A {
constructor(name) {
this.name = name
}
}
A.prototype.someMethod1 = function() {
console.log("1: " + this.name)
}
A.prototype.someMethod2 = function() {
console.log("2: " + this.name)
}
A.prototype.some = function() {
return {
someMethod1: this.someMethod1.bind(this),
someMethod2: this.someMethod2.bind(this)
}
}
let a = new A("John")
a.someMethod1()
a.someMethod2()
a.some().someMethod1()
a.some().someMethod2()
The same logic can be implemented with class methods without prototype. You can also omit the bind with arrow functions as class properties but you should care about support in this case.
class A {
constructor(name) {
this.name = name
}
some1 = () => `1. ${this.name}`
some2 = () => `2. ${this.name}`
some = () => {
return {
some1: this.some1,
some2: this.some2
}
}
}
let a = new A("John")
console.log(a.some1())
console.log(a.some2())
console.log(a.some().some1())
console.log(a.some().some2())
You can also use getter for some so you can call it like property instead of method.
class A {
constructor(name) {
this.name = name
}
some1 = () => `1. ${this.name}`
some2 = () => `2. ${this.name}`
get some() {
return {
some1: this.some1,
some2: this.some2
}
}
}
let a = new A("John")
console.log(a.some1())
console.log(a.some2())
console.log(a.some.some1())
console.log(a.some.some2())

Related

What is the nodejs equivalent of PHP trait

In PHP , I've used traits before which is a nice way of separating out reusable code & generally making things more readable.
Here is a specific Example: ( trait and class can be in separate files ) . How could I do this in nodejs?
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
..more functions..
}
class TheWorld {
use HelloWorld;
}
$o = new TheWorldIsNotEnough();
$o->sayHello();
?>
In Nodejs , I have looked at at Stampit which looks quite popular , but surely there is a simple way to compose functions in a nice OOP & make more readable in nodejs without depending on a package?
Thanks for your time!
In JavaScript you can use any function as trait method
function sayHello() {
console.log("Hello " + this.me + "!");
}
class TheWorld {
constructor() {
this.me = 'world';
}
}
TheWorld.prototype.sayHello = sayHello;
var o = new TheWorld();
o.sayHello();
or pure prototype version
//trait
function sayHello() {
console.log("Hello " + this.me + "!");
}
function TheWorld() {
this.me = "world";
}
TheWorld.prototype.sayHello = sayHello;
var o = new TheWorld();
o.sayHello();
You can even create function that's apply traits to class
//trait object
var trait = {
sayHello: function () {
console.log("Hello " + this.me + "!");
},
sayBye: function () {
console.log("Bye " + this.me + "!");
}
};
function applyTrait(destClass, trait) {
Object.keys(trait).forEach(function (name) {
destClass.prototype[name] = trait[name];
});
}
function TheWorld() {
this.me = "world";
}
applyTrait(TheWorld, trait);
// or simply
Object.assign(TheWorld.prototype, trait);
var o = new TheWorld();
o.sayHello();
o.sayBye();
There is the NPM module: https://www.npmjs.com/package/traits.js
Code example from their README:
var EnumerableTrait = Trait({
each: Trait.required, // should be provided by the composite
map: function(fun) { var r = []; this.each(function (e) { r.push(fun(e)); }); return r; },
inject: function(init, accum) { var r = init; this.each(function (e) { r = accum(r,e); }); return r; },
...
});
function Range(from, to) {
return Trait.create(
Object.prototype,
Trait.compose(
EnumerableTrait,
Trait({
each: function(fun) { for (var i = from; i < to; i++) { fun(i); } }
})));
}
var r = Range(0,5);
r.inject(0,function(a,b){return a+b;}); // 10

Javascript context in inner class object

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

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

Class with multiple functions in JavaScript

I try to make a function that can either show a confirmation window or a dialog form. Both these two functions are in the same window, so I might reuse the code in both.
I guess it should be something like
const MyWindow = function (options) {
};
MyWindow.prompt = function (options) {
..
};
MyWindow.confirm = function (options) {
...
}
MyWindow.alert = function (options) {
...
}
The problem is that I don't know where to draw the window.
I have tried creating a new method
const MyWindow = function (options) {
};
MyWindow.createElements = function (options) {
this.window = document.createElement('div');
this.window.style.height = 300;
this.window.style.width = 300;
document.body.insertBefore(this.window, document.body.childNodes[0]);
};
MyWindow.prompt = function (options) {
this.createElements();
this.window.style.background-color = 'red';
};
but this.createElements() and this.window are not accessible from the prompt() function.
How do you normally develop something like this? Should I use ES6 classes?
You can use functions and the new-keyword. This will create a new object with access to alert and prompt, while the init-method is private for MyWindow.
const MyWindow = function() {
const init = () => console.log("do init stuff");
this.alert = () => {
init();
console.log("alert!");
};
this.prompt = () => {
init();
console.log("prompt");
}
}
const myWindow = new MyWindow();
myWindow.alert();
myWindow.prompt();
You should use .prototype to extend a class. This should help you...
Refer this link
var MyWindow = function (options) {
}
MyWindow.prototype.createElements = function (options) {
this.window = document.createElement('div');
this.window.style.height = '300px';
this.window.style.width = '300px';
document.body.insertBefore(this.window, document.body.childNodes[0]);
};
MyWindow.prototype.prompt = function (options) {
this.createElements();
this.window.style.backgroundColor = 'red';
}
var el = new MyWindow()
el.prompt()
when you say class you could have a look at ES2015 which is the new Javascript. Let me give you an example:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log('Hello ' + this.name + ' wow you are ' + this.age + ' years old');
}
}
I would use ES2015 literals in the example above in my console log but I can't do this on here, or can I?
To use the class above you would just do:
const person = new Person('Jack', 21)
person.sayHello()
output - Hello Jack wow you are 21 years old
So that's an example of you would would write a class in ES2015 with some methods. If you want to add methods to a 'class' the older way you would just do something like:
function Person(name, age) {
this.name = name;
this.age = age;
}
To add a method (extend) your function you just need to use prototype like so:
Person.prototype.sayHello = function() {
console.log('Hello ' + this.name + ' wow you are ' + this.age + ' years old');
}

How to use composition with JavaScript

I have something like this:
dog = function () {
this.age;
this.name;
this.dog1 = new dog.shepherd();
}
dog.prototype = {
bark : function () {
alert( (this.getName()) + " is barking." );
},
[3]**getName** : function () {
return this.name;
}
}
dog.shepherd = function () {
this.something;
}
dog.shepherd.prototype = function () {
guard : function () {
alert( ([2]**parent.getName()**) + " is barking." );
}
}
window.onload = function () {
var n = new dog();
[1]**n.guard()**;
}
How can I use a function which is prototyped to the dog from dog.speherd prototype?
In other words, when [1] is executed, then [2] is written in pseudo-code, to call [3].
I know this code shouldn't work; it's just to help me explain what I want.
Your title says you want to use composition, but your question body implies inheritance. Here's how you would do this with composition, which is probably the better option. A shepherd "is not a" dog after all (nor vice versa) so inheritance probably isn't right here.
Also, you usually don't want to set the entire prototype of a function like you're doing; just add the functions you want. And constructor functions by convention start with a capital letter.
function Shepherd(mydog) {
this.dog = mydog;
}
Shepherd.prototype.guard = function () {
alert( this.dog.getName() + " is barking." );
}
function Dog(age, name) {
this.age = age;
this.name = name;
}
Dog.prototype.getName = function () {
return this.name;
}
Dog.prototype.bark = function () {
alert(this.getName() + " is barking." );
}
window.onload = function () {
var someDog = new Dog(4, "Ubu");
var someShepherd = new Shepherd(someDog);
someShepherd.guard();
}
jsfiddle
I don't know why you need such code, but you can try this:
dog = function () {
this.age;
this.name;
this.dog1 = new dog.shepherd(this);
var d = this.dog1;
for (var i in dog.shepherd.prototype) {
this[i] = function(){
return this.dog1[i].apply(d, arguments);
}
}
}
dog.prototype = {
bark : function () {
alert( (this.getName()) + " is barking." );
},
getName : function () {
return this.name;
}
}
dog.shepherd = function (parent) {
this.parent = parent;
this.something;
}
dog.shepherd.prototype = {
guard : function () {
alert( (this.parent.getName()) + " is barking." );
}
}
window.onload = function () {
var n = new dog();
n.guard();
}

Categories

Resources