Store object in the localStorage - javascript

// Extending the Storage object:
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
Storage.prototype.getObject = function(key) {
var value = this.getItem(key);
return value && JSON.parse(value);
}
// START HERE:
var dog = {
name : '',
woof : function(){ console.log('woof woof, I am ' + this.name); }
}
dog.name = 'Lovely';
//Store in the localstorage
localStorage.setObject('mydog', dog);
//Get from the localstorage
var mydog = localStorage.getObject('mydog');
console.log(mydog.name); // prints 'Lovely'
console.log(mydog.woof()); // ootest.html:36 Uncaught TypeError: mydog.woof is not a function(…)
Why I am getting that error ?

The right way: use object constructor
let Dog = function(name) {
this.name = name
}
Dog.prototype.woof = function woof() {
console.log('woof woof, I am ' + this.name);
}
// Create a 'static' method for a Dog 'class'
Dog.fromJSON = function fromJSON(json) {
return new Dog(JSON.parse(json).name)
}
// Instantinate your objects with `new` keyword
let dog = new Dog('Lovely')
dog.woof()
let storedDog = JSON.stringify(dog)
console.log('stored:', storedDog)
// Dog.fromJSON() returns new Dog object,
// with the same parameters for a constructor
// and with all methods
let restoredDog = Dog.fromJSON(storedDog)
restoredDog.woof()
Limitation
This approach will work well only for objects than will behave similar if created with similar constructor parameter. If you want to store objects with rich data inside, please refer to How come JSON can't save object's functions? accepted answer.
Just for learning something new: storing functions in JSON
Functions can be created from string at runtime via Function object. To create function it we need to pass arguments and body:
new Function ([arg1[, arg2[, ...argN]],] functionBody)
To get a method params and body we need to call inherited toString() method on method:
dog.woof.toString()
We can store function declaration in string and we can create function from string. Next steps are: implement conventional functions storage as object properties, implement restoring saved functions from JSON string.
Dirty working implementation example in the snippet below.
Why you should not implement this?
Security risk. Somebody could hack the serialised functions with any arbitrary code.
Bad code design. Having objects without predefined constructors leads to maintenance hell, because you can't you javascript duck typing to make assumptions about object behavior.
Versioning. If you update you code, you can't be sure about which version of objects stored on clients.
let dog = {
name : '',
woof : function() {
console.log('woof woof, I am ' + this.name);
}
}
dog.name = 'Lovely';
dog.woof()
let storedDog = saveWithFuncToJSON(dog)
console.log('stored:', storedDog)
let restoredDog = restoreWithFuncFromJSON(storedDog)
restoredDog.woof()
console.log("Don't use this approach in production!")
// Code below is created only for fun,
// if you need this code in production,
// then your code smells
// Return JSON string
function saveWithFuncToJSON(object) {
// Use Object.assign() just for simplicity,
// something more solid needed for real object copying
let preparedObject = Object.assign({}, object)
for (let prop in preparedObject) {
if (typeof(preparedObject[prop]) !== 'function') continue
// Different platforms constructing function string in different ways
// so you'll have to put a lot of efforts to make it work stable
let funcStr = preparedObject[prop].toString()
let startParams = funcStr.indexOf('(') + 1
let endParams = funcStr.indexOf(')')
let hasParams = (endParams - startParams)
let funcParams = !hasParams ? [] : funcStr.slice(
funcStr.indexOf('(') + 1,
funcStr.indexOf('\n')
).split(',')
let funcBody = funcStr.slice(
funcStr.indexOf('{') + 1,
funcStr.lastIndexOf('}')
)
// This is the most interesting part
// We will store function as a string like freezing humans
preparedObject[`__${prop}Func`] = {
params: funcParams,
body: funcBody
}
}
return JSON.stringify(preparedObject)
}
function restoreWithFuncFromJSON(jsonSting) {
let object = JSON.parse(jsonSting)
for (let prop in object) {
// Functions to be restored should be named differently
let shouldConvertToFunc = prop.startsWith('__') && prop.endsWith('Func')
if (!shouldConvertToFunc) continue
let funcName = prop.slice(2, -4)
let funcData = object[prop]
let contructorArgsArray = funcData.params.concat([funcData.body])
// Function() does all work for us
object[funcName] = Function.apply(null, contructorArgsArray)
delete object[prop]
}
return object;
}

