Im doing a course in frontend Dev in uni and the teacher insists on us using a old book so to learn the history and basics of JavaScript before we move on to more advanced and recent implementations.
Now in this book we are instructed to code a webpage for a food truck and it is supposed to take orders.
Now in some scripts the objects are defined like this :
function DataStore() {
this.data = {};
}
Here the data object is defined using the keyword "this" as in saying it belongs to the function object DataStore.
however in some scripts the data object is defined as:
FormHandler.prototype.addSubmitHandler = function() {
console.log('Setting submit handler for form');
this.$formElement.on('submit', function(event){
event.preventDefault();
var data = {};
My question is what is the difference in the two data objects?
Short answer is at the bottom
Long and boring introduction to how things work
When you write this :
function SomeThing() { }
You can always do
let a = new SomeThing();
even when it doesn't make sense like in :
function lel() { console.log('lelelel'); }
let g = new lel();
console.log(g);
console.log(g.constructor.name);
What this means is that classes are actually the same as functions. And a function in which you use the keyword this usually means you will want to create instances of it.
now if I want all instances of my lel() function class to have a property called foo and a method called bar here's how you do :
lel.prototype.foo = "Some initial value";
lel.prototype.bar = function() {
console.log(this.foo);
}
now I can do
let g = new lel();
lel.bar();
lel.foo = "Hell yeah !";
lel.bar();
In conclusion, this :
function SomeThing() {
this.data = {};
}
SomeThing.prototype.setData = function(key, value) {
this.data[key] = value;
}
SomeThing.prototype.getDataKeys = function() {
return Object.keys(this.data);
}
SomeThing.prototype.getDataValues = function() {
return Object.values(this.data);
}
is the same thing as this
class SomeThing {
constructor() {
this.data = {};
}
setData(key, value) {
this.data[key] = value;
}
getDataKeys() {
return Object.keys(this.data);
}
getDataValues() {
return Object.values(this.data);
}
}
Clarifications about your question
If somewhere in your code you have :
FormHandler.prototype.addSubmitHandler = function() {
console.log('Setting submit handler for form');
this.$formElement.on('submit', function(event){
event.preventDefault();
var data = {};
if necessarily means that somewhere else in your code you have
function FormHandler(...) { ... }
Short answer
This :
function DataStore() {
this.data = {};
}
is how you define a class named DataStore with a property called data initialized to the value {}
And this :
FormHandler.prototype.addSubmitHandler = function() {
...
var data = {};
}
is how you add a method called addSubmitHandler to the already defined class FormHandler. That method uses a local variable called data, could have been any other name
In the first case, data is a property of the object that is created like this: new DataStore.
You can access this property like this:
var obj = new DataStore();
obj.data // => {}
/* or */
obj['data'] // => {}
In the second case, data is just a global variable, inside of an event handler, that is added executing the function.
var obj = new FormHandler();
obj.addSubmitHandler();
You access this variable like this:
data // => {}
I don't think it's a good idea to learn old JS. You would be out of date. You wouldn't be able to use latest technologies, and it would be harder to get a job.
I have the following javascript code:
function testClass() {
this.SaveValue = function (value) {
var isInstance = value instanceof TestEnum;
if (!isInstance) {
return;
}
}
}
TestEnum = {
VALUE_0: 0,
VALUE_1: 1,
VALUE_2: 2
}
I create an instance of this object in the following way:
$(function () {
var a = new testClass();
a.SaveValue(TestEnum.VALUE_1);
});
All I'd like to do is test that the value passed to the SaveValue function is actually the type of TestEnum. However, when I run this code I get the following error: Uncaught TypeError: Expecting a function in instanceof check, but got 1
Am I going about this the right way? I tried typeof but it only returns number which is not particularly useful to me.
You could create the values as instances of the "class":
function TestEnum(value) {
this._value = value;
}
TestEnum.prototype.valueOf = function() {
return this._value;
}
TestEnum.prototype.toString = function() {
return 'TestEnum_' + this._value;
}
TestEnum.VALUE_0 = new TestEnum(0);
TestEnum.VALUE_1 = new TestEnum(1);
The following would work then:
TestEnum.VALUE_0 instanceof TestEnum
But it also means you'd have to explicitly access the numerical value of one value with .valueOf. In some cases JS will do this automatically for you (like in 5 + TestEnum.VALUE_1). Overriding toString so that you can use a value as property might also be necessary.
It really depends on your use case whether this is a viable solution.
Alternatively, if just want to test whether a value is part of the enum, you can have an additional property which holds all possible values:
TestEnum.values = {0: true, 1: true, ...};
And then test it with
value in TestEnum.values
// or more reliable (fails for inherited `Object` properties)
TestEnum.values.hasOwnProperty(value);
You could even automate this:
function collectValues(obj) {
var values = {}; // or Object.create(null) if available
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
values[obj[prop]] = true;
}
}
return values;
}
TestEnum.values = collectValues(TestEnum);
This will only reliably work for primitive values though and won't distinguish between the string "1" and the number 1.
You are passing a number to the function in
a.SaveValue(TestEnum.VALUE_1);
Since TestEnum is simply an Object, and you are referencing a number property on that object, you're calling your function with a number. You should instead create a TestEnumValue object and use that for your Object's properties:
JSFiddle link for below
function testClass() {
this.SaveValue = function (value) {
var isInstance = value instanceof TestEnumValue;
if (!isInstance) {
return;
}
}
}
TestEnumValue = function(arg) {
arg = arg ? arg : 0; // sensible default
this.key = 'VALUE_' + arg;
this.val = arg;
}
Level = {
NumSpiders : new TestEnumValue(0),
NumCreepers: new TestEnumValue(1),
NumZombies : new TestEnumValue(2),
NumChickens: new TestEnumValue // uses default enum value
};
$(function() {
var a = new testClass();
a.SaveValue(Level.NumSpiders);
$('#hi').text(Level.NumSpiders.key);
});
Playing around with this, I noticed that you can leverage the fact that an enum compiles into an object that binds the values both ways combined with a hasOwnProperty check.
export enum TEST_ENUM{
ZERO, // 0
ONE, // 1
TWO, // 2
}
let a = 1;
let b = TEST_ENUM.TWO // 2
let c = 5 // incorrect value
TEST_ENUM.hasOwnProperty(a); // TRUE
TEST_ENUM.hasOwnProperty(b); // TRUE
TEST_ENUM.hasOwnProperty(c); // FALSE
This comes with a few caveats though;
// An object's keys are always strings...
// Although this shouldn't not matter usually (e.g. parsed user input)
TEST_ENUM.hasOwnProperty("2"); // TRUE
// And the enum is bound two-way so:
let input = "TWO";
if (TEST_ENUM.hasOwnProperty(input) { // TRUE
let result = input // "TWO"
// result is now the enum's value, instead of the key.
result = TEST_ENUM[input]; // this would be the correct assignment
};
Of course you can fix both of these with a typeof check, in case of a string assign it TEST_ENUM[mystring].
Note that my intellisense didn't autocomplete the hasOwnProperty function on an enum, but it doesn't complain about it either, and it's available on all browsers.
Edit
Here's an example of how you could do it.
function TestEnum(val) {
this.vals = this.vals || [];
if (this.vals.indexOf(val) == -1) console.log('nope: ' + val);
else console.log('ok: ' + val);
}
(function() {
var vals = {
VALUE_0: 0,
VALUE_1: 1,
VALUE_2: 2
};
TestEnum.prototype.vals = [];
for (var key in vals) {
TestEnum[key] = vals[key];
TestEnum.prototype.vals.push(vals[key]);
}
})();
Now new TestEnum(TestEnum.VALUE_0); is OK, but if you try, say, new TestEnum(3), then it throws an exception.
This is a bit backwards -- x instanceof y means that x has been created as x = new y(). Since TestEnum isn't even a function, you can't create an instance of it, so this isn't going to work.
What you could do is maybe something like this:
function MyEnum(enumVal) { this.val = enumVal; }
a.SaveValue( new MyEnum(TestEnum.VALUE_1) );
Then check using isInstance = value instanceof MyEnum.
I have this:
this.f = function instance(){};
I would like to have this:
this.f = function ["instance:" + a](){};
This will basically do it at the most simple level:
"use strict";
var name = "foo";
var func = new Function(
"return function " + name + "(){ alert('sweet!')}"
)();
//call it, to test it
func();
If you want to get more fancy, I have a written an article on "Dynamic function names in JavaScript".
You can use Object.defineProperty as noted in the MDN JavaScript Reference:
var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
In recent engines, you can do
function nameFunction(name, body) {
return {[name](...args) {return body.apply(this, args)}}[name]
}
const x = nameFunction("wonderful function", (p) => p*2)
console.log(x(9)) // => 18
console.log(x.name) // => "wonderful function"
Thanks to T S for pointing out the need to preserve this in the comments.
Also, these days, I'd probably use the Object.defineProperty approach to achieve something similar.
Update 2021: CherryDT's answer should be the easiest most straight forward way now, but it doesn't work consistently with different browsers for stack traces or Function.prototype.toString(), so if you need that you're stuck with this less convenient solution.
Old answer: Many suggestions here are suboptimal, by using eval, hacky solutions or wrappers.
As of ES2015 names are inferred from the syntactic position for variables and properties.
So this will work just fine:
const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'
Resist the temptation to create named function factory methods as you wouldn't be able to pass the function from outside and retrofit it into the syntactic position to infer its name. Then it's already too late. If you really need that, you have to create a wrapper. Someone did that here, but that solution doesn't work for classes (which are also functions).
A much more in-depth answer with all the variants outlined has been written here: https://stackoverflow.com/a/9479081/633921
As others mentioned, this is not the fastest nor most recommended solution. Marcosc's solution below is the way to go.
You can use eval:
var code = "this.f = function " + instance + "() {...}";
eval(code);
What about
this.f = window["instance:" + a] = function(){};
The only drawback is that the function in its toSource method wouldn't indicate a name. That's usually only a problem for debuggers.
The syntax function[i](){} implies an object with property values that are functions, function[], indexed by the name, [i].
Thus
{"f:1":function(){}, "f:2":function(){}, "f:A":function(){}, ... } ["f:"+i].
{"f:1":function f1(){}, "f:2":function f2(){}, "f:A":function fA(){}} ["f:"+i] will preserve function name identification. See notes below regarding :.
So,
javascript: alert(
new function(a){
this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
}("A") . toSource()
);
displays ({f:(function () {})}) in FireFox.
(This is almost the same idea as this solution, only it uses a generic object and no longer directly populates the window object with the functions.)
This method explicitly populates the environment with instance:x.
javascript: alert(
new function(a){
this.f=eval("instance:"+a+"="+function(){})
}("A") . toSource()
);
alert(eval("instance:A"));
displays
({f:(function () {})})
and
function () {
}
Though the property function f references an anonymous function and not instance:x, this method avoids several problems with this solution.
javascript: alert(
new function(a){
eval("this.f=function instance"+a+"(){}")
}("A") . toSource()
);
alert(instanceA); /* is undefined outside the object context */
displays only
({f:(function instanceA() {})})
The embedded : makes the javascript function instance:a(){} invalid.
Instead of a reference, the function's actual text definition is parsed and interpreted by eval.
The following is not necessarily problematic,
The instanceA function is not directly available for use as instanceA()
and so is much more consistent with the original problem context.
Given these considerations,
this.f = {"instance:1": function instance1(){},
"instance:2": function instance2(){},
"instance:A": function instanceA(){},
"instance:Z": function instanceZ(){}
} [ "instance:" + a ]
maintains the global computing environment with the semantics and syntax of the OP example as much as possible.
The most voted answer has got already defined [String] function body. I was looking for the solution to rename already declared function's name and finally after an hour of struggling I've dealt with it. It:
takes the alredy declared function
parses it to [String] with .toString() method
then overwrites the name (of named function) or appends the new one (when anonymous) between function and (
then creates the new renamed function with new Function() constructor
function nameAppender(name,fun){
const reg = /^(function)(?:\s*|\s+([A-Za-z0-9_$]+)\s*)(\()/;
return (new Function(`return ${fun.toString().replace(reg,`$1 ${name}$3`)}`))();
}
//WORK FOR ALREADY NAMED FUNCTIONS:
function hello(name){
console.log('hello ' + name);
}
//rename the 'hello' function
var greeting = nameAppender('Greeting', hello);
console.log(greeting); //function Greeting(name){...}
//WORK FOR ANONYMOUS FUNCTIONS:
//give the name for the anonymous function
var count = nameAppender('Count',function(x,y){
this.x = x;
this.y = y;
this.area = x*y;
});
console.log(count); //function Count(x,y){...}
For setting the name of an existing anonymous function:
(Based on #Marcosc's answer)
var anonymous = function() { return true; }
var name = 'someName';
var strFn = anonymous.toString().replace('function ', 'return function ' + name);
var fn = new Function(strFn)();
console.log(fn()); // —> true
Demo.
Note: Don't do it ;/
The function's name property by default isn't writeable, but since it's configurable we can still use Object.defineProperty to change it. Since Object.defineProperty conveniently returns the object itself, we can write a function with a dynamic name like this:
const theName = 'foobar'
const fn = Object.defineProperty(function () {
/* ... */
}, 'name', { value: theName })
console.log(fn.name) // Logs foobar
Of course this could be factored out into a helper function:
const nameFunction = (name, fn) => Object.defineProperty(fn, 'name', { value: name })
const fn = nameFunction('foobar', function () {
/* ... */
})
console.log(fn.name) // Logs foobar
The above nameFunction function can also be used to rename an existing function, of course (here it's just renaming and returning the anonymous one).
Dynamic methods of an object may be created using Object Literal Extensions provided by ECMAScript 2015 (ES6):
const postfixes = ['foo', 'bar'];
const mainObj = {};
const makeDynamic = (postfix) => {
const newMethodName = 'instance: ' + postfix;
const tempObj = {
[newMethodName]() {
console.log(`called method ${newMethodName}`);
}
}
Object.assign(mainObj, tempObj);
return mainObj[newMethodName]();
}
const processPostfixes = (postfixes) => {
for (const postfix of postfixes) {
makeDynamic(postfix);
}
};
processPostfixes(postfixes);
console.log(mainObj);
The output of running the code above is:
"called method instance: foo"
"called method instance: bar"
Object {
"instance: bar": [Function anonymous],
"instance: foo": [Function anonymous]
}
the best way it is create object with list of dynamic functions like:
const USER = 'user';
const userModule = {
[USER + 'Action'] : function () { ... },
[USER + 'OnClickHandler'] : function () { ... },
[USER + 'OnCreateHook'] : function () { ... },
}
There are two methods to achieve this, and they have their pros and cons.
name property definition
Defining immutable name property of a function.
Pros
Every character is available for the name. (eg. () 全 {}/1/얏호/ :D #GO(#*#%! /*)
Cons
The function's syntactic (“expressional”) name may not correspond with its name property value.
Function expression evaluation
Making a named function expression and evaluating it with Function constructor.
Pros
The function's syntactic (“expressional”) name always corresponds with its name property value.
Cons
Whitespaces (and etc.) are not available for the name.
Expression-injectable (eg. For input (){}/1//, the expression is return function (){}/1//() {}, gives NaN instead of a function.).
const demoeval = expr => (new Function(`return ${expr}`))();
// `name` property definition
const method1 = func_name => {
const anon_func = function() {};
Object.defineProperty(anon_func, "name", {value: func_name, writable: false});
return anon_func;
};
const test11 = method1("DEF_PROP"); // No whitespace
console.log("DEF_PROP?", test11.name); // "DEF_PROP"
console.log("DEF_PROP?", demoeval(test11.toString()).name); // ""
const test12 = method1("DEF PROP"); // Whitespace
console.log("DEF PROP?", test12.name); // "DEF PROP"
console.log("DEF PROP?", demoeval(test12.toString()).name); // ""
// Function expression evaluation
const method2 = func_name => demoeval(`function ${func_name}() {}`);
const test21 = method2("EVAL_EXPR"); // No whitespace
console.log("EVAL_EXPR?", test21.name); // "EVAL_EXPR"
console.log("EVAL_EXPR?", demoeval(test21.toString()).name); // "EVAL_EXPR"
const test22 = method2("EVAL EXPR"); // Uncaught SyntaxError: Unexpected identifier
If you want to have a dynamic function like the __call function in PHP, you could use Proxies.
const target = {};
const handler = {
get: function (target, name) {
return (myArg) => {
return new Promise(resolve => setTimeout(() => resolve('some' + myArg), 600))
}
}
};
const proxy = new Proxy(target, handler);
(async function() {
const result = await proxy.foo('string')
console.log('result', result) // 'result somestring' after 600 ms
})()
You can use Dynamic Function Name and parameters like this.
1) Define function Separate and call it
let functionName = "testFunction";
let param = {"param1":1 , "param2":2};
var func = new Function(
"return " + functionName
)();
func(param);
function testFunction(params){
alert(params.param1);
}
2) Define function code dynamic
let functionName = "testFunction(params)";
let param = {"param1":"1" , "param2":"2"};
let functionBody = "{ alert(params.param1)}";
var func = new Function(
"return function " + functionName + functionBody
)();
func(param);
This utility function merge multiple functions into one (using a custom name), only requirement is that provided functions are properly "new lined" at start and end of its scoop.
const createFn = function(name, functions, strict=false) {
var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
for(var i=0, j=functions.length; i<j; i++) {
var str = functions[i].toString();
var s = str.indexOf(cr) + 1;
a.push(str.substr(s, str.lastIndexOf(cr) - s));
}
if(strict == true) {
a.unshift('\"use strict\";' + cr)
}
return new Function(a.join(cr) + cr + '}')();
}
// test
var a = function(p) {
console.log("this is from a");
}
var b = function(p) {
console.log("this is from b");
}
var c = function(p) {
console.log("p == " + p);
}
var abc = createFn('aGreatName', [a,b,c])
console.log(abc) // output: function aGreatName()
abc(123)
// output
this is from a
this is from b
p == 123
I had better luck in combining Darren's answer and kyernetikos's answer.
const nameFunction = function (fn, name) {
return Object.defineProperty(fn, 'name', {value: name, configurable: true});
};
/* __________________________________________________________________________ */
let myFunc = function oldName () {};
console.log(myFunc.name); // oldName
myFunc = nameFunction(myFunc, 'newName');
console.log(myFunc.name); // newName
Note: configurable is set to true to match the standard ES2015 spec for Function.name1
This especially helped in getting around an error in Webpack similar to this one.
Update: I was thinking of publishing this as an npm package, but this package from sindresorhus does exactly the same thing.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
I struggled a lot with this issue. #Albin solution worked like a charm while developing, but it did not work when I changed it to production. After some debugging I realized how to achieve what I needed. I'm using ES6 with CRA (create-react-app), which means it's bundled by Webpack.
Lets say you have a file that exports the functions you need:
myFunctions.js
export function setItem(params) {
// ...
}
export function setUser(params) {
// ...
}
export function setPost(params) {
// ...
}
export function setReply(params) {
// ...
}
And you need to dynamically call these functions elsewhere:
myApiCalls.js
import * as myFunctions from 'path_to/myFunctions';
/* note that myFunctions is imported as an array,
* which means its elements can be easily accessed
* using an index. You can console.log(myFunctions).
*/
function accessMyFunctions(res) {
// lets say it receives an API response
if (res.status === 200 && res.data) {
const { data } = res;
// I want to read all properties in data object and
// call a function based on properties names.
for (const key in data) {
if (data.hasOwnProperty(key)) {
// you can skip some properties that are usually embedded in
// a normal response
if (key !== 'success' && key !== 'msg') {
// I'm using a function to capitalize the key, which is
// used to dynamically create the function's name I need.
// Note that it does not create the function, it's just a
// way to access the desired index on myFunctions array.
const name = `set${capitalizeFirstLetter(key)}`;
// surround it with try/catch, otherwise all unexpected properties in
// data object will break your code.
try {
// finally, use it.
myFunctions[name](data[key]);
} catch (error) {
console.log(name, 'does not exist');
console.log(error);
}
}
}
}
}
}
Node.js JavaScript Class Based Dynamic Function Name
File: Schema.js
class Schema {
constructor() {
this.name = null;
}
virtual(name = null) {
this.name = name;
return this;
}
get(func = false) {
if (!this.name || !func instanceof Function) {
throw new Error("Name and function must be provided.");
}
// Attach the dynamic function name to the "this" Object
this[this.name] = func;
this.name = null;
}
}
module.exports = Schema;
File: index.js
const Schema = require("./Schema.js");
const User = new Schema();
User.virtual("getPostCount").get(() => {
return 10 + 10;
});
const ok = User.getPostCount();
console.log({ User });
console.log(ok);
Thank you Marcosc! Building on his answer, if you want to rename any function, use this:
// returns the function named with the passed name
function namedFunction(name, fn) {
return new Function('fn',
"return function " + name + "(){ return fn.apply(this,arguments)}"
)(fn)
}
function myFunction() {
console.log('It works!');
}
var name = 'myFunction';
window[name].call();
You was near:
this["instance_" + a] = function () {...};
{...};
I might be missing the obvious here, but what's wrong with just adding the name? functions are invoked regardless of their name. names are just used for scoping reasons. if you assign it to a variable, and it's in scope, it can be called. hat happens is your are executing a variable which happens to be a function. if you must have a name for identification reasons when debugging, insert it between the keyword function and the opening brace.
var namedFunction = function namedFunction (a,b) {return a+b};
alert(namedFunction(1,2));
alert(namedFunction.name);
alert(namedFunction.toString());
an alternative approach is to wrap the function in an outer renamed shim, which you can also pass into an outer wrapper, if you don't want to dirty the surrounding namespace. if you are wanting to actually dynamically create the function from strings (which most of these examples do), it's trivial to rename the source to do what you want. if however you want to rename existing functions without affecting their functionality when called elsewhere, a shim is the only way to achieve it.
(function(renamedFunction) {
alert(renamedFunction(1,2));
alert(renamedFunction.name);
alert(renamedFunction.toString());
alert(renamedFunction.apply(this,[1,2]));
})(function renamedFunction(){return namedFunction.apply(this,arguments);});
function namedFunction(a,b){return a+b};
This is BEST solution, better then new Function('return function name(){}')().
Eval is fastest solution:
var name = 'FuncName'
var func = eval("(function " + name + "(){})")
I apologize because this topic comes up a lot, but I have not been able to have this adequately explained in anything I've read today.
I am trying to make a simple collection class (and learn about javascript prototyping at the same time) designed to store objects with a "name" property and lets its members be accessed by index or value. So far I've got this:
function Collection() {}
Collection.prototype.constructor = Collection;
Collection.prototype._innerList = [];
Collection.prototype._xref = {};
Collection.prototype.count = function () { return this._innerList.length; };
Collection.prototype.add = function (obj) {
this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function (id) {
if (typeof id == "string") {
return this._innerList[this._xref[id]];
} else {
return this._innerList[id];
}
};
http://jsfiddle.net/YrVFZ/
The problem:
var foo = new Collection();
foo.add({name: "someitem", value:"hello world"}); // foo.count()== 1
var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"}); // bar.count()== 2
Hmm...
Basically, the new instance bar is created with all the properties having the current values of the data in foo. I know I can fix this by putting _xref, etc. inside the constructor, but I'm trying to understand how prototyping works. If I create a new instance, and make changes to the data in that instance, why would those values carry over when I create another new instance?
If I make further changes to a property from the prototype of foo or bar they are independent, so it doesn't seem as if I'm somehow referencing the same instance of anything. So what is causing bar to be instantiated with the current values from foo?
Consider a classroom full of students. Putting something on the prototype is like putting something on the white board for them all to see. When you're declaring
Collection.prototype._innerList = [];
you're giving every collection that property; regardless of calling new Collection() any changes to the white board affects all students. However, if you define it within the constructor, or one of the functions as this.variableName = [], each copy will have its own variableName, like handing each student a handout. Obviously, there's some cases when it's okay to have something on the white board, such as instructions that will be universal from student to student, but if each item is going to be different for each student, it should be an individual property. Hope this explanation makes sense...
You want to be doing this.
function Collection() {
if (!this instanceof Collection)
return new Collection();
this._innerList = [];
this._xref = {};
return this;
}
Collection.prototype.count = function() {
return this._innerList.length;
};
Collection.prototype.add = function(obj) {
this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function(id) {
if (typeof id == "string") {
return this._innerList[this._xref[id]];
} else {
return this._innerList[id];
}
};
var foo = new Collection();
foo.add({name: "someitem", value:"hello world"});
console.log(foo.count()); // 1
var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"});
console.log(bar.count()); // 1
http://jsfiddle.net/vXbLL/
Edit
Not really relevant to your question, but it's something I do so I will throw it out there. Whenever I'm doing something on the prototype, if I'm not returning something, I return this. It allows chaining, so you could do instance.function1().function2().function3() as long as function1 and function2 return this.
You can think of a prototype as giving all objects of that class shared variables. Like static variables in a c++ class if that makes any sense. That's ok to do for functions because they're all the same for each instance of the class. However, if you want the object to have its own non-shared variable, you shouldn't use the prototype. One simple way to do it is to assign them in the constructor method like this:
function Collection()
{
this._innerList = [];
this._xref = {};
}
Collection.prototype.count = function () { return this._innerList.length; };
Collection.prototype.add = function (obj) {
this._xref[obj.name] = this._innerList.push(obj) - 1;
}
Collection.prototype.get = function (id) {
if (typeof id == "string") {
return this._innerList[this._xref[id]];
} else {
return this._innerList[id];
}
};
var foo = new Collection();
foo.add({name: "someitem", value:"hello world"}); // foo.count()== 1
document.write(foo.count(),"<br>");
var bar= new Collection();
bar.add({name: "someotheritem", value:"hello world"}); // bar.cou
document.write(bar.count(),"<br>");
Do JavaScript objects/variables have some sort of unique identifier? Like Ruby has object_id. I don't mean the DOM id attribute, but rather some sort of memory address of some kind.
If you want to lookup/associate an object with a unique identifier without modifying the underlying object, you can use a WeakMap:
// Note that object must be an object or array,
// NOT a primitive value like string, number, etc.
var objIdMap=new WeakMap, objectCount = 0;
function objectId(object){
if (!objIdMap.has(object)) objIdMap.set(object,++objectCount);
return objIdMap.get(object);
}
var o1={}, o2={}, o3={a:1}, o4={a:1};
console.log( objectId(o1) ) // 1
console.log( objectId(o2) ) // 2
console.log( objectId(o1) ) // 1
console.log( objectId(o3) ) // 3
console.log( objectId(o4) ) // 4
console.log( objectId(o3) ) // 3
Using a WeakMap instead of Map ensures that the objects can still be garbage-collected.
No, objects don't have a built in identifier, though you can add one by modifying the object prototype. Here's an example of how you might do that:
(function() {
var id = 0;
function generateId() { return id++; };
Object.prototype.id = function() {
var newId = generateId();
this.id = function() { return newId; };
return newId;
};
})();
That said, in general modifying the object prototype is considered very bad practice. I would instead recommend that you manually assign an id to objects as needed or use a touch function as others have suggested.
Actually, you don't need to modify the object prototype. The following should work to 'obtain' unique ids for any object, efficiently enough.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
I've just come across this, and thought I'd add my thoughts. As others have suggested, I'd recommend manually adding IDs, but if you really want something close to what you've described, you could use this:
var objectId = (function () {
var allObjects = [];
var f = function(obj) {
if (allObjects.indexOf(obj) === -1) {
allObjects.push(obj);
}
return allObjects.indexOf(obj);
}
f.clear = function() {
allObjects = [];
};
return f;
})();
You can get any object's ID by calling objectId(obj). Then if you want the id to be a property of the object, you can either extend the prototype:
Object.prototype.id = function () {
return objectId(this);
}
or you can manually add an ID to each object by adding a similar function as a method.
The major caveat is that this will prevent the garbage collector from destroying objects when they drop out of scope... they will never drop out of the scope of the allObjects array, so you might find memory leaks are an issue. If your set on using this method, you should do so for debugging purpose only. When needed, you can do objectId.clear() to clear the allObjects and let the GC do its job (but from that point the object ids will all be reset).
const log = console.log;
function* generateId() {
for(let i = 0; ; ++i) {
yield i;
}
}
const idGenerator = generateId();
const ObjectWithId = new Proxy(Object, {
construct(target, args) {
const instance = Reflect.construct(target, args);
instance['id'] = idGenerator.next().value;
return instance;
}
})
const myObject = new ObjectWithId({
name: '##NativeObject'
});
log(myObject.id);