Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding custom properties to a function

Searching for appropriate answer proved difficult because of the existence of many other problems related to my keywords, so I'll ask this here.

As we know, functions in javascript are objects and they have their own properties and methods (more properly, function istances, inherited from Function.prototype).

I was considering adding custom properties for one function (method), let's skip the "why?" part and go straight to the code:

var something = {     myMethod: function () {         if (something.myMethod.someProperty === undefined) {             something.myMethod.someProperty = "test";         }         console.log(something.myMethod);     } } 

When inspected with Firebug's DOM explorer, the property is defined as expected. However, as I don't consider myself a javascript expert, I have the following questions:

  1. Can this method be considered "proper" and standards compliant? It works in Firefox but there are many things working as expected in web browsers and aren't by any means standards.
  2. Is this kind of altering objects by adding new properties to them a good practice?
like image 813
Przemek Avatar asked Dec 21 '11 10:12

Przemek


People also ask

How do you add properties to a function?

To assign properties to a function declare the function and use dot notation to assign a property to it, e.g. myFunction. myProperty = myValue . TypeScript allows us to define properties on functions simply by assigning the properties in the same scope.

Can we add properties to function in JavaScript?

Read this article: packtpub.com/article/using-prototype-property-in-javascript You cannot add a property x to a function fn like so: fn. x = 'something'. You need to use prototype.

How can we add the custom property method to all instance of any object?

Using the prototype object to add custom methods to objects The prototype object can also help you quickly add a custom method to an object that is reflected on all instances of it. To do so, simply create the object method as usual, but when attaching it to the object (you guessed it), use "prototype" beforehand.

How do you add a property to an object?

One way is to add a property using the dot notation: obj. foo = 1; We added the foo property to the obj object above with value 1.


1 Answers

First of all, it's important to realise that standard function properties (arguments, name, caller & length) cannot be overwritten. So, forget about adding a property with that name.

Adding your own custom properties to a function can be done in different ways that should work in every browser.


Adding your own custom properties to a function

Way 1 : adding properties while running the function :

var doSomething = function() {     doSomething.name = 'Tom';     doSomething.name2 = 'John';     return 'Beep'; };  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name :  doSomething.name2 : undefined doSomething() : Beep doSomething.name :  doSomething.name2 : John  

Way 1 (alternate syntax) :

function doSomething() {     doSomething.name = 'Tom';     doSomething.name2 = 'John';     return 'Beep'; };  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name : doSomething doSomething.name2 : undefined doSomething() : Beep doSomething.name : doSomething doSomething.name2 : John  

Way 1 (second alternate syntax) :

var doSomething = function f() {     f.name = 'Tom';     f.name2 = 'John';     return 'Beep'; };  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name : f doSomething.name2 : undefined doSomething() : Beep doSomething.name : f doSomething.name2 : John  

A problem with this strategy is that you need to run your function at least once to assign the properties. For many functions, that's obviously not what you want. So let's consider the other options.


Way 2 : adding properties after defining the function :

function doSomething() {     return 'Beep'; };      doSomething.name = 'Tom'; doSomething.name2 = 'John';  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name : doSomething doSomething.name2 : John doSomething() : Beep doSomething.name : doSomething doSomething.name2 : John  

Now, you don't need to run your function first before you're able to access your properties. However, a disadvantage is that your properties feel disconnected from your function.


Way 3 : wrap your function in anonymous function :

var doSomething = (function(args) {     var f = function() {         return 'Beep';     };     for (i in args) {         f[i] = args[i];     }     return f; }({     'name': 'Tom',     'name2': 'John' }));  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name :  doSomething.name2 : John doSomething() : Beep doSomething.name :  doSomething.name2 : John  

Wrapping your function in an anonymous function, you can collect your attributes into an object and use a loop to add those attributes one-by-one within the anonymous function. That way, your attributes feel more connected to your function. This technique is also very useful for when your attributes need to be copied from an existing object. A disadvantage, however, is that you can only add multiple attributes at the same time when you define your function. Also, it doesn't exactly result in DRY code if adding properties to a function is something you want to do often.


Way 4 : add an 'extend' function to your function, that adds the properties of an object to itself one by one :

var doSomething = function() {     return 'Beep'; };      doSomething.extend = function(args) {     for (i in args) {         this[i] = args[i];     }     return this; }  doSomething.extend({     'name': 'Tom',     'name2': 'John' });  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name :  doSomething.name2 : John doSomething() : Beep doSomething.name :  doSomething.name2 : John  

This way, you can extend multiple properties and/or copy properties from another project at any time. Again, however, your code isn't DRY if this is something you do more often.


Way 5 : Make a generic 'extend' function :

var extend = function(obj, args) {     if (Array.isArray(args) || (args !== null && typeof args === 'object')) {         for (i in args) {             obj[i] = args[i];         }     }     return obj; }      var doSomething = extend(     function() {         return 'Beep';     }, {         'name': 'Tom',         'name2': 'John'     } );  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name :  doSomething.name2 : John doSomething() : Beep doSomething.name :  doSomething.name2 : John  