LocalStorage only supports strings. You can store objects with JSON-encoding, but that won't work for your functions.
JSON only supports numbers, strings, booleans, arrays, and basic object structures.

Related

Object literal with nested function as prototype of data Object

I'm creating a GAS Spreadsheets Service based app that reads/writes & updates a row of data. I have a key-value object that represents a row of data, like the example data provided in snippet.
Use case:
var exampleData = [{weekendVolume=5186270,midweekVolume=16405609}];
// tuple length 2 of two known values
function _DataRecordObject( exampleData ) {
this._endOfWeek = new Date().endOfWeek();// Date.prototype method
}
var _DataRecordMethods = {
weekEnding: function() {
return this._endOfWeek.formatDateString()
},
weekMonth: function() {
return this._endOfWeek.getMonthLabelShort()
},
/* Processed volume */
weekendVolume: function() {
return 'weekendVolume'
},
midweekVolume: function() {
return 'midweekVolume'
},
totalVolumeProcessed: function() {
return _SumTotal(
this.weekendVolume(),
this.midweekVolume()
)
}
}
_DataRecordObject.prototype = _DataRecordMethods;
The new DataRecordObject is prototype of a Sheet object that provides other helpful properties. _SumTotal is a helper function.
My question:
When I call a new DataRecordObject with sheet range as argument, how do I update the exampleData object with the new properties such as totalVolumeProcessed?
For example:
var foo = new _DataRecordObject( exampleData );
Console.log( foo );
//[{weekEnding='Aug-17',weekMonth=4,weekendVolume=5186270,midweekVolume=16405609,totalVolumeProcessed=21591879}]
I'd like the flexibility of using constructor-prototype inheritence, but using a boilerplate style template like Object-literals. My intuition suggests that I need to pass the data object keys when constructing a new dataRecordObject.
I'm a newcomer to JavaScript and have not yet gotten my head around inheritance, prototypes, and respective design-patterns. Factories and Modules, or perhaps Observers seem like appropriate patterns but my limited experience with JS is a limiting factor to solving my problem.
This might work for you.
1) Define the prototype as an object literal:
var methods = {
sayName: function() {
return "My name is " + this.name;
},
sayAge: function() {
return "I am " + this.age + " years old";
}
};
2) You can either make the 'methods' variable global or define it inside the following function. The function creates a new object using 'methods' variable as a prototype and populates it with values from the 'data' argument.
function createNewObj (data) {
var data = data || null;
var result = Object.create(methods);
for (var key in data) {
if (data.hasOwnProperty(key)) {
result[key] = data[key];
}
}
return result;
}
3) Bringing things together
function test() {
var data = {name: "John", age: "32"};
var row = createNewObj(data);
Logger.log(row.name); //logs 'John'
Logger.log(row.age); //logs '32'
Logger.log(row.sayName()); //logs 'My name is John'
Logger.log(row.sayAge()); //logs 'I am 32 years old'
Logger.log(Object.getPrototypeOf(row)); // returns contents of the 'methods' object literal
Logger.log(row.hasOwnProperty("sayName")); //logs 'false' because 'hasOwnProperty' doesn't go up the prototype chain
Logger.log("sayName" in row); //logs 'true' because 'in' goes up the chain
}
I suggest you take a look at this blog post by Yehuda Katz that dives deeper into prototypes http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/ It has examples of much cleaner code that might be helpful.
I've found a solution, which expands on #Anton-Dementiev 's response. His suggestion to read the Yehudi Katz was also most helpful.
The create new object function, _DataRecordObject is where the solution lies..
function _DataRecordObject( RowDataObject ) {
this._endOfWeek = new Date().endOfWeek();// Date.prototype method
var data = RowDataObject || null;
var result = Object.create( _DataRecordMethods );
for (var key in data) {
if ( data.hasOwnProperty( key ) ) {
// if value is present in the RowDataObject,
// then assign its value to the result
result[key] = data[key];
} else {
// if not, the value is a method function,
// which should be evaluated in that context,
// and then return the method value as result
var foo = Object.getPrototypeOf( result )[ key ];
result[key] = foo.call( data );
}
}
return result;
}
//simples
Since the methods are passed as property functions, they need to be called as functions in that context.

Get name of constructor in javascript

