Can I store JavaScript functions in arrays? - javascript

How can I store functions in an array with named properties, so I can call like
FunctionArray["DoThis"]
or even
FunctionArray[integer]
?
Note: I do not wish to use eval.

The important thing to remember is that functions are first class objects in JavaScript. So you can pass them around as parameters, use them as object values and so on. Value(s) in an array are just one example of that.
Note that we are not storing the functions in an array although we can do that and access them with a numeric index. We are storing them in a regular object keyed by the name we want to access that function with.
var functions = {
blah: function() { alert("blah"); },
foo: function() { console.log("foo"); }
};
call as
functions.blah();
or
functions["blah"]();

You want an object literal, not an array.
x = { 'dothis': function() { alert('hi'); } };
Object
x['dothis']()
You can also dynamically invoke
y = 'dothis';
x[y]()
Static/hard coded invocation:
x.dothis()
If you do want an array though:
x = [function(){alert('hi');}][0]()

You can actually do that. Just declare it outside the array, like so...
const your_function = function(){ console.log("I am your function") }
const group = [0, "lizard", false, your_function()]
group[3]
You may also change where it's called, if you want to...
const your_function = function(){ console.log("I am your function") }
const group = [0, "lizard", false, your_function]
group[3]()
Functions were named wrong :/ sry

You can store things directly in an array, but as an object, for example:
var Functions = { DoThis: function() { alert("do this"); } };
Functions['DoThis'](); //alerts "do this"
Functions.DoThis() //alerts "do this"
You can give it a try here.

You can access an object's properties through its name (x["A"]). If you want to assign indexes (0 = "A") you have to do this, and here is an example. (I'm not sure if the for loop will work on any browser; I've tested on Firefox, but you can get the idea.)
var x = {};
x.A = function() { alert("func 1"); };
x.B = function() { alert("func 2"); };
var i = 0;
for (a in x)
{
x[i] = x[a];
++i;
}
x[0](); // func 1
x[1](); // func 2
x["A"](); // func 1
x["B"](); // func 2

You even can use a function as the name of the property:
var func = function(a, b){alert(a+b)};
var obj = {};
obj[func] = 2;

Here is an array that contains various data types, including a function.
Although there is an object in this example, the function is not within the object.
If you replace this object with a string, the function will still work as planned.
I can call the function from within or without the array.
myArray = [
1,
true,
"String",
{
name: "trey",
age: 43,
},
[1,2,3,4],
myFunction = function(){
console.log("What\'s up!");
},
myArray[5](),
];
console.log(myArray);
myArray[5]();
Here is the output:
What's up!
[ 1, true, 'String', { name: 'trey', age: 43 }, [ 1, 2, 3, 4 ], [Function], undefined ]
What's up!

Basically, a function is a special type of object in JavaScript. And in JavaScript, in array you can store anything (not necessarily of the same type). So going by this, yes!, you can store a function, object, and primitive values in an array in JavaScript:
var arr = ["Hello", true, false, 1, {name: "Arshad"}, function(){}]
And you can mix the object and function to make the named function like:
{ callBythisname: function(){ .... }}
You can store this object in an array as well:
var arr = [{ callBythisname: function(){ .... }}];
If you want to call it, call like:
arr[0].callBythisname();

The answer is has a simple answer, but it doesn't to be simplified by the answers in this thread. The simple answer is yes you can place function in an array. In fact, can declare variables and reference them in your function.
Example:
let a = 1;
let b = 2;
let arr = [
a,
b,
function () {
return a + b;
},
];
console.log(arr[2]()); // return 3
console.log(typeof arr); // returns object

Related

Getting the property key from within a property's value

