Updating a private variable inside an object using IIFE - javascript

An object 'person' has a private variable 'name'. This private variable cannot be updated directly using object reference.
Used IIFE for tackling this. IIFE will return 'name' variable, 'get' and 'update' methods for getting the name and updating it respectively.
const person = (function () {
let name;
var obj = {};
init();
function init() {
name = 'dravid';
}
function getName() {
return name;
}
function modifyName(newName) {
name = newName;
}
Object.defineProperties(obj, {
'name': {
value: name,
writable: false
},
'get': {
value: getName,
writable: false
},
'update': {
value: modifyName,
writable: false
}
});
return obj;
})();
Let's see below what this code is doing.
person.name // returns 'dravid'
person.name = 'dhoni'
person.name // still returns 'dravid'
Now, few more...
person.get() // returns 'dravid'
person.update('dhoni')
person.get() // returns 'dhoni'
Above is pretty much what is required, except for a confusion.
person.name // still returns 'dravid' instead of 'dhoni'
Why such behavior?
Here's the link! to the working code.

The values name and person.name are not the same or equal. When you execute Object.defineProperties and return obj you're setting the default value of person.name equal to name. But when you call person.update you are changing the variable name. You are NOT changing the object key person.name. If you want to change person.name you need to change your modifyName method.
function modifyName(newName) {
obj.name = newName;
}

Related

How to check and access a function from another object using JavaScript Proxy?

I am practicing JavaScript Proxies and want to access a method from another object but with an empty Proxy object:
const data = {
hello: {
log() {
return 'hello log'
},
},
hi: {
log() {
return 'hi log'
},
},
}
const blankObject = {} // I can just pass the data object but I want an empty one
const proxy = new Proxy(blankObject, {
get(target, key) {
const v = target[key]
if (typeof v === 'function') {
// if data.hello.log() or data.hi.log()
return function() {
return data[key]['WHAT_TO_DO']// access data.hello.log or data.hi.log() here?
}
}
return v
}
})
proxy.hello.log() // hello log;
Basically I'm trying to check if that method property exist in another object. I just want to tell the proxy to get the value from another object without passing it into the constructor.
I don't understand why you want to use a different object than the object you are proxying. It makes little sense to proxy an empty object instead.
Secondly, if you are going to access the hello property of the proxy object, then realise that this hello value -- as found in the data object -- is not proxied, so it is not in this proxy that you should check whether the property is a function. At this point the log property is not accessed yet. It's only about the hello access that the proxy is aware.
But you can divert the path to the data object when hello is accessed, by verifying it is indeed a property of the data object. So:
const data = {
hello: {
log() {
return 'hello log';
},
},
hi: {
log() {
return 'hi log'
},
},
}
const blankObject = {};
const proxy = new Proxy(blankObject, {
get(target, key) {
if (key in data) return data[key]; // <---
return target[key]; // default
}
});
console.log(proxy.hello.log()); // hello log;
still trying to figure out what in the world you are trying to accomplish here. you're trying to add a method from one object, to another object?
const blankObject = { hello: data.hello };
though this feels a bit hacky. would probably be best to just pass the whole data object into the constructor, so that the blank object has a reference to it, and can use its methods whenever it needs to.
Doesn't matters, using a test, no needs to over-engineer!
let data = {
hello: {
log() {
return 'hello log'
},
},
hi: {
log() {
return 'hi log'
},
},
}
let blankObject = {} // I can just pass the data object but I want an empty one
// We test if the object has a given property and return accordingly
console.log(
(blankObject["hello"]) ? blankObject.hello.log() : data.hello.log()
)

How to use an object as a function parameter in JS?

I created a function that should return an object with user data. I want the function to accept an object as a parameter and I'd like this object to be defined inside the function with all the values pre-defined as default parameters in case they are not passed at a function call. The example code has only 2 values but I will need to pass over 15. How can I achieve such a solution?
const userCreator = (name, age) => {
if (name === undefined) {
name = 'John'
}
if (age === undefined) {
age = 25
}
return {
name:name,
age:age
}
};
...and I'd like this object to be defined inside the function with all the values pre-defined as default parameters in case they are not passed at a function call.
I think you're saying:
You want the function to accept an object
You want all the properties of that object to be optional with defaults specified by the function
To do that, you can use destructuring defaults. But see also below, because in this specific case, where you want to return an object, you may want to use a different approach.
But let's start with just basic destructuring without defaults, then add them:
const userCreator = ({ name, age }) => {
// ^−−−−−−−−−−−^−−−−−−−−−−−−−−−− destructuring
// ...stuff with `name` and `age` here...
return {
name,
age,
};
};
To add defaults for those properties, you add them within the destructuring pattern (the {} where the parameter name would otherwise be):
const userCreator = ({ name = "John", age = 25, }) => {
// ^^^^^^^^^−−−−−^^^^^−−−− defaults for the properties
return {
name,
age,
};
};
If there are lots, probably best to break up into lines:
const userCreator = ({
name = "John",
age = 25,
}) => {
return {
name,
age,
};
};
That version still expects an object to be provided, though. If you want to allow userCreator() (no parameter passed at all), you need to add a default parameter value for the object parameter:
const userCreator = ({
name = "John",
age = 25,
} = {}) => {
//^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− default for the parameter
return {
name,
age,
};
};
That uses {} as the default value if no parameter is provided at all, "John" as the default if name isn't provided, and 25 as the default if age isn't provided. And since there is no name or age on the default {}, they get defaulted when you do userCreator().
The alternative approach:
Since you want to return an object, you might just accept the object parameter directly, then use property spread or Object.assign to fill in defaults, like this:
const userDefaults = {
name: "John",
age: 25,
};
const userCreator = (template) => {
const result = { // The result object we'll return
...userDefaults, // Fill in defaults
...template // Overwrite with any properties from the caller
};
// Or with `Object.assign` in older environments without property spread:
//const result = Object.assign(
// {}, // The result object we'll return
// userDefaults, // Fill in defaults
// template // Overwrite with any properties from the caller
//);
return result;
};
Property spread and Object.assign both ignore null or undefined, so if no object is passed at all, template is undefined, and
You should define default values directly in the function parameters:
const userCreator = (name = 'John', age = 25) => {
return {
name: name,
age: age
}
};

