Make object method a variable in vanilla JS - javascript

Say, I have a constructor called Animal, that has two prototype methods, foo and bar.
I create a new object:
const animal = new Animal()
I could call:
animal.foo()
or
animal.bar()
Can I replace the two methods with another method (baz)?
Like:
animal.baz()
So depending on the context, I want to assign either foo or bar to baz.

The object-oriented way to do this would be to create two subclasses, one for each context. In each one, you would define the baz method to simply pass its arguments on to this.foo or this.bar, respectively.
You could also dynamically define baz on objects using animal.baz = Animal.<foo|bar>. However, that strategy would be less maintainable (you would have to define that every time you instantiated an object), and less performant (interpreters are optimized for objects with unchanging properties).

Without knowing what form the context is in, a simple way you could do this would be to pass the context (in whatever form) in the constructor, store it in the object, and when baz is called, check the stored context and act accordingly. This is also pretty flexible:
// ES6
class Animal {
constructor(context) {
this.context = context;
}
foo() {
...
}
bar() {
...
}
baz() {
if(context == "some string id maybe?") {
return this.foo();
} else if(context == 13523) { // some integer id
return this.foo();
} else if(context === someContextObject) { // some context object
return this.bar();
} else {
return this.foo();
}
}
}
//Vanilla
const Animal = function(context) {
this.context = context;
}
Animal.prototype.foo = function() {
...
}
Animal.prototype.bar = function() {
...
}
Animal.prototype.baz = function() {
if(context == "some string id maybe?") {
return this.foo();
} else if(context == 13523) { // some integer id
return this.foo();
} else if(context === someContextObject) { // some context object
return this.bar();
} else {
return this.foo();
}
}

Something like this? :D
class Animal {
constructor(isAgressive) {
this.seesMe = isAgressive ? this.attack : this.flee;
}
attack = function () {
console.log('attack!');
}
flee() {
console.log('cya!');
}
}
const zebra = new Animal(false);
const lion = new Animal(true);
zebra.seesMe();
lion.seesMe();

Related

Equivalence of object constructors