Given the following javascript object:
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: function(){
return this.key; //I want this to return "back" (the prop name)
},
desc: "goes back"
}
}
How can i access the Property Name which is "back" from within the action()?
I think it should be pretty simple, but if it isn't something simple than I'll add more details.
NOTE: aliases[0] is holding the name by chance, and it is not promised to hold it in the future or in other commands.
EDIT:
Sometimes we get to complicated while we can solve the problem pretty fast.
In this case i can just go ahead and return the string "back"
I'll leave the question and accept the answer that solves my question if there is such a solution.
Returning the string as you mentioned is definitely the easiest way. But I could see cases where someone might want to be able to get similar functionality with a dynamically created object in which the keys are not known until run-time.
A solution that would work in that case is exposing the commands object to the sub objects, so they can look themselves up:
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: function(){
var commandKeys = Object.keys(commands);
for(var i=0; i < commandKeys.length; i++){
if(commands[commandKeys[i]] === this){
return commandKeys[i];
}
}
},
desc: "goes back"
}
};
In this case it may also make more sense to share the function across all those action objects:
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: getAction,
desc: "goes back"
},
forward: {
//...
action: getAction,
//...
}
};
function getAction() {
var commandKeys = Object.keys(commands);
for(var i=0; i < commandKeys.length; i++){
if(commands[commandKeys[i]] === this){
return commandKeys[i];
}
}
}
Unless you need to perform some specific logic for each sub object.
EDIT: To improve efficiency, we can make it where the getAction function is not executed every call and add a property that will store the name. That way the lookup only occurs the first time.
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: getAction,
desc: "goes back"
},
forward: {
//...
action: getAction,
//...
}
};
// Only needs to getKey the first time called.
function getAction() {
if(!this.key) this.key = getKey(this);
return this.key;
}
function getKey(obj) {
var commandKeys = Object.keys(commands);
for(var i=0; i < commandKeys.length; i++){
if(commands[commandKeys[i]] === obj){
return commandKeys[i];
}
}
}
When you call action as the following:
commands.back.action();
the scope of action is back. Sadly, the creation of the object that gets assigned to commands.back does not know that this inside of action is called "back". From my understanding, this is done because we could assign the object assigned to commands.back to another object with another name. As in:
var foo = { f: function(){console.log(this) } };
var bar = foo;
bar.f();
Or closer to what you have...
var foo = {
bar: {
f:function(){console.log(this)}
}
};
var other = { another: (foo.bar) };
The only way I know of where the object knows the name of what it was created within are functions. So, we can create a temp function that has the name back that will create an object as desired.
var commands = {
back:(new function back(){
// I prefer to assign to a variable to assist with the readability as to what "this" is:)
var self = this;
self.command = "b";
self.aliases = ["back","go back","backwards"];
self.action = function(){
// Can leave as "this" or change to "self".
return this.key;
};
self.desc = "goes back";
self.key = self.prototype.constructor.name;
})
}
Simplest Solution
But at that point might as well just add a property that already has the name. I would recommend doing a property called key or name rather than placing the name directly into the action function to make it easier to have multiple places where the name is used. Also, allows there to be a single place to change the name within the object if need be.
var commands = {
back:{
command: "b",
aliases: ["back","go back","backwards"],
action: function(){
return this.key;
},
desc: "goes back",
key: "back"
}
}
EDIT: Added this edit as another way to do this, but I would still do the previous way. We can utilize Object.keys to get the name of the property since back is being added as an enumerable property of commands.
var i = 0,
commands = { back: {
key: (function(id){return function(){return Object.keys(commands)[id]}})(i++)
}}
Then can get the key by the following:
commands.back.key();
Or within the action function as:
this.key();
Can add key to back as a get which would look like:
var i = 0,
commands = { back: {
id: (i++),
get key() {return Object.keys(commands)[this.id]}
}}
This will allow you to access the property as commands.back.key and within the action function as this.key.
Can also pre-define everything then can do the following:
var i = 0, commands = { back: undefined };
commands.back = { key: Object.keys(commands)[i++] };
You can add and also advisable to add a toString method for your every object like this.
var commands = {
back:{
command: "b",
name : "back",
aliases: ["back","go back","backwards"],
action: function(){
return this.toString();
},
desc: "goes back",
toString : function(){
return this.name;
}
}
}
console.log(commands.back.action()); // back
console.log(commands.back.toString()); // back
What you are having here, is a nested object, held on the property of an object.
You can not get that property by hand - unless you are doing some strange metaprogramming stuff, such as getting the AST parent node and trying to determine the property the object is held etc. The easiest way, is to hold the property name using a string i.e.: "back".
In simple terms, it is like holding the object to a var
var obj = {/*....*/};
And you are trying to get the var name from within the object.
Remember though that in JavaScript, you can access an object property, using both string and index notation, so commands.back can also be called using commands['back']. If I am guessing right, you are trying to make a sort of dispatching, so this notation can be useful for you.
If you've got that object (literal object) you can't use this keyword. You have two solutions:
return commands.back.aliases[0]
Or otherwise,you can construct the object as a prototype object and not literal object:
var commands = function() {
this.back = function() {
this.command = "b";
this.aliases = ["back","go back","backwards"];
this.action = function() {
return this.aliases[0];
};
this.desc = "goes back";
};
};
And initialize like this
var instance = new commands();
instance.action(); // it returns "back" string

Javascript create reference to an object property?