Javascript object value wont change es6

I have cloned a javascript object using lodash.cloneDeep().
I want to change the value of one of the object properties and just print out for testing purpose. But "this" does not seem to change.
const object = {
id: 'old value',
toString: () => {
console.log(this.id);
},
getThis: () => {
return this;
}
}
const clonedObj = _.cloneDeep(object);
clonedObj.id = 'new value'
console.log(clonedObj.id) // returns "new value"
clonedObj.toString() // returns "old value"
console.log(cloneObj === object) // returns false
console.log(cloneObj.getThis === object) // returns true
Can someone help me understand this.

Is it possible to declare non enumerable property on javascript constructor?

I need to know if is possible to make instances of a javascript class that has non-enumerable attributes, for example
// Constructor
function MyClass(a) {
Object.defineProperty(this, '_number', {
enumerable: false
});
this.number = a; // Call setter for '_number'
}
// Getters and setters for '_number'
Object.defineProperty(MyClass.prototype, 'number', {
get: function() {
return this._number;
},
set: function(n) {
// Validate 'n' here
this._number = n;
}
});
What I wanted this code to do is to define the property _number on any instance of MyClass, making it not enumerable so that instance private variables such as _number wouldn't be listed in, suppose, a for in structure. You would use the number getter/setter to alter the private variable _number.
I have tried declaring the _number and number properties both inside the constructor and on MyClass.prototype with no success...
Any help would be appreciated!
Your approach is fine, you just need to watch out for the other attribute values which default to false:
function MyClass(a) {
Object.defineProperty(this, '_number', {
value: 0, // better than `undefined`
writable: true, // important!
enumerable: false, // could be omitted
configurable: true // nice to have
});
this.number = a; // Call setter for '_number'
}
// Getters and setters for '_number'
Object.defineProperty(MyClass.prototype, 'number', {
get: function() {
return this._number;
},
set: function(n) {
// Validate 'n' here
this._number = n;
},
enumerable: true, // to get `.number` enumerated in the loop
configurable: true // nice to have
});

How to inherit and chain String without polluting String.prototype?

What I want to be able to do is something like this:
var where = new Where();
where('a'); // returns a string 'WHERE a' that I can chain against
where('a').andWhere('b'); // reuturns 'WHERE a AND b' that is also chainable
where('a').andWhere('b').orWhere('c'); // 'WHERE a AND b OR c', and so on ...
The where methods should return what is for all intents and purposes a string, with all string like methods, but with the two custom andWhere and orWhere methods.
When I tried inheriting from Sting.prototype, my where methods returned an object, not a string. Of course, if I returned a string directly from the methods, they didn't have the andWhere and orWhere methods, so chaining broke.
The code below does what I want, but it does it by polluting the String.prototype. Is there a way to get the same behavior, but encapsulated in a custom object?
Object.defineProperty(String.prototype, "andWhere", {
value: function _andWhere(clause) {
return [this, 'AND', clause].join(' ');
},
configurable: true,
enumerable: false,
writeable: true
});
Object.defineProperty(String.prototype, "orWhere", {
value: function _orWhere(clause) {
return [this, 'OR', clause].join(' ');
},
configurable: true,
enumerable: false,
writeable: true
});
function where(clause){
return ['WHERE', clause].join(' ');
}
where('a').andWhere('b').orWhere('c');
// => 'WHERE a AND b OR c'
Edit
I still want to have access to all the string methods off the object directly. In other words the returned object acts just like a string, but with a couple more methods. For example:
var whereStr = where('a').andWhere('b').orWhere('c');
whereStr.length; // => 18
whereStr.concat(' and so on'); // => 'WHERE a AND b OR c and so on'
If it makes any difference, this is primarily for Node, but ideally would work for any recent (ES5) javascript implementation. Again, this works perfectly if I'm bad and use String.prototype, I'm hoping there's a way to do a drop in replacement.
UPDATED Added in an example of creating the length property as a "getter".
function Where(conditional) {
var thisObj = this;
//Setup the length property's "getter"
this.__defineGetter__( "length", function() {
return thisObj.clause.length;
});
this.start( conditional );
}
Where.prototype = {
AND_STR: " AND ",
OR_STR: " OR ",
add: function(conditional, prefix) {
this.clause += prefix + conditional;
},
and: function(conditional) {
this.add( conditional, this.AND_STR );
return this;
},
or: function(conditional) {
this.add( conditional, this.OR_STR );
return this;
},
start: function(conditional) {
this.clause = "WHERE " + conditional;
},
toString: function() {
return this.clause;
}
}
//Use it like this (this shows the length of the where statement):
alert( new Where( "a" ).and( "b" ).or( "c" ).length );

Categories