create object function that generate properties to its caller object - javascript

I want to create a function inside an object. I need this function to generate setters and getters
for the properties of the caller object without generating getters or setters for the property of the function value.
I reached for something like this. But It gives me RangeError Maximum call stack size exceeded.
function Emp() {
return {
name: "Mohamed",
id: "5",
getSetGen: function() {
for (var i in this) {
if (typeof this[i] !== 'function') {
(function(j) {
Object.defineProperty(this, j, {
get: function() {
return this[j];
},
set: function(val) {
this[j] = val
}
})
})(i);
}
}
}
}
}
I want to apply getSetGen() to var user = { name:”Ali”,age:10} for example.
Is there any possible solution? Thanks in advance.
Edit:
This is the text that describes what I need ...
Create your own custom object that has getSetGen as
function value, this function should generate setters and getters
for the properties of the caller object
This object may have description property of string value if
needed
Let any other created object can use this function property to
generate getters and setters for his own properties
Avoid generating getters or setters for property of function
value

In trying to solve this I reworked some of your code, but I think this does basically what you're looking for, and you can tweak it to your preferences.
(I think the issue was that the new accessor property had the same name as the existing data property. See comments in the code for further explanation.)
const
baseObj = getBaseObj(),
user = { name: "Ali", age: 10 };
baseObj.addAccessorsForAllDataProps.call(user);
console.log("\nsetting age to 11...");
user.age = 11;
console.log("\nretrieving user age...");
console.log(user.age);
function getBaseObj(){
return {
name: "Mohamed",
id: 5,
addAccessorsForAllDataProps(){
for(let originalPropName in this){
if(!isFunction(this[originalPropName])){
// Renames the data property with an underscore
const dataPropName = `_${originalPropName}`
// Binds existing value to renamed data property
this[dataPropName] = this[originalPropName];
// Passes originalPropName to be used as accessorPropName
addAccessors(this, originalPropName, dataPropName);
}
}
// Accessor prop can't have same name as data prop
// (I think your stack overflow happened b/c getter got called infinitely)
function addAccessors(obj, accessorPropName, dataPropName){
Object.defineProperty(obj, accessorPropName, {
get: function(){
console.log("(getter invoked)"); // Just proving accessors get called
return obj[dataPropName];
},
set: function(val){
console.log("(setter invoked)");
obj[dataPropName] = val;
}
});
};
}
}
}
function isFunction(val) {
return val && {}.toString.call(val) === '[object Function]';
}

Related

How to pass by reference or emulate it