I understand that in javascript, primitives are passed by value and objects are passed by reference.
I'm interested in creating a workaround of some kind that would let me get a reference to an object property containing a primitive. For example, what I wish would work is:
var someObject = {a: 1, b: 2};
var myRef = someObject.b;
myRef ++;
someObject.b #=> 3
Of course, this doesn't work. I'm aware that you could create a getter and setter function instead, or use one object to reference another object, but what I'd really like is some kind of workaround that allowed me to define a variable as a reference to the property of another object, and so far it seems this just can't be done.
So, my question is simply: is this even possible, and if so, how?
Primitive types are immutable, so no, it's not possible. You can wrap your primitive type with an object, like this:
function MyNumber(n) { this.n = n; }
MyNumber.prototype.valueOf = function() { return this.n; }
var someObject = { a: 1, b: new MyNumber(2) };
var myRef = someObject.b;
MyNumber.call(myRef, myRef + 1);
console.log(+someObject.b); // convert to number with +
OR
var someObject = {
a: { value: 1 },
b: { value: 2 },
};
var myRef = someObject.b;
my_inc(myRef); // function my_inc (obj) { obj.value++; }
// someObject.b.value == 3
The React framework uses a very simple pattern to encapsulate values.
function Link(value, requestChange)
{
this.value = value;
this.requestChange = requestChange;
}
You can pass around the object, the current value can be accessed by inspecting the value property of the object, if you want to change it you can call requestChange with a new value, you can change the value. The advantage would be to have the actual "storage location" and the logic for changing the value decoupled from the value read and write access. Note that the values can also be complex objects.
You could also achieve something similar with closures:
var someObject = {
a: 1,
b: 2
};
function property(object, prop) {
return {
get value () {
return object[prop]
},
set value (val) {
object[prop] = val;
}
};
}
var ref = property(someObject, "b");
ref.value; // 2
++ref.value; // 3
someObject.b; // 3
This works because the getter and setter functions have access to whatever bindings were in scope at the time of their creation (object and prop). You can now pass ref around, store it in a data structure, etc.
No, there isn't a nice way to do it.
You can use a work-around if you want to. Something like wrapping all your primary data types with single element arrays:
var someObject = {a: [1], b: [2]};
var myRef = someObject.b;
myRef[0]++;
someObject.b[0]; // 3
That's less than ideal though, as you have to use [0] to access the property all the time. There are some cases where it can be useful though, and the default toString of a single element array is just the toString of its element, so you can use the property directly in a string context:
console.log('My value: ' + someObject.b); // 'My value: 3'
if you want to "link" or "synchronize" two properties , each of a different object, you could do it like this:
var someObject = {
a: 1,
b: 2
};
var linkedObject = {
a:1,
b:2
}
function property(object, prop) {
return {
get value () {
return object[prop]
},
set value (val) {
object[prop] = val;
}
};
}
var s_prop = 'b'
var o_ref = property(someObject, s_prop);
var tmp = linkedObject[s_prop];
Object.defineProperty(
linkedObject,
s_prop,
{
set: function(value) {
o_ref.value = value;
},
get: function() {
return o_ref.value
}
}
);
linkedObject[s_prop] = tmp
someObject.b = 333 /// linkedObject.b is also 333 now
console.log(someObject.b) // 333
console.log(linkedObject.b)// 333
linkedObject.b = {"test": 2}
console.log(someObject.b) // {test:2}
console.log(linkedObject.b)// {test:2}
someObject.b.test = 3
console.log(someObject.b) // {test:3}
console.log(linkedObject.b)//{test:3}
I don't know how satisfying this is, but you could do it if you were ok with wrapping the desired object in an object like so:
var a = {a:{a:1},b:2};
var b = a.a;
b.a++;
a.a.a //=> 2
It isn't exactly what you asked for, but it would work.

Simulating Array Functionality