A genetic extend function allows for a more DRY approach, allowing you to add the object or any project to any other object.


Way 6 : Create an extendableFunction object and use it to attach an extend function to a function :

var extendableFunction = (function() {     var extend = function(args) {         if (Array.isArray(args) || (args !== null && typeof args === 'object')) {             for (i in args) {                 this[i] = args[i];             }         }         return this;     };     var ef = function(v, obj) {         v.extend = extend;         return v.extend(obj);     };      ef.create = function(v, args) {         return new this(v, args);     };     return ef; })();  var doSomething = extendableFunction.create(     function() {         return 'Beep';     }, {         'name': 'Tom',         'name2': 'John'     } );  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name :  doSomething.name2 : John doSomething() : Beep doSomething.name :  doSomething.name2 : John  

Rather than using a generic 'extend' function, this technique allows you to generate functions that have an 'extend' method attached to it.


Way 7 : Add an 'extend' function to the Function prototype :

Function.prototype.extend = function(args) {     if (Array.isArray(args) || (args !== null && typeof args === 'object')) {         for (i in args) {             this[i] = args[i];         }     }     return this; };  var doSomething = function() {     return 'Beep'; }.extend({     name : 'Tom',     name2 : 'John' });  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name :  doSomething.name2 : John doSomething() : Beep doSomething.name :  doSomething.name2 : John  

A great advantage to this technique is that it makes adding new properties to a function very easy and DRY as well as completely OO. Also, it's pretty memory friendly. A downside, however, is that it's not very future proof. In case future browsers ever add a native 'extend' function to the Function prototype, this that could break your code.


Way 8 : Run a function recursively once and then return it :

var doSomething = (function f(arg1) {     if(f.name2 === undefined) {         f.name = 'Tom';         f.name2 = 'John';         f.extend = function(args) {             if (Array.isArray(args) || (args !== null && typeof args === 'object')) {                 for (i in args) {                     this[i] = args[i];                 }             }             return this;         };         return f;     } else {         return 'Beep';     } })();  console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); console.log('doSomething() : ' + doSomething()); console.log('doSomething.name : ' + doSomething.name); console.log('doSomething.name2 : ' + doSomething.name2); 

Output :

doSomething.name : f doSomething.name2 : John doSomething() : Beep doSomething.name : f doSomething.name2 : John  

Run a function once and have it test whether one of its properties is set. If not set, set the properties and return itself. If set, execute the function. If you include an 'extend' function as one of the properties, you can later execute that to add new properties.


Adding your own custom properties to an object

In spite of all these options, I would nevertheless recommend against adding properties to a function. It's much better to add properties to objects!

Personally, I prefer the singleton classes with the following syntax.

var keyValueStore = (function() {     return {         'data' : {},         'get' : function(key) { return keyValueStore.data[key]; },         'set' : function(key, value) { keyValueStore.data[key] = value; },         'delete' : function(key) { delete keyValueStore.data[key]; },         'getLength' : function() {             var l = 0;             for (p in keyValueStore.data) l++;             return l;         }     } })(); 

An advantage to this syntax is that it allows for both public and private variables. For example, this is how you make the 'data' variable private :

var keyValueStore = (function() {     var data = {};          return {         'get' : function(key) { return data[key]; },         'set' : function(key, value) { data[key] = value; },         'delete' : function(key) { delete data[key]; },         'getLength' : function() {             var l = 0;             for (p in data) l++;             return l;         }     } })(); 

But you want multiple datastore instances, you say? No problem!

var keyValueStore = (function() {     var count = -1;          return (function kvs() {         count++;          return {             'data' : {},             'create' : function() { return new kvs(); },             'count' : function() { return count; },             'get' : function(key) { return this.data[key]; },             'set' : function(key, value) { this.data[key] = value; },             'delete' : function(key) { delete this.data[key]; },             'getLength' : function() {                 var l = 0;                 for (p in this.data) l++;                 return l;             }         }     })(); })(); 

Finally, you can seperate the instance and singleton properties and use a prototype for the instance's public methods. That results in the following syntax :

var keyValueStore = (function() {     var count = 0; // Singleton private properties              var kvs = function() {         count++; // Instance private properties         this.data = {};  // Instance public properties     };          kvs.prototype = { // Instance public properties         'get' : function(key) { return this.data[key]; },         'set' : function(key, value) { this.data[key] = value; },         'delete' : function(key) { delete this.data[key]; },         'getLength' : function() {             var l = 0;             for (p in this.data) l++;             return l;         }     };              return  { // Singleton public properties         'create' : function() { return new kvs(); },         'count' : function() { return count; }     }; })(); 

With this syntax, you can have :

  • multiple instances of an object
  • private variables
  • class variables

You use it like this :

kvs = keyValueStore.create(); kvs.set('Tom', "Baker"); kvs.set('Daisy', "Hostess"); var profession_of_daisy = kvs.get('Daisy'); kvs.delete('Daisy'); console.log(keyValueStore.count()); 
like image 62
John Slegers Avatar answered Oct 18 '22 21:10

John Slegers