I have two IFFE:
var Helper = (function () {
return {
number: null,
init: function (num) {
number = num;
}
}
})();
var Helper2 = (function () {
return {
options: {
number: [],
},
init: function(num){
this.options.number = num;
},
getData: function () {
return this.options.number;
}
}
})();
Helper2.init(Helper.number);
console.log(Helper2.getData());
Helper.init(5);
console.log(Helper2.getData());
What I want is
Helper2.init(Helper.number);
console.log(Helper2.getData()); // null
Helper.init(5);
console.log(Helper2.getData()); // 5
what I get is
Helper2.init(Helper.number);
console.log(Helper2.getData()); // null
Helper.init(5);
console.log(Helper2.getData()); // null
What techniques can be done to have it pass by reference, if it can?
JSBIN: https://jsbin.com/gomakubeka/1/edit?js,console
Edit: Before tons of people start incorporating different ways to have Helper2 depend on Helper, the actual implementation of Helper is unknown and could have 100's of ways they implement the number, so Helper2 needs the memory address.
Edit 2: I suppose the path I was hoping to get some start on was knowing that arrays/objects do get passed by reference, how can I wrap this primitive type in such a way that I can use by reference
Passing by reference in JavaScript can only happen to objects.
The only thing you can pass by value in JavaScript are primitive data types.
If on your first object you changed the "number:null" to be nested within an options object like it is in your second object then you can pass a reference of that object to the other object. The trick is if your needing pass by reference to use objects and not primitive data types. Instead nest the primitive data types inside objects and use the objects.
I altered you code a little bit but I think this works for what you were trying to achieve.
var Helper = function (num) {
return {
options: {
number: num
},
update: function (options) {
this.options = options;
}
}
};
var Helper2 = function (num) {
return {
options: {
number: num,
},
update: function(options){
this.options = options;
},
getData: function () {
return this.options.number;
}
}
};
var tempHelp = new Helper();
var tempHelp2 = new Helper2();
tempHelp2.update(tempHelp.options);
tempHelp.options.number = 5;
console.log(tempHelp2.getData());
First of all why doesn't it work:
helper is a self activating function that returns an object. When init is called upon it sets an number to the Helper object.
Then in Helper2 you pass an integer (Helper.number) to init setting the object to null. So you're not passing the reference to Helper.number. Only the value set to it.
You need to pass the whole object to it and read it out.
An example:
var Helper = (function () {
return {
number: null,
init: function (num) {
this.number = num; //add this
}
}
})();
var Helper2 = (function () {
return {
options: {
number: [],
},
init: function(obj){
this.options = obj; //save a reference to the helper obj.
},
getData: function () {
if (this.options.number)
{
return this.options.number;
}
}
}
})();
Helper2.init(Helper); //store the helper object
console.log(Helper2.getData());
Helper.init(5);
console.log(Helper2.getData());
I don't think you're going to be able to get exactly what you want. However, in one of your comments you said:
Unfortunately interfaces aren't something in javascript
That isn't exactly true. Yes, there's no strong typing and users of your code are free to disregard your suggestions entirely if you say that a function needs a specific type of object.
But, you can still create an interface of sorts that you want users to extend from in order to play nice with your own code. For example, you can tell users that they must extend from the Valuable class with provides a mechanism to access a value computed property which will be a Reference instance that can encapsulate a primitive (solving the problem of not being able to pass primitive by reference).
Since this uses computed properties, this also has the benefit of leveraging the .value notation. The thing is that the .value will be a Reference instead of the actual value.
// Intermediary class that can be passed around and hold primitives
class Reference {
constructor(val) {
this.val = val;
}
}
// Interface that dictates "value"
class Valuable {
constructor() {
this._value = new Reference();
}
get value() {
return this._value;
}
set value(v) {
this._value.val = v;
}
}
// "Concrete" class that implements the Valuable interface
class ValuableHelper extends Valuable {
constructor() {
super();
}
}
// Class that will deal with a ValuableHelper
class Helper {
constructor(n) {
this.options = {
number: n
}
}
getData() {
return this.options.number;
}
setData(n) {
this.options.number = n;
}
}
// Create our instances
const vh = new ValuableHelper(),
hh = new Helper(vh.value);
// Do our stuff
console.log(hh.getData().val);
vh.value = 5;
console.log(hh.getData().val);
hh.setData(vh.value);
vh.value = 5;

Enforce object structure in function parameter

I am new to javascript. I have a function taking an object. But how do I make sure caller is following the structure I want in the object. As there is no concept of class in javascript, I can't create a model class and make the caller use it ?
function foo(myObject)
{
}
Whoever is calling should give me
{
Count,
[
{
FirstName,
LastName
},
{
FirstName,
LastName
},
]
}
Well you could simply check the type of object you have received as an argument, and then check if those values are actually there, like so:
function foo(myObject) {
if (typeof myObject !== 'object') {
// doesn't match
return;
}
if (typeof myObject.Count === 'undefined') {
// no count property
}
}
However, from your question, it seems you would like to make it more fix which kind of object should be sent as an argument, and this you could also do in javascript, by doing for eg:
function MyParamOptions() {
// define properties here
this.persons = [];
Object.defineProperty(this, 'Count', {
get: function() {
return this.Names.length;
},
set: function() {
// dummy readonly
}
});
}
Then you could instantiate an instance of this class by saying
var options = new MyParamOptions();
options.persons.push({ firstName: 'bla', lastName: 'bla' });
and change a check inside your foo function like
function foo(myObject) {
if (myObject instanceof MyParamOptions) {
// here myObject is MyParamOptions, so you can access the persons array, the Count property etc...
}
}
// and call foo with your MyParamOptions
foo(options);
However this will not throw any warnings at compile time, so people can call your function with any kind of parameter. If you are looking for errors at compile time, you might look into TypeScript or a similar technology that then transpiles your TypeScript code to javascript)

List All Prototype Properties of a Javascript Object