Good day! I have this code:
function MyArray() {}
MyArray.prototype.length = 0;
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'];
for (var i = 0; i < methods.length; i++) (function(name) {
MyArray.prototype[ name ] = function() {
return Array.prototype[ name ].apply(this, arguments);
};
})(methods[i]);
})();
I need explanation. I understood that "methods" is array of real methods, which just "exported" to our new class. But, what is this: MyArray.prototype.length = 0; ? Author create new prototype property and assign it zero. And later use this new property!
var mine = new MyArray();
mine.push(1, 2, 3);
assert(mine.length == 3 ...
.....
How it is work? "length" have not instantiation in code above!
Its getting initialized at zero so that if you never call any of its functions, it will return zero (like a real array) and not undefined. Also it needs to start at zero so that the methods update it correctly. in your example, length its 3 because the push method did so.
You can't really subclass Array http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
So if you create an instance of MyArray you can't do: MyArr[0]=...
You can wrap an array inside MyArray and take advantage of the Array functions:
var MyArray=function() {
this.arr=[];
[].push.apply(this.arr,arguments);
//following doesn't work in older browsers
Object.defineProperty(this,"length",{
get:function(){return this.arr.length;},
enumerable:true,
configurable:true
});
}
MyArray.prototype.valueOf=function(){return this.arr;};
(function() {
var methods = ['push', 'pop', 'shift', 'unshift',
'slice', 'splice', 'join'],i=methods.length
while(--i!==-1){
;(function(name) {
MyArray.prototype[ name ] = function() {
console.log(arguments);
return Array.prototype[ name ].apply(this.arr, arguments);
};
}(methods[i]));
}
}());
var mArr1=new MyArray(1,2,3);
console.log(mArr1.slice(0,1));
//you cannot do this: myArr1[0]=22;

What is the most efficient way to create map of functions to arguments?

I want to create a map of functions to its argument. This map will be updated dynamically. Finally all the functions will be called with their corresponding arguments.
function foo1(x) {
//do something with x
}
function foo2(x) {
//do something else with x
}
var map = {};
map[foo1] = [1,2,3]; //this array is updated dynamically in my code
map[foo2] = [4,5,6];
// I want to call foo1 and foo2 with their [1,2,3] and [4,5,6] arguments respectively.
I tried 2 approaches :
Converted foo1 to string (using toString() method) as the key for the map. Then later I get back the function from this string using Function constructor. But I am afraid if this will hit the performance.
// This works. But concerned about the performance
map[foo1.toString()] = [1,2,3];
for(i in map){
var fn = Function( 'return '+ i)();
fn(map[i]);
}
Store objects that wrap up function and their respective arguments like:
{ fn : foo1 , args : [1,2,3] }
{ fn : foo2 , args : [4,5,6] }
Here I store the references to functions instead of the entire function definition. But I have to traverse through the entire array to add more arguments.
Is there any better approach to maintain this map? What are the drawbacks in the above mentioned approaches?
UPDATE
Answer to the question "in what situation I will need this" :
I am maintaining a map from arguments to functions. I update it dynamically.
Later in my code I want to create a reverse map and call the functions with all its arguments.
For eg :
1 -> foo1
2 -> foo2
3 -> foo1,foo2
4 -> foo1
... and so on.
Then I want to create a reverse map like this :
foo1 -> [1,3,4...]
foo2 -> [2,3,...]
And finally call :
foo1( [1,3,4...])
foo2( [2,3,...])
Objects in JavaScript can only have strings as keys, so using map[foo1] is practically identical to map[foo1.toString()]. These both have problems that you haven't noticed: they discard closed-over variables, e.g.:
function makeCounter() {
var counter = 0;
return function() { return ++counter; }
}
If I have
var myCounter = makeCounter();
then myCounter.toString() will be function() { return ++counter; }, and trying to reconstitute that with the Function constructor will result in having the wrong counter reference.
Really, the best option might be to use the function's name as the property and as a value, use an object like you suggested:
var map = {};
map['foo1'] = { fn: foo1, args: [1, 2, 3] };
Then, if you want to add more arguments later, it's pretty obvious:
map['foo1'].args.push(4);
And to call them all, you might use something like this:
for(var functionName in map) {
if(!Object.prototype.hasOwnProperty.call(map, functionName)) {
continue;
}
map[functionName].fn.apply(null, map[functionName].args);
}
Until there's a native cross-browser solution for having objects as keys, you could always implement your own solution. Here's an example of what you could do. In the code below, the ObjectMap will store a generated key as a property of the object that needs to serve as a key. The property name that is used to store the key on the object is randomized to reduce possible conflicts. The map implementation can then use this property's value to retrieve the key on the object and then retrieve it's associated value.
JSPERF: http://jsperf.com/object-map
function ObjectMap() {
this.key = 0;
//you should implement a better unique id algorithm
this.mapId = '_' + Math.floor(Math.random() * 10000);
this.data = {};
}
ObjectMap.prototype = {
set: function (object, value) {
var key = ++this.key;
if (object[this.mapId]) {
return;
}
object[this.mapId] = key;
this.data[key] = value;
},
get: function (object) {
var key = object[this.mapId];
return key? this.data[key] : null;
},
remove: function (object) {
var key = object[this.mapId];
if (!key) {
return;
}
delete this.data[key];
delete object[key];
}
};
function a() {}
var map = new ObjectMap();
map.set(a, 'test');
console.log(map.get(a)); //test
In order to use objects (or functions) as keys you'll need to use Harmony (EcmaScript 6) WeakMap or Map. They're both currently experimental and both are available in Firefox. I believe WeakMap might also be available in Chrome (with the proper flag settings?).
If your platform supports WeakMap, and you choose to incorporate them, then their usage is quite straightforward:
var myWeakMap=new WeakMap();
myWeakMap.get(key [, defaultValue]);
myWeakMap.set(key, value);
myWeakMap.has(key);
myWeakMap.delete(key);
myWeakMap.clear();
More information (note the MDN references appear to be unlisted):
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/WeakMap
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Map
http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps
Also: Alternatively you can use an array of functions, then use indexOf to get the index of the function, then access the parameters in an another array with that index.
function a(){}
function b(){}
var x=[a,b].indexOf(b); //x=1
Credits to Dagg Nabbit for suggesting this in the comments under my question.
"Don't forget functions can have properties. You could always store the
functions in an array, and attach their index in the array to the
function as a propery, and look them up that way." - Dagg Nabbit
Consider the following map of args-to-callback arguments :
map :
1 -> foo1
2 -> foo1,foo2
3 -> foo2
The objective is to construct a callback-to-args map (reverse map) like this :
callbackMap:
foo1 -> [1,2]
foo2 -> [2,3]
Approach :
var allArgsPossible = [1,2,3]
// contains the list of callbacks to be called
var callbackArray = [];
//maps the callback index in callbackArray to the callback's arguments
//callbackMap[index] = args means callbackArray[index] will be called with parameter "args"
var callbackMap = {};
for( i in allArgsPossible)
{
var item = allArgsPossible[i];
var callbacks = map[ item ];
for(j in callbacks)
{
var callback = callbacks[j];
if(callback.index == undefined)
{
var index = callbackArray.length;
// adding a new property "index" to the callback
callback.index = index;
callbackMap[index] = [item];
//create a new entry in callbackArray
callbackArray.push(callback);
}
else
{
callbackMap[callback.index].push(item);
}
}
}
console.log(JSON.stringify(callbackMap));
for( i in callbackArray)
{
var callback = callbackArray[i];
//get arguments from our reverse map
var args = callbackMap[callback.index];
// Bingo !
callback(args);
}
You can get the whole picture here : http://jsfiddle.net/kyvUA/2/
One point to note here is that the callback function may already have an "index" property for some other purpose. If that is a concern, you can generate a random string and store this property on the callback with the index as the value. ( as suggested by #plalx )
Cheers !

Is it possible to refer to a "real-time parent" function's variable from inside of a function of the same array?

I fully realize that this code is kinda strange by the very nature of javascript, but look at it:
var arr = [
function () {
this.b = "hello";
//or var b = "hello";
arr[1]();
},
function () {
var str = RealtimeParentFunc.b;
alert(str);
// want to get "hello"
}
];
// want to get "hello"!
arr[0]();
Maybe it's impossible, but I wonder if there's a way of doing this. How to get a variable of an array's function that called a function in the same array?
UPDATE
I had to go back to this problem and decided to post an update: of course I can't reference arr[i] from within itself, but I must emphasize that the code above was not supposed to be taken too literally: it was just a demo of what I wanted; but it looks like I can do what I want if I have a small "adjuvant" function that is previously defined in the code:
var a = function (i, v) {
return arr[i]()[v];
};
var arr = [
function () {
var h = "hello, ";
var n = a(2, "n");
return {
"h": h+n
}
}, function () {
var h = a(0, "h");
alert(h)
}, function () {
return {
"n": "your name"
}
}];
arr[1]();
You can't define an array and access it's values at the same time, since at the time of creation, the arr2 does not exist yet.
The [ function(){ .. }, function(){ .. }] array isn't created and set to arr2 until it reaches the ; semicolon at the end. Therefore you can't reference arr2[1]() while it's being defined.
On that note, what you can do is define the array first, then .push() the functions into it, but you're still limited to doing it sequentially:
var arr2 = [];
arr2.push(
function () {
var b = "hello";
//arr2[1](); // CAN'T DO THIS since arr2[1] doesn't exist
return {
b: b
};
});
arr2.push(
function () {
var str = arr2[0]().b;
alert(str);
// want to get "hello"
});
// want to get "hello"!
arr2[0]();
arr2[1]();
See working fiddle: http://jsfiddle.net/amyamy86/5k57X/

Categories

Resources