I'm trying to understand how to create inheritance. I think I have it (below), but the part I can't figure out is how to call the "baseclass" method (ie, doing a "super()"). How do I call the delegate's talk() method below?
(demo link: https://plnkr.co/edit/H76NFBiuWqgaZaUfR7H2?p=preview)
function Person(name) {
var api = {
name,
talk
}
return api;
function init(name) {
this.name = name;
}
function talk() {
return (`I am ${this.name}`)
}
}
function Student(name, major) {
var api = {
major: major,
talk
};
var o = $.extend({}, Person(name), api);
return o;
function changeMajor(newMajor) {
this.major = newMajor;
}
function talk() {
var str = ""
// var str = Person.prototype.talk.call(this)
str += ` and I study ${major}`;
return str;
}
}
var s = Student("Sue", "Economics")
console.log(s.talk())
You lose the reference to the "super" talk method from the Person object when using extend:
var o = $.extend({}, Person(name), api);
Although that parent object is created, it is not stored. Only the modified version of it (with api.talk overriding its talk method) is available in o.
So... you need to keep a reference to the original talk method:
var proto = Person(name);
var o = $.extend({}, proto, api);
And then reference it:
var str = proto.talk.call(this);
Note that since ES6 you can use the native Object.assign which has similar functionality as $.extend.
function Person(name) {
var api = {
name,
talk
};
return api;
function init(name) {
this.name = name;
}
function talk() {
return (`I am ${this.name}`)
}
}
function Student(name, major) {
var api = {
major: major,
talk
};
var proto = Person(name);
var o = Object.assign({}, proto, api);
return o;
function changeMajor(newMajor) {
this.major = newMajor;
}
function talk() {
var str = proto.talk.call(this);
str += ` and I study ${major}`;
return str;
}
}
var s = Student("Sue", "Economics");
console.log(s.talk());
You could reveal the init method in your api objects but mark it as I'm not part of the published API, I only happen to be public here so to say. Leveraging a naming convention you could do something like this:
function Person(name) {
var api = {
"__init__": function () {
// I'm part of the API but please treat me as private
},
talk: function () {
// Do sth.
}
};
return api;
}
function Student(name, major) {
var personApi = Person(name);
var api = {
"__init__": function () {
// I'm also part of the API but treat me as private
personApi.__init__.call(this, name)
},
talk: function () {
return personApi.talk.call(this);
}
};
return api;
}
But then you'd have to marshall around the this and you have to think about not accidentally overriding you __init__ so you customize your own $.extend() function... the rabbit hole goes deep :-). Things like this have been done by some frameworks in the past but I personally wouldn't recommend rolling your own object system - it's fun as a toy but in this day and age there are better options.
The old ES5 way to do this is via constructor functions:
// Base `class` is just a constructor function
function Person(name) {
this.name = name;
}
Person.prototype.talk = function () {
// Do sth.
};
function Student(name, major) {
Person.call(this, name); // emulating super(name)
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.changeMajor = function () {
// Do sth.
};
// later
var homer = new Student("Homer Simpson", "Donuts");
In modern browsers or with the help of a transpiler say babeljs or TypeScript you can do this - which is probably what you wanted in the first place:
class Person {
constructor(name) {
this.name = name;
}
talk() {
// Do sth.
}
}
class Student extends Person {
constructor(name, major) {
super(name); // proper super(name);
this.major = major; // Or anything you want to do
}
changeMajor() {
// Do sth.
super.talk();
}
}
// later
let homer = new Student("Homer Simpson", "Donuts");
Related
When using get in an object like this, get works:
var people = {
name: "Alex",
get sayHi() {
return `Hi, ${this.name}!`
}
};
var person = people;
document.write(person.sayHi);
But with a function I get an error. How to use Getters and Setters in a function like this?
function People2() {
this.name = "Mike";
get sayHi() {
return `Hi, ${this.name}!`;
}
};
var user = new People2();
document.write(user.sayHi);
You can use the actual get and set keywords only in classes (ES2015) and object literals.
ECMAScript 5
In ES5, your would typically use Object.defineProperty to implement what you're trying to achieve:
function People2() {
this.name = "Mike";
}
Object.defineProperty(People2.prototype, "sayHi", {
get: function() {
return "Hi, " + this.name + "!";
}
});
ECMAScript 2015
In ES2015, you could also use classes to achieve the desired behavior:
class People2 {
constructor() {
this.name = "Mike";
}
get sayHi() {
return `Hi, ${this.name}!`;
}
}
You can try this
<script>
function People2(name) {
this.name = name;
};
People2.prototype = {
get sayHi() {
return `Hi, ${this.name}!`;}
};
var user = new People2('Alex');
document.write(user.sayHi);
</script>
or this one...
<script>
function people(name) {
this.name = name;
};
Object.defineProperty(people.prototype, 'sayHi', {
get: function() { return `Hi, ${this.name}!`; }
});
var person = new people('Alex');
document.write(person.sayHi);
</script>
For the case you want to define a property like as name for a function with more control, we can use Object.defineProperty on function itself as following:
function people(name) {
//this.name = name; //this can be modified freely by caller code! we don't have any control
var _name = name; //use a private var to store input `name`
Object.defineProperty(this, 'name', {
get: function() { return _name; }, //we can also use `return name;` if we don't use `name` input param for other purposes in our code
writable: false, //if we need it to be read-only
//... other configs
});
};
var person = new people('Alex');
console.log(person.name); //writes Alex
For example, use this:
function People2() {
this.name = "Mike";
this.__defineGetter__("sayHi", function() {
return `Hi, ${this.name}!`;
});
};
I create a class in JavaScript with public and private properties - data and methods to operate on this data. Some data is private and should not be accessible via "."(dot) operator from class instance. Is there way to avoid method duplication for every class instance?
function MyClass() {
let privateVar;
let publicVar;
function publicFun() {
// do something
}
function privateFun(){
// do something else
}
this.v = publicVar;
this.f = publicFun;
}
let obj1 = new MyClass();
let obj2 = new MyClass(); // publicFun and privateFun methods duplication
ClassName.prototype approach require completely public API for all class data. So this doesn't work for me.
Here is my example if I understood you correctly:
Methods are defined only once, within the wrapper function (thus they are not declared on every instance)
You can create instances of objects they will all refer to the same methods, and can have exposed data.
Here is a fiddle example:
function wrapper() {
//Methods defined only once
function method() {
alert("this is method");
}
function methodWithParams(param, callback) {
var paramsVar = param;
function realMethodHere() {
alert("We passed a param: " + paramsVar);
paramsVar = "Changed"
callback(paramsVar);
alert("Now we cahnged the param's value to: " + paramsVar + ", rerun the method to verify");
}
return realMethodHere;
}
//Class constructor
function classConstructor() {
//Private
var privateData = "Private"
function privateFunction() {
alert("this is some private function, inaccesible");
}
//This callback was addedto allow yo uto change private data.
function privateDataChangerCallback(param) {
privateData = param;
}
//Public
this.publicData = "Public"
this.callMethod = method;
this.paramMethod = methodWithParams(privateData, privateDataChangerCallback);
}
return classConstructor;
}
var classDefinition = wrapper();
var classInstance = new classDefinition();
classInstance.callMethod(); //method without param
classInstance.paramMethod(); //method with exposed Private data
//rerunning the method to see what the value is:
classInstance.paramMethod(); //method with exposed Private data
You can try using TypeScript it's a javascript library that support OOP so you can write your code like in c# or java and the compiler will generate the real javascript for you.
If I understand right, you can add a parameter to your class definition and based on this parameter, you can choose to include additional properties to your return object.
Sample
function myClass(option) {
var myFunc1 = function() {}
var myFunc2 = function() {}
var myFunc3 = function() {}
var myFunc4 = function() {}
var myFunc5 = function() {}
var finalProps = {
myFunc1: myFunc1,
myFunc2: myFunc2,
}
switch (option) {
case "all":
finalProps["myFunc5"] = myFunc5;
case "more":
finalProps["myFunc3"] = myFunc3;
finalProps["myFunc4"] = myFunc4;
break;
}
return finalProps;
}
(function() {
var f1 = new myClass();
var f2 = new myClass("more");
var f3 = new myClass("all");
console.log(f1, f2, f3)
})()
You can create a stand-alone function in the constructor function:
var HelloWorld = (function () {
function anonymouse() {
return "MUHAHAHA! ALL MINE!!!";
}
function HelloWorld() {
this.greeting = "Hello World";
}
//public
HelloWorld.prototype.greet = function () {
console.log("Hello, " + this.greeting + " " + anonymouse());
};
return HelloWorld;
}());
var greeter = new HelloWorld();
greeter.greet();
console.log(greeter);
But this does have the side effect of duplicating said function on all instances of your class.
Alternatively, maybe create a namespace to hide it in, and reference your functions from there. That would eliminate the duplicate function issue:
var MySecretClasses;
(function (MySecretClasses) {
function anonymouse() {
return "MUHAHAHA! ALL MINE!!!";
}
var HelloWorld = (function () {
function HelloWorld() {
this.greeting = "Hello World";
}
//public
HelloWorld.prototype.greet = function () {
console.log("Hello, " + this.greeting + " " + anonymouse());
};
return HelloWorld;
}());
MySecretClasses.HelloWorld = HelloWorld;
})(MySecretClasses || (MySecretClasses = {}));
var greeter = new MySecretClasses.HelloWorld();
greeter.greet();
console.log(MySecretClasses);
console.log(greeter);
TYPESCRIPT
As Shlomi Haver points out, you could use TypeScript for this.
module MySecretClasses {
function anonymouse() {
return "MUHAHAHA! ALL MINE!!!";
}
export class HelloWorld {
greeting: string = "Hello World";
constructor() {
}
//public
public greet() {
console.log("Hello, " + this.greeting + anonymouse());
}
}
}
var greeter = new MySecretClasses.HelloWorld();
greeter.greet();
console.log(greeter);
Main File
var DogModule = require('Dog');
var dogsList = [];
function addNewDog(){
var newDog = new Dog();
dogsList.push(newDog);
}
???.on('bark', function(barkSound) {
console.log(barkSound);
});
Dog File
var EventEmitter = require('events').EventEmitter;
function Dog() {
EventEmitter.call(this);
this.name = "asda";
this.chipId = 1234;
this.emit('bark', "au-au");
}
Dog.prototype = {
getName: function () {
return this.name;
},
getChipId: function () {
return this.chipId;
}
}
Question 1 - How can i properly add EventEmitter.prototype to Dog object and save the current prototype and basically just get access to EventEmitter methods?
Dog.prototype = Object.create(EventEmitter.prototype);
Using util module and then util.inherits(Dog, EventEmitter);
The problem here is just how to not overwrite the existing methods...
Question 2 - Handle one object its no problem but for multiple how i can handle them individuality, knowing that they will be stored on that list?
Thank you.
To prevent rewriting the prototype, you can use Object.assign:
util.inherits(Dog, EventEmitter);
Dog.prototype = Object.assign({
getName: function () {
return this.name;
},
getChipId: function () {
return this.chipId;
}
}, Dog.prototype);
$(document).ready(function () {
var patient = (function (options) {
var age = options.age;
var name = options.name;
function getName() {
return this.name;
}
function setName(val) {
name = val;
}
function getAge() {
return this.age;
}
function setAge(val) {
age = val;
}
return {
getAge: getAge,
setAge: setAge,
getName: getName,
setName: setName
}
})();
});
I realize that I'm never passing any options in my example here.
If I try to do something like patient.setAge('100') and then console.log(patient.getAge()) I get an error saying cannot read property Age of undefined. The overarching theme that I'm trying to get at is within a module, how can I emulate consturctors to instantiate a new patient object while keeping all the OOP goodness of private variables and all that jazz.
I've seen some examples of constructors in a module pattern on here and I haven't understood them very well. Is it a good idea in general to have a constructor in a module? Is its main purpose similarity with class-based languages?
This is a constructor:
function Patient(options) {
options = options || {};
this.age = options.age;
this.name = options.name;
}
$(document).ready(function () {
var patient = new Patient();
});
You can put it inside a module if you want. What you shouldn’t do is provide getters and setters, especially ones that don’t do anything. If you’re exposing a variable through two properties to get and set it, it should just be one property.
Try this
function Patient (options) {
options = options || {};
var age = options.age;
var name = options.name;
function getName() {
return name;
}
function setName(val) {
name = val;
}
function getAge() {
return age;
}
function setAge(val) {
age = val;
}
return {
getAge: getAge,
setAge: setAge,
getName: getName,
setName: setName
}
}); // pass empty object
$(document).ready(function () {
var p1 = new Patient({});
var p2 = new Patient();
var p3 = new Patient({age:20});
var p4 = new Patient({name:"abcd"});
var p5 = new Patient({age:21, name:"abcd"});
});
I have this code here:
var Person = (function() {
var name;
var PersonConstructor = function(n) {
name = n;
};
PersonConstructor.prototype.getName = function() {
return name;
};
return PersonConstructor;
})();
var people = [];
var person1 = new Person("Foo");
var person2 = new Person("Bar");
alert(person1.getName()); // I want it to be Foo
people.push(person1);
people.push(person2);
I got the idea of emulating classes from here.. But of course, I neglected the fact that the private variable var name; is also a static variable. Since this is tripping my current efforts I would like to know if there is a way to keep the private behaviour in this example but avoid the static one?
Use this.
var Person = (function() {
var PersonConstructor = function(n) {
this.name = n;
};
PersonConstructor.prototype.getName = function() {
return this.name;
};
return PersonConstructor;
})();
Unfortunately, this won't preserve the private state.
It's just a scope issue.
var Person = (function(){
var PersonConstructor = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
return PersonConstructor;
})();
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
EDIT - Here is my original solution.
var Person = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
There is no "private" when working with prototypes.
It should be noted there is no value in private state, avoid it like the plague. Closures are ugly and expensive.
var o = {
name: value
}
However if you insist on being delusional and want private state badly then
You can store state in a closure
function nameHolder(name) {
return {
get name() {
return name
},
set name(n) {
name = n
}
}
}
Note this is highly inefficient and has little benefit.
Alternatively you can store state in a weakmap
function privates() {
var map = new WeakMap()
return function (key) {
var v = map.get(key)
if (!v) {
v = {}
map.set(key, v)
}
return v
}
}
var NameHolder = (function () {
var state = privates()
return {
constructor: function (name) {
state(this).name = name
},
getName: function () {
return state(this).name
}
}
}())
WeakMap browser support is non-existant so emulate it using pd.Name