Is there any other way to look up for the prototype properties of an javascript object. Lets say I have like this.
function proton() {
this.property1 = undefined;
this.property2 = undefined;
};
proton.prototype = {
sample1 : function() {
return 'something';
},
sample2 : function() {
return 'something';
}
};
var my_object = new proton();
console.log(Object.keys(my_object));
returns ["property1", "property2"]
console.log(Object.getOwnPropertyNames(my_object));
returns ["property1", "property2"]
But what i want to print is the prototype properties of the object my_object.
['sample1', 'sample2']
In order for me to see the prototype properties of that object i need to console.log(object) and from developer tools i can look up for the properties of that object.
But since I am using third party libraries like phaser.js, react.js, create.js
so i don't know the list of the prototype properties of a created object from this libraries.
Is there any prototype function of Object to list down all the prototpye properties of a javascript object?
Not a prototype method, but you can use Object.getPrototypeOf to traverse the prototype chain and then get the own property names of each of those objects.
function logAllProperties(obj) {
if (obj == null) return; // recursive approach
console.log(Object.getOwnPropertyNames(obj));
logAllProperties(Object.getPrototypeOf(obj));
}
logAllProperties(my_object);
Using this, you can also write a function that returns you an array of all the property names:
function props(obj) {
var p = [];
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
var op = Object.getOwnPropertyNames(obj);
for (var i=0; i<op.length; i++)
if (p.indexOf(op[i]) == -1)
p.push(op[i]);
}
return p;
}
console.log(props(my_object)); // ["property1", "property2", "sample1", "sample2", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"
function prototypeProperties(obj) {
var result = [];
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
}
EDIT: This will grab all the properties that were defined on any ancestor. If you want a more granular control of what is defined where, Bergi's suggestion is good.
The quick and dirty one-liner solution would be:
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf({ prop1: 'val1' })))
If you want something more precise, go with the accepted answer!

Copy object with results of getters