Is there a way to get the name of the variable made with a constructor?
var TestFunction = function () {
this.name = ???(); // should return '$testName'
}
var $testName = new TestFunction();
$testName.name should return $testName
THX
should return '$testName'
That means you're asking how a function can know the name of a variable its result (or rather, the result of new calling it) is about to be assigned to. It can't, there is no mechanism for that, not least because of these possibilities:
a = b = c = d = new TestFunction();
// or
new TestFunction();
// or
foo(new TestFunction());
...but really because fundamentally, the function has no business knowing anything about the context in which it's called other than what the programmer has chosen to tell it by passing as arguments.
Consequently, if you want the function to have that information, you'll need to pass it in, even though that's repetitive:
var $testName = new TestFunction("$testName");
There's a special case (variables at global scope) where you could avoid repeating the name and only pass it as an argument to the function (leaving off the var $testname = part) and then have the function create the "variable," but it would tie the function to global scope, which would fall deep, deep into Bad Idea™ territory. :-)
This is what the special case looks like. Strongly recommend not doing this. (Instead: squint had an excellent suggestion in a comment.)
// STRONGLY RECOMMEND NOT DOING THIS
function TestFunction(name) {
window[name] = this; // Creates a global
this.name =name;
}
new TestFunction("$testname");
console.log($testname); // {name: "$testname"}
That works because when you create a property on the global object (which you can access via window on browsers), it creates a global variable.
Please don't do that. :-)
Regarding squint's Proxy idea, it would look something like this:
// Requires ES2016 ("ES6") support in the browser
// Proxy cannot be shimmed, so transpiling won't help
const cookieStore = new Map(); // Our fake storage; you'd use the browser's actual cookie store
function cmAccessor(name, ...args) {
if (args.length == 0) {
// Getter; you'd actually use the browser store
const entry = cookieStore.get(name);
return entry && entry.value;
}
// Setter
const [value, duration] = args;
console.log(`Setting '${name}' to '${value}' for ${duration}`);
// You'd use the real browser store here
cookieStore.set(name, {value, duration});
}
const CM = new Proxy(Object.create(null), {
get(target, name) {
let result = target[name];
if (!result) {
result = cmAccessor.bind(null, name);
target[name] = result;
}
return result;
}
});
CM.cookie1("cookie1 value", 42);
CM.cookie2("cookie2 value", 42);
console.log(CM.cookie1());
console.log(CM.cookie2());
But you'd probably be better off just using a function, a'la jQuery:
// This version is ES5 compatible
const cookieStore = new Map(); // Our fake storage; you'd use the browser's actual cookie store
function CM(name, value, duration) {
switch (arguments.length) {
case 0:
throw new Error("'name' is required");
case 1:
// Getter
// You'd use the browser's real cookie store here
const entry = cookieStore.get(name);
return entry && entry.value;
default:
// Setter
console.log("Setting '" + name + "' to '" + value + "' for " + duration);
// You'd use the real cookie store here
cookieStore.set(name, {name: name, value: value});
}
}
// Usage:
CM("cookie1", "cookie1 value", 42);
CM("cookie2", "cookie2 value", 42);
console.log(CM("cookie1"));
console.log(CM("cookie2"));

Turning JSON strings into objects with methods

