I am building a javascript library where I have to create a log of classes and most of them have a lot of properties which have to make public for the user.
For example:
function Person(name,age){
}
Now I want to create the getter and setter for properties (name and age).
Nornall, I have to add these methods to Person.prototype:
Person.prototype.getName=function(){}
Person.prototype.setName=function(x){
//check if x is typeof String
}
Person.prototype.getAge=function(){}
Person.prototype.setAge=function(x){
//check if x is typeof Number
}
This will result in two many lines of repeated codes.
So I wonder if I can call a method like this:
makeThesePropertiesPublic(Person,{
name:"string",
age:"number"
});
Then I can call this:
var p=new Person("xx",1);
p.getName();
p.getAge();
.......
Is there a out-of-box method to implement this?
First of all you can't define the getter and setter functions on the prototype because they need to be able to access name and age which are only accessible inside the constructor. Hence you would need to define the getter and setter functions inside the constructor.
I would do this:
function Person(name, age) {
var private = {
name: name,
age: age
};
Object.defineProperties(this, {
name: getAccessor(private, "name", "String"),
age: getAccessor(private, "age", "Number")
});
}
function getAccessor(obj, key, type) {
return {
enumerable: true,
configurable: true,
get: function () {
return obj[key];
},
set: function (value) {
if (typeOf(value) === type)
obj[key] = value;
}
};
}
function typeOf(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
Now you can access create a Person and access their name and age properties as follows:
var person = new Person("Aadit M Shah", 20);
person.name = 0; // it won't set the name
person.age = "twenty"; // it won't set the age
alert(person.name);
alert(person.age);
See the demo: http://jsfiddle.net/aVM2J/
What about something like this?
function Person(n, a) {
var name = n;
var age = a;
var person = {};
person.getName = function () {
return name
}
person.getAge = function () {
return age;
}
return person;
}
var p = Person("jon",22);
console.log(p.getName());//jon
Related
var user = {
Name: "Some user",
Methods: {
ShowGreetings: function() {
// at this point i want to access variable "Name",
//i dont want to use user.Name
// **please suggest me how??**
},
GetUserName: function() { }
}
}
You can't.
There is no upwards relationship in JavaScript.
Take for example:
var foo = {
bar: [1,2,3]
}
var baz = {};
baz.bar = foo.bar;
The single array object now has two "parents".
What you could do is something like:
var User = function User(name) {
this.name = name;
};
User.prototype = {};
User.prototype.ShowGreetings = function () {
alert(this.name);
};
var user = new User('For Example');
user.ShowGreetings();
var user = {
Name: "Some user",
Methods: {
ShowGreetings: function() {
alert(this.Parent.Name); // "this" is the Methods object
},
GetUserName: function() { }
},
Init: function() {
this.Methods.Parent = this; // it allows the Methods object to know who its Parent is
delete this.Init; // if you don't need the Init method anymore after the you instanced the object you can remove it
return this; // it gives back the object itself to instance it
}
}.Init();
Crockford:
"A privileged method is able to access the private variables and
methods, and is itself accessible to the public methods and the
outside"
For example:
function user(name) {
var username = name;
this.showGreetings = function()
{
alert(username);
}
}
You can try another approach using a closure:
function userFn(name){
return {
Methods: {
ShowGreetings: function() {
alert(name);
}
}
}
}
var user = new userFn('some user');
user.Methods.ShowGreetings();
Old question but why can't you just do something like this :
var user = {
Name: "Some user",
Methods: {
ShowGreetings: function() {
// at this point i want to access variable "Name",
//i dont want to use user.Name
// **please suggest me how??**
var thisName = user.Name; //<<<<<<<<<
},
GetUserName: function() { }
}
}
Because you will only call user.Methods.ShowGreetings() after the user has been instantiated. So you will know about the variable 'user' when you want to use its name ?
As others have said, with a plain object it is not possible to lookup a parent from a nested child.
However, it is possible if you employ recursive ES6 Proxies as helpers.
I've written a library called ObservableSlim that, among other things, allows you to traverse up from a child object to the parent.
Here's a simple example (jsFiddle demo):
var test = {"hello":{"foo":{"bar":"world"}}};
var proxy = ObservableSlim.create(test, true, function() { return false });
function traverseUp(childObj) {
console.log(JSON.stringify(childObj.__getParent())); // returns test.hello: {"foo":{"bar":"world"}}
console.log(childObj.__getParent(2)); // attempts to traverse up two levels, returns undefined because test.hello does not have a parent object
};
traverseUp(proxy.hello.foo);
Very late to the party, but this works
var user = {
Name: "Some user",
Methods() {
return {
that: this,
ShowGreetings: function() {
console.log(this.that.Name)
},
GetUserName: function() { }
}
}
}
user.Methods().ShowGreetings() // Some user
David Dorward's right here. The easiest solution, tho, would be to access user.Name, since user is effectively a singleton.
ES6 Classes
One simple solution would be to create a Class with methods!
class User {
// Assign properties when an instance
// is created using the `new` keyword
constructor(name) {
this.name = name;
}
// Methods:
showGreetings() {
console.log(`Hello, ${this.name}!`);
}
getUserName() {
return this.name;
}
// Or rather, use Getters:
get username() {
return this.name;
}
}
// Create a new user:
const user = new User("Praveen");
// Use methods:
user.showGreetings(); // "Hello, Praveen!"
console.log(user.getUserName()); // "Praveen"
console.log(user.username); // "Praveen"
Why the above suggestion? Mostly because:
you cannot reference a parent Object from a child Object directly
const User = {
name: "Some user", // hardcoded stuff? Is this an intentional Singleton?
methods: { // <<< Child Object of User
sayName() {
// Sadly, `this` refers to `methods`, not to `user`:
console.log(this); // methods{}
console.log(User.name); // "Some user" // Get Singleton's name
// ... but that's not what you want.
}
}
};
User.methods.sayName();
// ^^^^^^^ Why would you want this `methods` anyways?!
and it makes no sense to hardcode Strings (like "Some user") inside an Object Singleton — which could actually be a reusable function Object.
If you want to associate a child Node to a parent Node — read this answer (Get value of parent Object).
How about this way?
user.Methods.ShowGreetings.call(user, args);
So you can access user.Name in ShowGreetings
var user = {
Name: "Some user",
Methods: {
ShowGreetings: function(arg) {
console.log(arg, this.Name);
},
GetUserName: function() { }
},
Init: function() {
this.Methods.ShowGreetings.call(this, 1);
}
};
user.Init(); // => 1 "Some user"
As a variant:
var user = (obj => {
Object.keys(obj.Methods).map(option => {
const currOpt = obj.Methods[option];
if (currOpt instanceof Function) {
obj.Methods[option] = currOpt.bind(obj);
};
});
return obj;
})({
Name: "Some user",
Methods: {
Greeting: function () { return this.Name },
GetUserName: function() { console.log(this) }
},
});
But I don't know why somebody can use this strange approach
I know I'm very late.
I wrote this simple method. Let's say you have:
{
subObj: {
x:'hello_world';
}
}
Then, if you want a reference to the bigger object from subObj, you can convert it to a function, and utilize this.
var tmpVal=reference_to_subObj; //keep value of subObj safe
reference_to_subObj=function(){return this;}//this returns the scope, here the parent
var parent=reference_to_subObj(); //call the function
reference_to_subObj=tmpVal; delete tmpVal; //set things back to normal
//Now you have variable 'parent'.
I used a Function() constructor because it let me create the function as a string, so I could pass a string as code.
function findParent(stringReference) {
Function(/*same as above, except filled in all reference_to_subObj with stringReference.*/
//stringReference is a stringified version of dot or bracket notation.
So I could call findParent('obj.subObj').
// Make user global
window.user = {
name: "Some user",
methods: {
showGreetings: function () {
window.alert("Hello " + this.getUserName());
},
getUserName: function () {
return this.getParent().name;
}
}
};
// Add some JavaScript magic
(function () {
var makeClass = function (className) {
createClass.call(this, className);
for (key in this[className]) {
if (typeof this[className][key] === "object") {
makeClass.call(this[className], key);
}
}
}
var createClass = function (className) {
// private
var _parent = this;
var _namespace = className;
// public
this[className] = this[className] || {};
this[className].getType = function () {
var o = this,
ret = "";
while (typeof o.getParent === "function") {
ret = o.getNamespace() + (ret.length === 0 ? "" : ".") + ret;
o = o.getParent();
}
return ret;
};
this[className].getParent = function () {
return _parent;
};
this[className].getNamespace = function () {
return _namespace;
}
};
makeClass.call(window, "user");
})();
user.methods.showGreetings();
I ran across this old post trying to remember how to solve the problem. Here is the solution I used. This is derived from Pro JavaScript Design Patterns by Harmes and Diaz (Apress 2008) on page 8. You need to declare a function and then create a new instance of it as shown below. Notice the Store method can access "this".
function Test() {
this.x = 1;
}
Test.prototype = {
Store: function (y) { this.x = y; },
}
var t1 = new Test();
var t2 = new Test();
t1.Store(3);
t2.Store(5);
console.log(t1);
console.log(t2);
Like #Quentin said, there is no upwards relationship in JS. However try this workaround;
foo = { bar: {parent: foo} };
console.log(foo);
console.log(foo.bar.parent);
which is also similar to;
function Foo(){
this.bar = {parent: this}
}
foo = new Foo();
console.log(foo);
console.log(foo.bar.parent);
I'm kinda new with js + ES6 + class; I have problem with creating function inside constructor.
#1. I need to add new Hobby, a person allowed to have plenty hobbies ;
#2. I don't know how to show all the data of students;
another questions are in the comments ,in case if you want to answer it too, if not i'm also fine.
so here's my code :
class Student {
constructor(name,hobbies){
this.name = name;
var hobby = new Set(); //do I set here or inside the function ??
//since the function addHobbies also need, then it's fine to be global right ?
this.hobbies = (hobbies) => { //function ES6 like this right ??
this.hobbies = hobby.add(hobbies);
return this.hobbies; //can I return hobby instead of this.hobbies ??
};
}
setName(newName){
this.name = newName;
}
addHobbies(newHobbies){
this.Hobbies = hobby.add(newHobbies); //it should be like this to add >> to set ?
}
getName(){
return this.name;
}
getHobbies(){
return this.hobbies;
}
}
and how to return all the data ?
let andy = new Student("andy","dance");
let vince = new Student("vince","codding");
so it will show all students-attribute by getCode() ?
do I set here or inside the function ??
That depends on what you need. Do you want each Student instead to have one set of hobbies, or do you want to create a new set every time the function is called?
this.hobbies = (hobbies) => { //function ES6 like this right ??
this.hobbies = hobby.add(hobbies);
That doesn't work at all. You're creating the property with a function value, but when the method is called you're overwriting the property with the return value of the add method.
To make it work, I'd recommend making the .hobbies set an instance property instead of a local variable.
class Student {
constructor(name, ...hobbies) {
this.name = name;
this.hobbies = new Set();
this.addHobbies(...hobbies);
}
getName() {
return this.name;
}
setName(newName) {
this.name = newName;
}
getHobbies() {
return this.hobbies;
}
addHobbies(...newHobbies) {
for (const newHobby of newHobbies)
this.hobbies.add(newHobby);
}
}
Alternatively, if you insist on using a local constructor variable, it would look like this:
class Student {
constructor(name, ...hobbies) {
this.name = name;
this.hobbies = new Set(...hobbies);
this.getHobbies = () => {
return this.hobbies;
};
this.addHobbies = (...newHobbies) => {
for (const newHobby of newHobbies)
this.hobbies.add(newHobby);
};
}
… // further methods (for name etc)
}
Try this:
class Student {
constructor(name, hobbies) {
this.name = name;
// Allow passing both an array of hobbies and a single hobby
this.hobbies = Array.isArray(hobbies) ? new Set(hobbies) : new Set([hobbies]);
}
setName(newName) {
this.name = newName;
}
addHobbies(newHobbies) {
if (Array.isArray(newHobbies)) {
newHobbies.forEach((hobby) => this.hobbies.add(hobby));
} else {
this.hobbies.add(newHobbies);
}
}
getName() {
return this.name;
}
getHobbies() {
return this.hobbies;
}
}
let andy = new Student("andy","dancing");
let vince = new Student("vince",["codding", "running"]);
andy.addHobbies("slipping");
vince.addHobbies(["running", "eating"]);
You are in the correct direction. I have rewritten your class to do what I think is more similar to what you are trying to achieve.
Play with the code at: https://jsbin.com/vejumo/edit?js,console
And here's the rewritten class:
class Student {
constructor(name, hobbies = []){
this.name = name;
// new Set() is used to work with objects. It does not work with well with strings
// Let's use an array to store the hobbies.
// if a hobby or an hobbies array is passed, store it, otherwise set an empty array.
this.hobbies = this.parseHobbies(hobbies);
}
// This function will normalize the hobbies to an Array
parseHobbies(hobbies) {
if (typeof hobbies === "string") {
// hobbies is a string, means it's a single hobby and not an array
return [hobbies];
}
// Assuming the hobbies is a an Array
return hobbies;
}
setName(newName) {
this.name = newName;
}
// this function will allow you to add a single hobby to the array
addHobbies(hobbies = []) {
// Same logic like in constract, this can accept a string or an array
// We use Array.concat and push to append to array
this.hobbies = this.hobbies.concat(this.parseHobbies(hobbies));
}
getName() {
return this.name;
}
getHobbies() {
return this.hobbies
}
// This will return all student attributes.
getAttributes() {
// Return a copy of all the attributes instead of returning references
return Object.assign({}, this);
}
}
let george = new Student("George", "Sports");
george.addHobbies(["Singing", "Fishing"]);
george.addHobbies("Dancing");
console.log(george.getAttributes());
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}!`;
});
};
So with the growth of new frameworks with JavaScript many have adopted ECMAScript 6 shim's or TypeScript, with many new features. My question is this:
How does one iterate over the methods/properties of an ES6 class?
e.g. (with objects)
var obj = {
prop: 'this is a property',
something: 256,
method: function() { console.log('you have invoked a method'); }
}
for (var key in obj) {
console.log(key);
}
// => 'prop'
// => 'something'
// => 'method'
(with classes)
class MyClass {
constructor() {
this.prop = 'prop';
this.something = 256;
}
method() {
console.log('you have invoked a method');
}
}
How do I list the methods MyClass has, and optionally its properties as well?
The constructor and any defined methods are non-enumerable properties of the class's prototype object.
You can therefore get an array of the names (without constructing an instance of the class) with:
Object.getOwnPropertyNames(MyClass.prototype)
You cannot obtain the properties without creating an instance, but having done so you can use the Object.keys function which returns only the enumerable properties of an object:
Object.keys(myInstance)
AFAIK there's no standard way to obtain both the non-enumerable properties from the prototype and the enumerable properties of the instance together.
Yes it is possible
I use an util function that can:
get method names of a not instanciated class
of instanciated class
that recursively gets all method of parent classes
Use it like:
class A {
fn1() { }
}
class B extends A {
fn2() { }
}
const instanciatedB = new B();
console.log(getClassMethodNames(B)) // [ 'fn2' ]
console.log(getClassMethodNames(instanciatedB)) // [ 'fn2', 'fn1' ]
Here is the util function code:
function getClassMethodNames(klass) {
const isGetter = (x, name) => (Object.getOwnPropertyDescriptor(x, name) || {}).get;
const isFunction = (x, name) => typeof x[name] === 'function';
const deepFunctions = x =>
x !== Object.prototype &&
Object.getOwnPropertyNames(x)
.filter(name => isGetter(x, name) || isFunction(x, name))
.concat(deepFunctions(Object.getPrototypeOf(x)) || []);
const distinctDeepFunctions = klass => Array.from(new Set(deepFunctions(klass)));
const allMethods = typeof klass.prototype === "undefined" ? distinctDeepFunctions(klass) : Object.getOwnPropertyNames(B.prototype);
return allMethods.filter(name => name !== 'constructor' && !name.startsWith('__'))
}
There is a way to find the names of the methods only. The following has been tested in nodeJS v10.9.0 with no special flags.
First we inject a new method into Object.
Object.methods = function(klass) {
const properties = Object.getOwnPropertyNames(klass.prototype)
properties.push(...Object.getOwnPropertySymbols(klass.prototype))
return properties.filter(name => {
const descriptor = Object.getOwnPropertyDescriptor(klass.prototype, name)
if (!descriptor) return false
return 'function' == typeof descriptor.value && name != 'constructor'
})
}
You can see above that it is necessary to specifically exclude the constructor as it is not strictly a method of the class.
Create some class containing a constructor, accessors and methods
class Test {
constructor(x, y) {
this.x = x
this.y = y
}
sum() { return x + y }
distanceFromOrigin() { return Math.sqrt(this.squareX + this.squareY) }
get squareX() { return this.x * this.x }
get squareY() { return this.y * this.y }
[Symbol.iterator]() {
return null // TODO
}
}
Let's see how this works
> console.log(Object.methods(Test))
Array(3) ["sum", "distanceFromOrigin", Symbol(Symbol.iterator)]
I've not tested, still I think that there are 2 ways to do it.
1st one is to return the 'this' enviroment and loop over it.
2nd one is the same as Javascript's object.
Example for 1st one:- (untested)
class MyClass {
constructor() {
this.prop = 'prop';
this.something = 256;
}
method() {
console.log('you have invoked a method');
}
get getthis()
{
return this;
}
}
for( var key in MyClass.getthis )
{
console.log(key);
}
this is the second method:-( untested )
class MyClass {
constructor() {
this.prop = 'prop';
this.something = 256;
}
method() {
console.log('you have invoked a method');
}
}
for( var key in MyClass )
{
console.log(key);
}
I have a basic library I created as follows:
(function () {
function Store() {
var store = [];
if (!(this instanceof Store)) {
return new Store();
}
this.add = function (name, price) {
store.push(new StoreItem(name, price));
return this;
};
}
function StoreItem(name, price) {
if (!(this instanceof StoreItem)) {
return new StoreItem();
}
this.Name = name || 'Default item';
this.Price = price || 0.0;
}
Store.prototype.toString = function () {
// build a formatted string here
};
StoreItem.prototype.toString = function () {
return this.Name + ' $' + this.Price;
};
window.shop = window.shop || {
Store: function () {
return new Store();
}
};
}());
The large majority of this works well! However, I do not want to expose my store array defined in the Store constructor as I do not want it modified in anyway outside this library's control.
But, on the contrary, I would like to override the Store's toString method to make use of the StoreItems in the store array so I can return a formatted string of all the StoreItems using its toString method.
E.g. if store was exposed, the toString method would look something like:
Store.prototype.toString = function () {
return this.store.join('\r\n');
};
// shop.Store().add('Bread', 2).add('Milk', 1.5).toString() result:
// Bread $2
// Milk $1.5
Is there anyway I can achieve this without exposing my store array publicly?
You can give each Store it's own .toString method, being privileged to access the local store variable through closure - just like you did with .add:
function Store() {
if (!(this instanceof Store)) {
return new Store();
}
var store = [];
this.add = function(name, price) {
store.push(new StoreItem(name, price));
return this;
};
this.toString = function () {
return store.join('\n');
};
}
Alternatively, you will have to define some kind of accessor method anyway, or your store would be useless if you only can add to it but never read it. Something will need to display the store items, will need to iterate them, will need to test them for availability… If you create a generic accessor, maybe in form of some iterator (an each method with a callback?), then a .prototype.toString method could use that as well.
It's possible to keep the prototype toString method whilst protecting store, by adding a public method that returns the stringified store, and calling it from Store.prototype.toString.
Below I added this.getStoreString to the constructor, and called it from the original toString.
(function () {
function Store() {
var store = [];
if (!(this instanceof Store)) {
return new Store();
}
this.add = function (name, price) {
store.push(new StoreItem(name, price));
return this;
};
// new method
this.getStoreString = function(){
return store.join('\r\n');
};
}
function StoreItem(name, price) {
if (!(this instanceof StoreItem)) {
return new StoreItem();
}
this.Name = name || 'Default item';
this.Price = price || 0.0;
}
Store.prototype.toString = function () {
// call the new method
return this.getStoreString();
};
StoreItem.prototype.toString = function () {
return this.Name + ' $' + this.Price;
};
window.shop = window.shop || {
Store: function () {
return new Store();
}
};
}());
Fiddle
#Bergi's answer seems simpler though, but this shows another option.