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');
}
Related
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())
function userCreator(name,score){
const newUser = Object.create(userFunctions);
newUser.name = name;
newUser.score = score;
return newUser;
}
userFunctions = {
increment: function(){
this.score++;
}
};
userCreator.prototype.foo = function(){
console.log("foo");
};
const user1 = userCreator("Phil",5);
user1.foo();
I try to add a function to my constructure but when I add this function and call it with user1 it says user1.foo() is not a function.
It looks like you want the object prototype to inherit from the userFunctions object, in which case you should set
userCreator.prototype = Object.create(userFunctions);
outside of the constructor. You should also call new on the constructor, and don't return an object from it, in order for <functionName>.prototype to work correctly:
function userCreator(name,score){
this.name = name;
this.score = score;
}
userFunctions = {
increment: function(){
this.score++;
}
};
userCreator.prototype = Object.create(userFunctions);
userCreator.prototype.foo = function(){
console.log("foo");
};
const user1 = new userCreator("Phil",5);
user1.foo();
(technically, you could use return this, but it's superfluous)
The prototype you're assigning the object in userCreator isn't userCreator.prototype, it's userFunctions. So you'd add foo to that, not userCreator.prototype. Also, don't forget to declare userFunctions, at the moment your code is falling prey to what I call The Horror of Implicit Globals.
function userCreator(name,score){
const newUser = Object.create(userFunctions);
newUser.name = name;
newUser.score = score;
return newUser;
}
const userFunctions = { // *** Added const
increment: function(){
this.score++;
}
};
userFunctions.foo = function(){ // *** `userFunctions`, not `userCreator.prototype`
console.log("foo");
};
const user1 = userCreator("Phil",5);
user1.foo();
userCreator.prototype would be used automatically as the prototype of the new object if you were using new userCreator to create the object, but you're doing it manually with Object.create(userFunctions).
Or alternately, get rid of userFunctions and use userCreator.prototype throughout:
function userCreator(name,score){
const newUser = Object.create(userCreator.prototype);
newUser.name = name;
newUser.score = score;
return newUser;
}
userCreator.prototype.increment = function(){
this.score++;
};
userCreator.prototype.foo = function(){
console.log("foo");
};
const user1 = userCreator("Phil",5);
user1.foo();
Just for what it's worth, the version using new:
function UserCreator(name,score){
this.name = name;
this.score = score;
}
UserCreator.prototype.increment = function(){
this.score++;
};
UserCreator.prototype.foo = function(){
console.log("foo");
};
const user1 = new UserCreator("Phil",5);
user1.foo();
or, since you're already using ES2015+ features:
class UserCreator {
constructor(name,score){
this.name = name;
this.score = score;
}
increment() {
this.score++;
}
}
// If for some reason you wanted to add it separately
// from the `class` definition
UserCreator.prototype.foo = function(){
console.log("foo");
};
const user1 = new UserCreator("Phil",5);
user1.foo();
But doing it without new is fine, too, just add to the correct object.
Since you do not use the new syntax in calling userCreator (and it isn't a function that returns an instance of its prototype), you don't use userCreator as a (standard) constructor. Therefore the created objects do not have userCreator.prototype as proto, and so any mutation of userCreator.prototype has no effect on your created object.
You seem to want to have User objects with UserFunctions. In ES6 syntax you would achieve that like this:
class Scorer {
constructor() {
this.score = 0;
}
increment() {
this.score++;
}
}
class User extends Scorer {
constructor(name, score) {
super();
this.name = name;
this.score = score;
}
foo() {
console.log("foo");
}
}
const user1 = new User("Phil", 5);
user1.foo();
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);
I think I finally wrapped my head around understanding how methods, constructor functions, and objects work. Could someone please review my code, and let me know if I using the correct names and syntax? Thanks a ton!
function objectConstructor (arg1, arg2) {
this.property1 = arg1;
this.property2 = arg2;
this.methodName = functionName;
}
function functionName() {
console.log(this.property1 + ' ' + this.property2);
}
var object1 = new objectConstructor('value1','value2');
console.log(object1.property1);
console.log(object1.methodName());
Methods of Javascript classes should be defined as prototype:
var CustomObject = function (arg1, arg2) {
this.property1 = arg1;
this.property2 = arg2;
};
CustomObject.prototype.functionName = function() {
console.log(this.property1 + ' ' + this.property2);
};
var object1 = new CustomObject("value1","value2");
Everything else seems fine to me though.
Use prototype functions. Else your inner functions get copied with every instance.
function Person(firstName, lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = function()
{
return this.firstName+' '+this.lastName;
}
var person1 = new Person('foo', 'bar');
console.log(person1.getFullName());
There are many other patterns which for example prevent the pollution of the global scope or allow a more class like approach. (Object literal, Module Pattern, Self-Executing Anonymous Functions)
The same example with the module pattern:
var Person = (function()
{
var Person = function(firstName, lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.getFullName = function()
{
return this.firstName+' '+this.lastName;
}
return Person;
})();
var person1 = new Person('foo', 'bar');
console.log(person1.getFullName());
I've tried several ways but I couldn't do it.
On the next example I want the Soldier gets all properties of Person, and allowing to add more properties. How to do it correctly?
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.hi = function (message) {
console.log(message + "!!!");
};
var Soldier = new(Person); // it is not the way to do it
Soldier.prototype.good_hi = function (message) {
console.log("Sir! " + message + ", sir!");
};
You don't have a Soldier constructor. You need to make that first. Then you'd apply the Person constructor to new Soldier instances.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.hi = function (message) {
console.log(message + "!!!");
};
function Soldier(name, age) {
Person.apply(this, arguments);
}
Soldier.prototype = Object.create(Person.prototype); // is better
Soldier.prototype.constructor = Soldier;
Soldier.prototype.good_hi = function (message) {
console.log("Sir! " + message + ", sir!");
};
Then use it like this:
var s = new Soldier("Bob", 23);
s.good_hi("Hello");
DEMO: http://jsfiddle.net/3kGGA/