I have an app that allows users to generate objects, and store them (in a MySQL table, as strings) for later use. The object could be :
function Obj() {
this.label = "new object";
}
Obj.prototype.setLabel = function(newLabel) {
this.label = newLabel;
}
If I use JSON.stringify on this object, I will only get the information on Obj.label (the stringified object would be a string like {label: "new object"}. If I store this string, and want to allow my user to retrieve the object later, the setLabel method will be lost.
So my question is: how can I re-instantiate the object, so that it keeps the properties stored thanks to JSON.stringify, but also gets back the different methods that should belong to its prototype. How would you do that ? I was thinking of something along "create a blank object" and "merge it with the stored one's properties", but I can't get it to work.
To do this, you'll want to use a "reviver" function when parsing the JSON string (and a "replacer" function or a toJSON function on your constructor's prototype when creating it). See Section 15.12.2 and 15.12.3 of the specification. If your environment doesn't yet support native JSON parsing, you can use one of Crockford's parsers (Crockford being the inventor of JSON), which also support "reviver" functions.
Here's a simple bespoke example that works with ES5-compliant browsers (or libraries that emulate ES5 behavior) (live copy, run in Chrome or Firefox or similar), but look after the example for a more generalized solution.
// Our constructor function
function Foo(val) {
this.value = val;
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.toJSON = function() {
return "/Foo(" + this.value + ")/";
};
// An object with a property, `foo`, referencing an instance
// created by that constructor function, and another `bar`
// which is just a string
var obj = {
foo: new Foo(42),
bar: "I'm bar"
};
// Use it
display("obj.foo.value = " + obj.foo.value);
display("obj.foo.nifty = " + obj.foo.nifty);
display("obj.bar = " + obj.bar);
// Stringify it with a replacer:
var str = JSON.stringify(obj);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var obj2 = JSON.parse(str, function(key, value) {
if (typeof value === "string" &&
value.substring(0, 5) === "/Foo(" &&
value.substr(-2) == ")/"
) {
return new Foo(value.substring(5, value.length - 2));
}
return value;
});
// Use the result
display("obj2.foo.value = " + obj2.foo.value);
display("obj2.foo.nifty = " + obj2.foo.nifty);
display("obj2.bar = " + obj2.bar);
Note the toJSON on Foo.prototype, and the function we pass into JSON.parse.
The problem there, though, is that the reviver is tightly coupled to the Foo constructor. Instead, you can adopt a generic framework in your code, where any constructor function can support a fromJSON (or similar) function, and you can use just one generalized reviver.
Here's an example of a generalized reviver that looks for a ctor property and a data property, and calls ctor.fromJSON if found, passing in the full value it received (live example):
// A generic "smart reviver" function.
// Looks for object values with a `ctor` property and
// a `data` property. If it finds them, and finds a matching
// constructor that has a `fromJSON` property on it, it hands
// off to that `fromJSON` fuunction, passing in the value.
function Reviver(key, value) {
var ctor;
if (typeof value === "object" &&
typeof value.ctor === "string" &&
typeof value.data !== "undefined") {
ctor = Reviver.constructors[value.ctor] || window[value.ctor];
if (typeof ctor === "function" &&
typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value);
}
}
return value;
}
Reviver.constructors = {}; // A list of constructors the smart reviver should know about
To avoid having to repeat common logic in toJSON and fromJSON functions, you could have generic versions:
// A generic "toJSON" function that creates the data expected
// by Reviver.
// `ctorName` The name of the constructor to use to revive it
// `obj` The object being serialized
// `keys` (Optional) Array of the properties to serialize,
// if not given then all of the objects "own" properties
// that don't have function values will be serialized.
// (Note: If you list a property in `keys`, it will be serialized
// regardless of whether it's an "own" property.)
// Returns: The structure (which will then be turned into a string
// as part of the JSON.stringify algorithm)
function Generic_toJSON(ctorName, obj, keys) {
var data, index, key;
if (!keys) {
keys = Object.keys(obj); // Only "own" properties are included
}
data = {};
for (index = 0; index < keys.length; ++index) {
key = keys[index];
data[key] = obj[key];
}
return {ctor: ctorName, data: data};
}
// A generic "fromJSON" function for use with Reviver: Just calls the
// constructor function with no arguments, then applies all of the
// key/value pairs from the raw data to the instance. Only useful for
// constructors that can be reasonably called without arguments!
// `ctor` The constructor to call
// `data` The data to apply
// Returns: The object
function Generic_fromJSON(ctor, data) {
var obj, name;
obj = new ctor();
for (name in data) {
obj[name] = data[name];
}
return obj;
}
The advantage here being that you defer to the implementation of a specific "type" (for lack of a better term) for how it serializes and deserializes. So you might have a "type" that just uses the generics:
// `Foo` is a constructor function that integrates with Reviver
// but doesn't need anything but the generic handling.
function Foo() {
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.spiffy = "I'm the spiffy inherited property.";
Foo.prototype.toJSON = function() {
return Generic_toJSON("Foo", this);
};
Foo.fromJSON = function(value) {
return Generic_fromJSON(Foo, value.data);
};
Reviver.constructors.Foo = Foo;
...or one that, for whatever reason, has to do something more custom:
// `Bar` is a constructor function that integrates with Reviver
// but has its own custom JSON handling for whatever reason.
function Bar(value, count) {
this.value = value;
this.count = count;
}
Bar.prototype.nifty = "I'm the nifty inherited property.";
Bar.prototype.spiffy = "I'm the spiffy inherited property.";
Bar.prototype.toJSON = function() {
// Bar's custom handling *only* serializes the `value` property
// and the `spiffy` or `nifty` props if necessary.
var rv = {
ctor: "Bar",
data: {
value: this.value,
count: this.count
}
};
if (this.hasOwnProperty("nifty")) {
rv.data.nifty = this.nifty;
}
if (this.hasOwnProperty("spiffy")) {
rv.data.spiffy = this.spiffy;
}
return rv;
};
Bar.fromJSON = function(value) {
// Again custom handling, for whatever reason Bar doesn't
// want to serialize/deserialize properties it doesn't know
// about.
var d = value.data;
b = new Bar(d.value, d.count);
if (d.spiffy) {
b.spiffy = d.spiffy;
}
if (d.nifty) {
b.nifty = d.nifty;
}
return b;
};
Reviver.constructors.Bar = Bar;
And here's how we might test that Foo and Bar work as expected (live copy):
// An object with `foo` and `bar` properties:
var before = {
foo: new Foo(),
bar: new Bar("testing", 42)
};
before.foo.custom = "I'm a custom property";
before.foo.nifty = "Updated nifty";
before.bar.custom = "I'm a custom property"; // Won't get serialized!
before.bar.spiffy = "Updated spiffy";
// Use it
display("before.foo.nifty = " + before.foo.nifty);
display("before.foo.spiffy = " + before.foo.spiffy);
display("before.foo.custom = " + before.foo.custom + " (" + typeof before.foo.custom + ")");
display("before.bar.value = " + before.bar.value + " (" + typeof before.bar.value + ")");
display("before.bar.count = " + before.bar.count + " (" + typeof before.bar.count + ")");
display("before.bar.nifty = " + before.bar.nifty);
display("before.bar.spiffy = " + before.bar.spiffy);
display("before.bar.custom = " + before.bar.custom + " (" + typeof before.bar.custom + ")");
// Stringify it with a replacer:
var str = JSON.stringify(before);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var after = JSON.parse(str, Reviver);
// Use the result
display("after.foo.nifty = " + after.foo.nifty);
display("after.foo.spiffy = " + after.foo.spiffy);
display("after.foo.custom = " + after.foo.custom + " (" + typeof after.foo.custom + ")");
display("after.bar.value = " + after.bar.value + " (" + typeof after.bar.value + ")");
display("after.bar.count = " + after.bar.count + " (" + typeof after.bar.count + ")");
display("after.bar.nifty = " + after.bar.nifty);
display("after.bar.spiffy = " + after.bar.spiffy);
display("after.bar.custom = " + after.bar.custom + " (" + typeof after.bar.custom + ")");
display("(Note that after.bar.custom is undefined because <code>Bar</code> specifically leaves it out.)");
You can indeed create an empty instance and then merge the instance with the data. I recommend using a library function for ease of use (like jQuery.extend).
You had some errors though (function ... = function(...), and JSON requires keys to be surrounded by ").
http://jsfiddle.net/sc8NU/1/
var data = '{"label": "new object"}'; // JSON
var inst = new Obj; // empty instance
jQuery.extend(inst, JSON.parse(data)); // merge
Note that merging like this sets properties directly, so if setLabel is doing some checking stuff, this won't be done this way.
So far as I know, this means moving away from JSON; you're now customizing it, and so you take on all of the potential headaches that entails. The idea of JSON is to include data only, not code, to avoid all of the security problems that you get when you allow code to be included. Allowing code means that you have to use eval to run that code and eval is evil.
If you want to use the setters of Obj :
Obj.createFromJSON = function(json){
if(typeof json === "string") // if json is a string
json = JSON.parse(json); // we convert it to an object
var obj = new Obj(), setter; // we declare the object we will return
for(var key in json){ // for all properties
setter = "set"+key[0].toUpperCase()+key.substr(1); // we get the name of the setter for that property (e.g. : key=property => setter=setProperty
// following the OP's comment, we check if the setter exists :
if(setter in obj){
obj[setter](json[key]); // we call the setter
}
else{ // if not, we set it directly
obj[key] = json[key];
}
}
return obj; // we finally return the instance
};
This requires your class to have setters for all its properties.
This method is static, so you can use like this :
var instance = Obj.createFromJSON({"label":"MyLabel"});
var instance2 = Obj.createFromJSON('{"label":"MyLabel"}');
From ECMAScript 6 onwards you can just do:
Object.assign(new Obj(), JSON.parse(rawJsonString))
Note: You create a new empty object of the defined type first and then override its properties with the parsed JSON. Not the other way around.
The methods define behaviour and contain no variable data. They are "stored" as your code. So you don't actually have to store them in the database.
You would have to write your own stringify method that stores functions as properties by converting them to strings using the toString method.
JavaScript is prototype based programming language which is classless language where object orientation achieved by process of cloning existing objects that serve as prototypes.
Serializing JSON would be considering any methods, for instance if you have an object
var x = {
a: 4
getText: function() {
return x.a;
}
};
You will get just { a:4 } where getText method is skipped by the serializer.
I ran into this same trouble a year back and I had to maintain a separate helper class for each of my domain object and used $.extend() it to my deserialized object when need, just more like having methods to a base class for the domain objects.
Try to use toString on the method.
Update:
Iterate over the methods in obj and store them as string, and then instantiate them with new Function.
storedFunc = Obj.prototype.setLabel.toString();
Obj2.prototype['setLabel'] = new Function("return (" + storedFunc + ")")();

Dynamic function name in javascript?

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 + "(){})")

Adding Prototype to JavaScript Object Literal

STORE = {
item : function() {
}
};
STORE.item.prototype.add = function() { alert('test 123'); };
STORE.item.add();
I have been trying to figure out what's wrong with this quite a while. Why doesn't this work? However, it works when I use the follow:
STORE.item.prototype.add();
The prototype object is meant to be used on constructor functions, basically functions that will be called using the new operator to create new object instances.
Functions in JavaScript are first-class objects, which means you can add members to them and treat them just like ordinary objects:
var STORE = {
item : function() {
}
};
STORE.item.add = function() { alert('test 123'); };
STORE.item.add();
A typical use of the prototype object as I said before, is when you instantiate an object by calling a constructor function with the new operator, for example:
function SomeObject() {} // a constructor function
SomeObject.prototype.someMethod = function () {};
var obj = new SomeObject();
All the instances of SomeObject will inherit the members from the SomeObject.prototype, because those members will be accessed through the prototype chain.
Every function in JavaScript has a prototype object because there is no way to know which functions are intended to be used as constructors.
After many years, when JavaScript (ES2015 arrives) we have finally Object.setPrototypeOf() method
const STORE = {
item: function() {}
};
Object.setPrototypeOf(STORE.item, {
add: function() {
alert('test 123');
}
})
STORE.item.add();
You can use JSON revivers to turn your JSON into class objects at parse time. The EcmaScript 5 draft has adopted the JSON2 reviver scheme described at http://JSON.org/js.html
var myObject = JSON.parse(myJSONtext, reviver);
The optional reviver parameter is a
function that will be called for every
key and value at every level of the
final result. Each value will be
replaced by the result of the reviver
function. This can be used to reform
generic objects into instances of
pseudoclasses, or to transform date
strings into Date objects.
myData = JSON.parse(text, function (key, value) {
var type;
if (value && typeof value === 'object') {
type = value.type;
if (typeof type === 'string' && typeof window[type] === 'function') {
return new (window[type])(value);
}
}
return value;
});
As of this writing this is possible by using the __proto__ property. Just in case anyone here is checking at present and probably in the future.
const dog = {
name: 'canine',
bark: function() {
console.log('woof woof!')
}
}
const pug = {}
pug.__proto__ = dog;
pug.bark();
However, the recommended way of adding prototype in this case is using the Object.create. So the above code will be translated to:
const pug = Object.create(dog)
pug.bark();
Or you can also use Object.setPrototypeOf as mentioned in one of the answers.
Hope that helps.
STORE = {
item : function() {
}
};
this command would create a STORE object. you could check by typeof STORE;. It should return 'object'. And if you type STORE.item; it returns 'function ..'.
Since it is an ordinary object, thus if you want to change item function, you could just access its properties/method with this command.
STORE.item = function() { alert('test 123'); };
Try STORE.item; it's still should return 'function ..'.
Try STORE.item(); then alert will be shown.

Categories