Often, when I create an object in Javascscript, I will do something like this:
function getObj(property) {
return {
property,
displayProp: function() {
console.log(this.property);
}
}
}
let obj = getObj("Hello World!);
I know that the usual way to create a constructor would be as such:
function getObj(property) {
this.property = property;
this.displayProp = function() {
console.log(this.property);
}
}
let obj = new getObj("Hello World!);
Now, my question is, are these two methods equivalent. As far as I can tell, they are the same, and you can also use the 'new' keyword when creating an object using the first case. For me, the first case makes more sense, but it doesn't matter too much to me. Thanks.
The first example is a function that returns a litteral object (which constructor will remain simply "Object").
The second one creates a new constructor.
function getObj(property) {
return {
property,
displayProp: function() {
console.log(this.property);
}
}
}
// this will be useless because the prototype of getObj is not related to the returned object
getObj.prototype.test_prop = "Test prop";
let obj = getObj("Hello World!");
console.log(obj.constructor.name) // Object
console.log(obj.test_prop) // undefined
function Obj(property) {
this.property = property;
this.displayProp = function() {
console.log(this.property);
}
}
Obj.prototype.test_prop = "Test prop";
obj = new Obj("Hello World!");
console.log(obj.constructor.name) // Obj
console.log(obj.test_prop) // Test prop

Force the use of setters instead of straight assignments in a JS ES6 Class

I have a CameraBuilder class that looks like this:
class CameraBuilder {
constructor() {
if (arguments.length) {
throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
}
this.camera = {};
}
withFarmLabel(farmLabel) {
this.camera.farm_label = farmLabel;
return this;
}
// more methods here
build() {
const missingProps = [];
if (!this.camera.farm_label) {
missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
}
// more validations like the one above here
if (missingProps.length) {
const errorMsg = missingProps.join('');
throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
}
return this.camera;
}
}
Since most of my validations are on the build() method and there are some business logic on some of these methods associated with how the user is building an instance of CameraBuilder, I wouldn't want anyone assigning cameraBuilderObj.camera directly. Is there any way I can enforce the use of the Class methods in order to assign properties to the Camera object?
You could make the camera property private by putting # in front of it, ensuring that only CameraBuilder's internals can reference it:
class CameraBuilder {
#camera = {};
constructor() {
if (arguments.length) {
throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
}
}
withFarmLabel(farmLabel) {
this.#camera.farm_label = farmLabel;
return this;
}
// more methods here
build() {
const missingProps = [];
if (!this.#camera.farm_label) {
missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
}
// more validations like the one above here
if (missingProps.length) {
const errorMsg = missingProps.join('');
throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
}
return this.#camera;
}
}
const c = new CameraBuilder();
c.withFarmLabel('label');
console.log(c.camera);
console.log(c.build().farm_label);
CertainPerformance's answer probably makes more sense--don't expose it in the first place--but if for some reason you didn't want to go that route (or if you're in an environment where private fields aren't supported) you could define setters on it, so that direct assignments go through your function.
class Foo {
constructor () {
this._bar = 'baz';
}
set bar (value) {
this._bar = value;
console.log('do whatever you want to do here.');
}
}
const f = new Foo();
f.bar = 'hey'; // direct assignment invokes the setter

Javascript Class returns private variable and not getter

I would expect an instance of the following class to return both the "private" variable AND it's getter, but the getter variable is not returned at all. Why?
class Foo {
_myPrivateVar = null;
get myPublicVar() {
return this._myPrivateVar;
}
set myPublicVar(v) {
if (v > 4) {
this._myPrivateVar = v;
}
}
}
const f = new Foo();
console.log(f) // Foo { _myPrivateVar: 5 }
I expected { _myPrivateVar: 5, myPublicVar: 5 }. I understand I can access f.myPublicVar directly, but the problem is when return the instance from an Express server, for example, the resulting JSON object is devoid of the getter property myPublicVar. Why?
The getter and setter is on the prototype, whereas _myPrivateVar is put on the instance itself.
When an object is serialized with JSON.stringify, such as for transfer over the network, only enumerable own properties will be included in the resulting JSON:
const obj = Object.create({ foo: 'foo' });
obj.bar = 'bar';
console.log(JSON.stringify(obj));
Another issue is that class instances usually can't be meaningfully stringified - when parsed on the other side, they'll be interpreted as plain objects, arrays, and values.
If you want to do something like this, you'll have to come up with a way to serialize the instance such that it can be turned into a proper instance on the other side. For a very basic example, if you put all instances into a foos array when sending, you can create a new object with an internal prototype ofFoo.prototype on the other side for every item in the array:
// Sending
class Foo {
_myPrivateVar = null;
get myPublicVar() {
return this._myPrivateVar;
}
set myPublicVar(v) {
if (v > 4) {
this._myPrivateVar = v;
}
}
}
const f1 = new Foo();
f1.myPublicVar = 5;
const f2 = new Foo();
f2.myPublicVar = 1;
console.log(JSON.stringify({ foos: [f1, f2] }));
// Receiving
class Foo {
_myPrivateVar = null;
get myPublicVar() {
return this._myPrivateVar;
}
set myPublicVar(v) {
if (v > 4) {
this._myPrivateVar = v;
}
}
}
const json = `{"foos":[{"_myPrivateVar":5},{"_myPrivateVar":null}]}`;
const { foos } = JSON.parse(json);
const properFoos = foos.map(({ _myPrivateVar }) => {
const foo = Object.create(Foo.prototype);
foo._myPrivateVar = _myPrivateVar;
return foo;
});
console.log(properFoos[0].myPublicVar);
console.log(properFoos[1].myPublicVar);
JavaScript.info
The property name is not placed into User.prototype. Instead, it is created by new before calling the constructor, it’s a property of the object itself.
class User {
name = "Anonymous";
sayHi() {
console.log(`Hello, ${this.name}!`);
}
}
new User().sayHi();
console.log('sayHi:',User.prototype.sayHi); // placed in User.prototype
console.log('name:',User.prototype.name); // undefined, not placed in User.prototype

Can I navigate upwards through a javascript object in the same way I can through the DOM? [duplicate]

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

using inheritance inside a namespace [duplicate]

How do I inherit/extend classes that are using the Revealing Prototype pattern?
And is there a way to make the private variables and functions protected?
Example base object:
myNameSpace.Person = function() {
this.name= "";
this.id = 0;
};
myNameSpace.Person.prototype = function(){
var foo = function(){
//sample private function
};
var loadFromJSON = function (p_jsonObject) {
...
};
var toJSON = function () {
...
};
var clone = function (p_other) {
...
};
return {
loadFromJSON : loadFromJSON,
toJSON: toJSON,
clone: clone
};
}();
There are no protected variables/properties in JavaScript. Though, you can reuse "private" variables when you declare the inheriting classes in the same scope, which seems possible in your case when the private variables are only "hidden utilities" of your prototype.
MyNamespace.Person = function Person(params) {
// private variables and functions, individual for each Person instance
var anything, id;
function execute_something() {}
// public properties:
this.name = "";
this.getId = function getId(){
// called a "privileged function", because it has access to private variables
}
}
MyNamespace.American = function(params) {
MyNamespace.Person.call(this, params); // inherit name and getId()
}
(function() { // new scope for
// hidden utility functions and other private things
function foo() { }
function helpJSON() { }
function fromJSON() { }
var bar;
(function(personProto) { // new scope for prototype module (not explicitly needed)
// "private" /static/ variables (and functions, if you want them private)
var personCount = 0;
personProto.clone = function clone() {
return this.constructor(myself); // or something
};
personProto.toJSON = function toJSON() {
// use of helpJSON()
};
personProto.fromJSON = fromJSON; // direct use
})(MyNamespace.Person.prototype);
(function(amiProto) {
// just the same as above, if needed
amiProto.special = function() {
// use foo() and co
};
})( MyNamespace.American.prototype = Object.create(MyNamespace.Person.prototype) );
})();
This is the JavaScript way of inheritance, which means American's prototype inherits the clone(), toJSON() and fromJSON() functions automagically from the Person's prototype. Of course overwritable. And the feature is
new MyNamespace.American() instanceof MyNamespace.Person; // true
Of course, if you don't need that, and want use the more module-like way, you could reuse the utility functions, i.e. just copy them:
(function() {
// hidden utility functions and other private things
var bar;
var personCount;
function foo() { }
function helpJSON() { }
function fromJSON() { }
function clone() {
return this.constructor(myself); // or something
}
function toJSON() { }
(function(personProto) { // new scope, not really needed
// private variables are useless in here
personProto.clone = clone;
personProto.toJSON = toJSON;
personProto.fromJSON = fromJSON;
})(MyNamespace.Person.prototype);
(function(amiProto) { // new scope, not really needed
// copied from personProto
amiProto.clone = clone;
amiProto.toJSON = toJSON;
amiProto.fromJSON = fromJSON;
// and now the differences
amiProto.special = function() {
// use foo() and co
};
})(MyNamespace.American.prototype);
})();

Categories