I have an object that contains a getter.
myObject {
id: "MyId",
get title () { return myRepository.title; }
}
myRepository.title = "MyTitle";
I want to obtain an object like:
myResult = {
id: "MyId",
title: "MyTitle"
}
I don't want to copy the getter, so:
myResult.title; // Returns "MyTitle"
myRepository.title = "Another title";
myResult.title; // Should still return "MyTitle"
I've try:
$.extend(): But it doesn't iterate over getters. http://bugs.jquery.com/ticket/6145
Iterating properties as suggested here, but it doesn't iterate over getters.
As I'm using angular, using Angular.forEach, as suggested here. But I only get properties and not getters.
Any idea? Thx!
Update
I was setting the getter using Object.defineProperty as:
"title": { get: function () { return myRepository.title; }},
As can be read in the doc:
enumerable true if and only if this property shows up during
enumeration of the properties on the corresponding object. Defaults to
false.
Setting enumerable: true fix the problem.
"title": { get: function () { return myRepository.title; }, enumerable: true },
$.extend does exactly what you want. (Update: You've since said you want non-enumerable properties as well, so it doesn't do what you want; see the second part of this answer below, but I'll leave the first bit for others.) The bug isn't saying that the resulting object won't have a title property, it's saying that the resulting object's title property won't be a getter, which is perfect for what you said you wanted.
Example with correct getter syntax:
// The myRepository object
const myRepository = { title: "MyTitle" };
// The object with a getter
const myObject = {
id: "MyId",
get title() { return myRepository.title; }
};
// The copy with a plain property
const copy = $.extend({}, myObject);
// View the copy (although actually, the result would look
// the same either way)
console.log(JSON.stringify(copy));
// Prove that the copy's `title` really is just a plain property:
console.log("Before: copy.title = " + copy.title);
copy.title = "foo";
console.log("After: copy.title = " + copy.title);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Syntax fixes:
Added missing variable declarations, =, and ;
Removed duplicate property title
Corrected the getter declaration syntax
If you want to include non-enumerable properties, you'll need to use Object.getOwnPropertyNames because they won't show up in a for-in loop, Object.keys, or $.extend (whether or not they're "getter" or normal properties):
// The myRepository object
const myRepository = { title: "MyTitle" };
// The object with a getter
const myObject = {
id: "MyId",
};
Object.defineProperty(myObject, "title", {
enumerable: false, // it's the default, this is just for emphasis,
get: function () {
return myRepository.title;
},
});
console.log("$.extend won't visit non-enumerable properties, so we only get id here:");
console.log(JSON.stringify($.extend({}, myObject)));
// Copy it
const copy = {};
for (const name of Object.getOwnPropertyNames(myObject)) {
copy[name] = myObject[name];
}
// View the copy (although actually, the result would look
// the same either way)
console.log("Our copy operation with Object.getOwnPropertyNames does, though:");
console.log(JSON.stringify(copy));
// Prove that the copy's `title` really is just a plain property:
console.log("Before: copy.title = " + copy.title);
copy.title = "foo";
console.log("After: copy.title = " + copy.title);
.as-console-wrapper {
max-height: 100% !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
First of all, fix your syntax, though it probably is good in your actual code:
myObject = {
id: "MyId",
get title () { return myRepository.title; }
}
Now, to the answer. :)
You can just use a for..in loop to get all the properties, then save them as-is:
var newObj = {};
for (var i in myObject) {
newObj[i] = myObject[i];
}
No jQuery, Angular, any other plugins needed!
I had the same issue but in TypeScript and the method mentioned by T.J. Crowder didnt work.
What did work was the following:
TypeScript:
function copyObjectIncludingGettersResult(originalObj: any) {
//get the properties
let copyObj = Object.getOwnPropertyNames(originalObj).reduce(function (result: any, name: any) {
result[name] = (originalObj as any)[name];
return result;
}, {});
//get the getters values
let prototype = Object.getPrototypeOf(originalObj);
copyObj = Object.getOwnPropertyNames(prototype).reduce(function (result: any, name: any) {
//ignore functions which are not getters
let descriptor = Object.getOwnPropertyDescriptor(prototype, name);
if (descriptor?.writable == null) {
result[name] = (originalObj as any)[name];
}
return result;
}, copyObj);
return copyObj;
}
Javascript version:
function copyObjectIncludingGettersResult(originalObj) {
//get the properties
let copyObj = Object.getOwnPropertyNames(originalObj).reduce(function (result, name) {
result[name] = originalObj[name];
return result;
}, {});
//get the getters values
let prototype = Object.getPrototypeOf(originalObj);
copyObj = Object.getOwnPropertyNames(prototype).reduce(function (result,name) {
let descriptor = Object.getOwnPropertyDescriptor(prototype, name);
if (descriptor?.writable == null) {
result[name] = originalObj[name];
}
return result;
}, copyObj);
return copyObj;
}

Setters/Getters for a nested Object

I know how to set Custom Setters & Getters in Javascript using Object.defineProperty
My intention with the following code snippet is to be able to hit the setter function whenever a property nested value inside the globalProject object is modified.
var ClassA = function () {
this.globalProject = {
a: "DEFAULT VALUE",
b: null
};
this.LastSavedProject = {};
};
Object.defineProperty(ClassA.prototype, 'globalProject', {
get: function () {
console.log("OVERRIDE");
return this._flag;
},
set: function (val) {
console.log("EDITING A DEEP VALUE")
this._flag = val;
},
});
var obj = new ClassA();
obj.globalProject.a = "HELLO WORLD" //Should get "EDITING A DEEP VALUE" in the console.
I imagine what is happening is that the getter method is being called and returning a reference to an object I want to modify. Because of that the setter is never being called since I am modifying a reference to a nested value and not the property I have a setter on.
Can anyone help me sort out this issue? Here's a fiddle: http://jsfiddle.net/z7paqnza/
When you execute obj.globalProject.a = "HELLO WORLD", you simply see "OVERRIDE" in the console because you are getting the value of obj.globalProject and setting the value of its data member a.
You do not see "EDITING A DEEP VALUE" in the console because you never set globalProject to refer to a different object, you simply changed one of the underlying object's data members. If you executed something like obj.globalProject = null, however, you would see "EDITING A DEEP VALUE" printed to the console, for you would have changed what object obj.globalProject refers to. See this jsfiddle: http://jsfiddle.net/z7paqnza/1/
What #PaulDapolito said is exactly correct. We are not calling the setter of globalObject when deep object is set. I have updated the code to add setters for deep object and now it calls the inner object setters. Here is the jsfiddle: http://jsfiddle.net/sjLLbqLc/4/
For this particular question, we can do the insert operation inside the GlobalProject class setters.
I am late to the party, but I hope this helps someone who lands up here in search of it.
var ClassA = function() {
this.globalProject = new GlobalProject(); // this sets global object
// you can initilaize your default values here to the global object. But you need the new GlobalProject() which will create the object with setters and getters.
};
Object.defineProperty(ClassA.prototype, 'globalProject', {
get: function() {
return this._flag;
},
set: function(val) {
console.log("Setting global object");
this._flag = val;
},
});
var GlobalProject = function() {
Object.defineProperty(GlobalProject.prototype, "a", {
get: function() {
return this._a;
},
set: function(value) {
console.log("Seting deep object");
this._a = value;
}
});
Object.defineProperty(GlobalProject.prototype, "b", {
get: function() {
return this._b;
},
set: function(value) {
this._b = value;
}
});
};
var obj = new ClassA();
obj.globalProject.a = "HELLO WORLD";// this sets the inner object

Categories

Resources