read the name of the optional arguments of a function - javascript

I have a bunch of functions (methods of a class actually) and I'm writing a method that will record the interactions with other methods in an array.
so for example :
foo = Base.extend ({
a : function (a1,a2){
....
},
b:function(b1,b2){
...
},
history : function(){ ... }
})
to simplify the history method, I'd like to read the name of the optional arguments and add them to the array, so for example if the method a is called, I want to record a1,a2 ...
so basically, is there any way to read the name of the optional arguments list of an array in javascript ?
here is the code :
var element = Base.extend({
constructor : function() {
if(arguments.length==1){
...
}else{
...
}
},
setLocation : function (top, left){
oldArgs = [this.top,this.left];
this.top = top;
this.left = left;
return(oldArgs);
},
setAspects : function (width, height){
oldArgs = [this.width,this.height]
this.width = width;
this.height = height;
return(oldArgs);
},
draw : function (page){
...
},
delet : function () {
...
},
$ : function(method,args){
var oldArgs = this[method].apply(this,args);
this.history(method,oldArgs);
Nx.pages[this.page].modified = true;
},
history : function (method,args){
Nx.history[Nx.history.length]=[this.id,method,args]
}
})
so in this class, if I want to call any method, I'll pas it through the $ method, and it will call the history method, so far what I've done is for example in the setLocation method it will return the old arguments and I will story them in my array Nx.history, but it's easier to factorise all of these "return" calls in the methods, and add a line to the $ method , that reads the name of the expected arguments of the method, and send it to the history method, so something like this :
$ : function(method,args){
this[method].apply(this,args);
**var oldArgs = this[method].arguments // get the list of argument names here
$.each(oldArgs, function(value) { Args[Args.length] = this.value //get the stored value in the class
})
this.history(method,Args); // and pass it to the history**
Nx.pages[this.page].modified = true;
}

I'm not 100% sure what you're asking for - a way to extract the formal parameter names of a function?
I have no idea if this would work or not, but could you parse the string representation of the function to extract the parameter names?
It would probably be a very lame solution, but you might be able to do something like:
function getArgNames(fn) {
var args = fn.toString().match(/function\b[^(]*\(([^)]*)\)/)[1];
return args.split(/\s*,\s*/);
}

2.0
The idea with this newer version is to define the properties that you want to record for the object beforehand. It's a level of duplication, but it's only a one time thing. Then, in the constructor, create property setters for each of these properties. The setter does some side work along with setting the property. It pushes the arguments name and value onto a stack, and assigns the properties. The $ method is supposed to call dispatch the call to the appropriate method. Once the call is complete, the stack will be populated with the parameters that were set in that function. Pop off each parameter from that stack, until the stack is empty. Then call history with the method name, and the parameters that we just popped off the stack. Please let me know if this doesn't make any sense, I might have to word it better.
See an example here.
Here's a code example written in MooTools which is slightly similar to your Base class.
var Device = new Class({
_properties: ['top', 'left', 'width', 'height'],
_parameterStack: [],
initialize: function() {
this._createPropertyAccessors();
},
_createPropertyAccessors: function() {
this._properties.each(function(property) {
Object.defineProperty(this, property, {
enumerable: true,
configurable: true,
set: function(value) {
var o = {};
o[property] = value;
// push the parameter onto the stack
this._parameterStack.push(o);
}.bind(this)
});
}.bind(this));
},
// method stays unchanged
setLocation: function(top, left) {
this.top = top;
this.left = left;
},
setAspects: function(width, height) {
this.width = width;
this.height = height;
},
// dispatches call to method
// pops off the entire stack
// passed method name, and emptied stack arguments to history
$: function(method, args) {
this[method].apply(this, args);
var argsStack = [];
while(this._parameterStack.length) {
argsStack.push(this._parameterStack.pop());
}
this.history(method, argsStack);
},
history: function(method, args) {
console.log("%s(%o) called", method, args);
}
});
1.0
The arguments passed to a JavaScript function are accessible through an array-like object named arguments which is available for all functions.
When calling history, pass the arguments object to it.
a: function(a1, a2) {
this.history.apply(this, arguments);
}
history will then be invoked as if it was called with two arguments with this being the base object - foo unless you call it with a different context.
I am not sure how Base plays into this. You would have to elaborate more as to the role of Base here.
Here' s a simple example:
var foo = {
a: function(a1, a2) {
this.history.apply(this, arguments);
},
history: function() {
console.log("history received " + arguments.length + " arguments.");
}
};
foo.a("hello", "world"); // history received 2 arguments
Also note that although a has two named parameters here, we can still pass it any number of arguments, and all of them will be passed to the history method in turn. We could call a as:
foo.a(1, 2, 3); // history received 3 arguments

Something like this should work. When you want to access the arguments that have been used you can loop through the foo.history array which contains the arguments list.
var foo = {
storeArgs: function (fn) {
return function() {
this.history += arguments
return fn.apply(null, arguments);
}
},
a: storeArgs(function(a1, a2) {
alert(a1+a2);
}),
history: []
};
I read your updated post. What do you mean by "names?" Variable names? For example, if someone called a(1337), would you want ["a1"] to be added to the array? And if a(1337, 132) was called ["a1", "a2"] would be added? I don't think there's any sane way to do that.
This is the best I can do. You will have to include a list of parameter names when defining your functions using the storeArgs function.
var foo = {
storeArgs: function (params, fn) {
return function() {
var arr = [];
for (var i = 0; i < arguments.length; i++) {
arr.push(params[i]);
}
this.history += arr;
return fn.apply(null, arguments);
}
}
a: storeArgs(["a1", "a2"], function(a1, a2) {
alert(a1+a2);
}),
history: []
};
Let me know if it works.

This works in IE, but I'm not sure about other browsers...
Function.prototype.params = function() {
var params = this.toString().split(/(\r\n)|(\n)/g)[0];
params = params.trim().replace(/^function.*?\(/, "");
params = params.match(/(.*?)\)/)[1].trim();
if (!params) {
return([]);
}
return(params.split(/, /g));
};
function Test(a, b) {
alert(Test.params());
}
Test();

Related

Property of object is not a function

Why do I get this error message Property 'shortDescr' of object #<Article> is not a function
function Article() {
this.id = null;
this.title = null;
this.url = null;
this.descr = null;
this.media = null;
};
Article.prototype.shortDescr = function () {
if ( this.descr.length > 100) {
return this.descr.substring(0,80) + "..";
} else {
return this.descr;
}
};
var ArticleFactory = {
numOfArgs : 5,
inputCheck : function(args) {
if (args.length != this.numOfArgs) {
throw new Error("Invalid number of arguments for class `Article`");
};
return true;
},
//Fill the properties with values from arguments
create : function() {
this.inputCheck(arguments);
var counter = 0;
var article = new Article();
for(propertie in article) {
article[propertie] = arguments[counter++];
}
return article;
}
};
var descr = "#hughes it actually can do both. i have an object i created with: var obj = and another object that is being passed into a callback from a server, the one passed through the callback prints with the little arrow so you can open it up, the statically created one just prints [object Object] with no arrow. ";
var article = ArticleFactory.create(1,"title","url",descr,{});
console.log(article.shortDescr());
Addendum
console.log(JSON.stringify(article, null, 4));
{
"id": 1,
"title": "title",
"url": "url",
"descr": "#hughes it actually can do both. i have an object i created with: var obj = and another object that is being passed into a callback from a server, the one passed through the callback prints with the little arrow so you can open it up, the statically created one just prints [object Object] with no arrow. ",
"media": {} }
Proof
#dystroy was right.
You're shadowing the function here :
for(propertie in article) {
article[propertie] = arguments[counter++];
}
More precisely, you iterate over the property names (including the ones of the prototype chain) and you set new values to your object. When you set a value with the name of a property of the prototype, you don't change the prototype but the value that will be found with article.shortDescr will be the one of the object, not the one of the prototype.
What you do is kind of blind (you don't even have any guarantee on the order of properties) so I would recommend to change your design on this point (how ? I can't say as I really don't get the purpose).
But if you want to keep it, you may skip the prototype properties by testing using hasOwnProperty.

How to properly derive object with private vars using javascript (prototypal) inheritance

I am new to JavaScript's (prototypal) inheritance and I'm trying to learn more about it.
I am using a simple observer pattern as example, in which I want observable objects to be derived from the 'subject' object. This is what I WANT to do:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
var trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
list.prototype = new subject()
function list()
{
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger('add', name);
}
this.remove = function(name)
{
delete items[name];
trigger('remove', name);
}
}
Now when using the code above like below, I run into my first problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- ReferenceError: trigger is not defined, trigger('add', name);
To continue testing I made the trigger function 'public' using this.trigger instead. Running my example again I run into the next problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The this object is subject, I want it to be list. My third problem occurs when creating another list:
var l2 = new list();
//Don;t register any observers
l2.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The callbacks list is shared between the 2 lists.
I've tried similar things with Object.create(new subject()) as well and run into similar problems.
My 3 questions in this are:
Can I have private methods that can be used in derived objects (and
should I even care about having them private or public)?
How can I have the this object I want (without needing to use function.call in the derived object, if possible)?
How can I keep the callbacks list in the base object without it being shared?
An interesting question. As for #1 and #2: let's say you have a function foo:
function foo() {
var _private = 'private var!';
this.access = function () {
return _private;
}
}
access is a so-called privileged method, it's a closure that can access the private variable private.
you can inherit the whole thing by making use of call, like so:
function bar() {
foo.call(this);
}
var b = new bar();
console.log(b.output()); // prints 'private var!'
With the methods apply, call and bind you can establish the context of a function, effectively tamper with the this object. (your #2 question, read here )
Naturally you cannot make use of a totally private method in a derived object. You'd need an accessor method which would defeat the purpose of the original method being private. Having said that, that's the way it works in strongly typed languages too (in java if you mark a method as private not even subclases will be able to access it, it would have to be protected).
As for #3, I cannot think of how to keep callbacks shared and private.
But you can make it a static property for all instances of a function (much like a static property in a lanaguage like java) by simply declaring a function like:
function foo() {
}
add your prototypes which will be assigned to each instance
foo.prototype.bar = // ...
and a static property
foo.callbacks = [];
All instances of foo will share the callbacks property.
You can’t have private methods, and that’s that. It will never work both properly and nicely at the same time, so don’t bother trying to emulate them in JavaScript.
Then all you have to do is call the parent’s constructor in the derived constructor.
function subject()
{
var callbacks = {};
this.register = function(name, callback)
{
callbacks[name] = callback;
};
this.unregister = function(name)
{
delete callbacks[name];
};
this.trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
};
}
list.prototype = Object.create(subject);
list.prototype.constructor = list;
function list()
{
subject.call(this);
var items = {};
this.add = function(name, item)
{
items[name] = item;
this.trigger('add', name);
};
this.remove = function(name)
{
delete items[name];
this.trigger('remove', name);
};
}
Incorporating Joe's suggestion, this is what I eventually ended up with:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
//without the following line, 'this' in firefox is 'subject' instead of 'list' (in chrome it is)
list.prototype = new subject()
//without these, 'list' is not an instanceof 'subject'
list.constructor = subject;
list.prototype.constructor = list;
function list(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger.call(this, 'add', name); //no way to do this without using call/apply
}
this.remove = function(name)
{
delete items[name];
trigger.call(this, 'remove', name); //no way to do this without using call/apply
}
this.getitems = function() { return items }
}
//without the following line, 'this' in firefox is 'subject' instead of 'queue'
queue.prototype = new subject()
//without these, 'queue' is not an instanceof 'subject'
queue.constructor = subject;
queue.prototype.constructor = queue;
function queue(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = [];
this.enqueue = function(item)
{
items.push(item);
trigger.call(this, 'enqueue', item); //no way to do this without using call/apply
}
this.dequeue = function()
{
var d = items.shift();
trigger.call(this, 'dequeue', d); //no way to do this without using call/apply
return d;
}
this.getitems = function() { return items }
}
var l1 = new list('l1')
l1.register('observer1', function() { console.log('l1', this, arguments) });
l1.add('item1', 'value1');
// ^ 'l1', list { name = 'l1' ... }, ['add', 'item1']
var l2 = new list('l2')
l2.register('observer2', function() { console.log('l2', this, arguments) });
l2.add('item2', 'value2');
// ^ 'l2', list { name = 'l2' ... }, ['add', 'item2']
var q1 = new queue('q1')
q1.register('observer3', function() { console.log('q1', this, arguments) });
q1.enqueue('item3');
// ^ 'q1', queue { name = 'q1' ... }, ['enqueue', 'item3']
console.log(l1 instanceof list, l1 instanceof subject, l1 instanceof queue);
// ^ true, true, false
console.log(q1 instanceof list, q1 instanceof subject, q1 instanceof queue);
// ^ false, true, true
This ticks all of my boxes (except for the use of call, but I can live with that).
Thanks for all the help,
Mattie
EDIT: appearantly this does not work as expected. creating a new object overwrites the other objects callbacks

Is this kind of chaining possible in JavaScript?

I'm working on a file manager framework similar to elFinder. My current code works fine but now I want to make it look better and add chaining (I'm not sure if it's chaining or decorator pattern).
Here is a sample of what I want to do:
function UI() {}
UI.prototype.folders = function(){
return [];
}
UI.prototype.folders.prototype.getSelectedFolder = function(){
return {};
}
Calling UI.folders() should return an array of folder objects. So if you call UI.folders() you would get something similar to this:
[
Object { name="folder1", selected=false },
Object { name="folder2", selected=false },
Object { name="folder3", selected=true }
]
And calling UI.folders().getSelectedFolder() would filter the results from UI.folders() and will return:
Object { name="folder3", selected=true }
Is this possible? Is it right to say "chaining" in this case or it's "decorative pattern"?
If it's not - is there another more appropriate way to do it?
Any help wold be really appreciated!
The code in your question isn't reflective of a proper implementation, but to answer your direct questions, yes, this...
UI.folders().getSelectedFolder()
...would be an example of method chaining.
A decorator pattern is different. If you have a set of methods, and each one should always first invoke some common function, you can create a decorator that will return a function that first calls the common one, then the actual one...
function foo() {
console.log('I\'m foo, and I\'m first, and I was given these args:', arguments);
}
function decorateWithFoo(decorated) {
return function () {
foo.apply(this, arguments);
decorated.apply(this, arguments);
};
}
So you can use decorateWithFoo to create a function that always invokes foo first...
// create and decorate bar()
var bar = function(a,b) {
console.log('I\'m bar, and I was called after "foo", and was given args:', a, b);
};
bar = decorateWithFoo(bar);
bar(123, 456); // this will first call `foo()`, then (the original) `bar()`.
// create and decorate baz()
var baz = function(a,b) {
console.log('I\'m baz, and I was called after "foo", and was given args:', a, b);
};
baz = decorateWithFoo(baz);
baz(123, 456); // this will first call `foo()`, then (the original) `baz()`.
Some languages have built in syntax for creating decorators. JavaScript currently does not.
If you find yourself using decorators in different ways, you could create another function that sets up the initial decorator function...
function generateDecorator(decorator) {
return function (decorated) {
return function () {
decorator.apply(this, arguments);
decorated.apply(this, arguments);
};
};
}
So our original decoreateWithFoo could have been set up like this...
function foo() {
console.log('I\'m foo, and I\'m first, and I was given these args:', arguments);
}
var decorateWithFoo = generateDecorator(foo);
To make this work properly, you need to make your folders method be a function that returns an object that inherits from an array.:
UI.prototype.folders = function(){
// must return an object that inherits from an array
// that has the additional methods on it you want like getSelectedFolder()
}
The are a few different ways to solve this. The primary goal is that when you call a function you get an object/function back that is the same type of object with different properties. I'm not a fan of the prototype usage so I would do it like this (this is one way to solve it):
var FolderList = function ()
{
var _folders = [];
folders.pop({ name: "folder1", selected: false });
folders.pop({ name: "folder2", selected: true });
folders.pop({ name: "folder3", selected: false });
// prevent other programers from changing _folders
// which would break this, so just use a function
this.folders = function ()
{
return _folders;
}
this.selectedFolders = function ()
{
var tmpFolders = [];
for (var folderIndex = 0;
folderIndex < this._folders.length;
folderIndex++)
{
if (this._folders[folderIndex].selected)
{
tmpFolders.pop(_folders[folderIndex]);
}
}
_folders = tmpFolders;
return this;
}
this.addFolder = function (folder)
{
_folders.pop(folder);
return this;
}
};
var folderList = new FolderList();
folderList.selectedFolders()
.addFolder({ name: "folder1", selected: false })
.addFolder({ name: "folder3", selected: true })
.selectedFolders();
// array of 2 objects, folder2 and folder3
var arrayOfSelectedFolder = folderList.folders();

Resume from an error

Before I get yelled at for trying something so reckless, let me tell you that I wouldn't do this in real life and it's an academic question.
Suppose I'm writing a library and I want my object to be able to make up methods as they are needed.
For example if you wanted to call a .slice() method, and I didn't have one then the window.onerror handler would fire it for me
Anyway I played around with this here
window.onerror = function(e) {
var method = /'(.*)'$/.exec(e)[1];
console.log(method); // slice
return Array.prototype[method].call(this, arguments); // not even almost gonna work
};
var myLib = function(a, b, c) {
if (this == window) return new myLib(a, b, c);
this[1] = a; this[2] = b; this[3] = c;
return this;
};
var obj = myLib(1,2,3);
console.log(obj.slice(1));
Also (maybe I should start a new question) can I change my constructor to take an unspecified amount of args?
var myLib = function(a, b, c) {
if (this == window) return new myLib.apply(/* what goes here? */, arguments);
this[1] = a; this[2] = b; this[3] = c;
return this;
};
BTW I know I can load my objects with
['slice', 'push', '...'].forEach(function() { myLib.prototype[this] = [][this]; });
That's not what I'm looking for
As you were asking an academic question, I suppose browser compatibility is not an issue. If it's indeed not, I'd like to introduce harmony proxies for this. onerror is not a very good practice as it's just a event raised if somewhere an error occurs. It should, if ever, only be used as a last resort. (I know you said you don't use it anyway, but onerror is just not very developer-friendly.)
Basically, proxies enable you to intercept most of the fundamental operations in JavaScript - most notably getting any property which is useful here. In this case, you could intercept the process of getting .slice.
Note that proxies are "black holes" by default. They do not correspond to any object (e.g. setting a property on a proxy just calls the set trap (interceptor); the actual storing you have to do yourself). But there is a "forwarding handler" available that routes everything through to a normal object (or an instance of course), so that the proxy behaves as a normal object. By extending the handler (in this case, the get part), you can quite easily route Array.prototype methods through as follows.
So, whenever any property (with name name) is being fetched, the code path is as follows:
Try returning inst[name].
Otherwise, try returning a function which applies Array.prototype[name] on the instance with the given arguments to this function.
Otherwise, just return undefined.
If you want to play around with proxies, you can use a recent version of V8, for example in a nightly build of Chromium (make sure to run as chrome --js-flags="--harmony"). Again, proxies are not available for "normal" usage because they're relatively new, change a lot of the fundamental parts of JavaScript and are in fact not officially specified yet (still drafts).
This is a simple diagram of how it goes like (inst is actually the proxy which the instance has been wrapped into). Note that it only illustrates getting a property; all other operations are simply passed through by the proxy because of the unmodified forwarding handler.
The proxy code could be as follows:
function Test(a, b, c) {
this[0] = a;
this[1] = b;
this[2] = c;
this.length = 3; // needed for .slice to work
}
Test.prototype.foo = "bar";
Test = (function(old) { // replace function with another function
// that returns an interceptor proxy instead
// of the actual instance
return function() {
var bind = Function.prototype.bind,
slice = Array.prototype.slice,
args = slice.call(arguments),
// to pass all arguments along with a new call:
inst = new(bind.apply(old, [null].concat(args))),
// ^ is ignored because of `new`
// which forces `this`
handler = new Proxy.Handler(inst); // create a forwarding handler
// for the instance
handler.get = function(receiver, name) { // overwrite `get` handler
if(name in inst) { // just return a property on the instance
return inst[name];
}
if(name in Array.prototype) { // otherwise try returning a function
// that calls the appropriate method
// on the instance
return function() {
return Array.prototype[name].apply(inst, arguments);
};
}
};
return Proxy.create(handler, Test.prototype);
};
})(Test);
var test = new Test(123, 456, 789),
sliced = test.slice(1);
console.log(sliced); // [456, 789]
console.log("2" in test); // true
console.log("2" in sliced); // false
console.log(test instanceof Test); // true
// (due to second argument to Proxy.create)
console.log(test.foo); // "bar"
The forwarding handler is available at the official harmony wiki.
Proxy.Handler = function(target) {
this.target = target;
};
Proxy.Handler.prototype = {
// Object.getOwnPropertyDescriptor(proxy, name) -> pd | undefined
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getPropertyDescriptor(proxy, name) -> pd | undefined
getPropertyDescriptor: function(name) {
var desc = Object.getPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getOwnPropertyNames(proxy) -> [ string ]
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(this.target);
},
// Object.getPropertyNames(proxy) -> [ string ]
getPropertyNames: function() {
return Object.getPropertyNames(this.target);
},
// Object.defineProperty(proxy, name, pd) -> undefined
defineProperty: function(name, desc) {
return Object.defineProperty(this.target, name, desc);
},
// delete proxy[name] -> boolean
delete: function(name) { return delete this.target[name]; },
// Object.{freeze|seal|preventExtensions}(proxy) -> proxy
fix: function() {
// As long as target is not frozen, the proxy won't allow itself to be fixed
if (!Object.isFrozen(this.target)) {
return undefined;
}
var props = {};
Object.getOwnPropertyNames(this.target).forEach(function(name) {
props[name] = Object.getOwnPropertyDescriptor(this.target, name);
}.bind(this));
return props;
},
// == derived traps ==
// name in proxy -> boolean
has: function(name) { return name in this.target; },
// ({}).hasOwnProperty.call(proxy, name) -> boolean
hasOwn: function(name) { return ({}).hasOwnProperty.call(this.target, name); },
// proxy[name] -> any
get: function(receiver, name) { return this.target[name]; },
// proxy[name] = value
set: function(receiver, name, value) {
this.target[name] = value;
return true;
},
// for (var name in proxy) { ... }
enumerate: function() {
var result = [];
for (var name in this.target) { result.push(name); };
return result;
},
// Object.keys(proxy) -> [ string ]
keys: function() { return Object.keys(this.target); }
};

How to dynamically set a function/object name in Javascript as it is displayed in Chrome

This is something which has been bugging me with the Google Chrome debugger and I was wondering if there was a way to solve it.
I'm working on a large Javascript application, using a lot of object oriented JS (using the Joose framework), and when I debug my code, all my classes are given a non-sensical initial display value. To see what I mean, try this in the Chrome console:
var F = function () {};
var myObj = new F();
console.log(myObj);
The output should be a single line which you can expand to see all the properties of myObj, but the first thing you see is just ▶ F.
My issue is that because of my OO framework, every single object instantiated gets the same 'name'. The code which it looks is responsible for this is like so:
getMutableCopy : function (object) {
var f = function () {};
f.prototype = object;
return new f();
}
Which means that in the debugger, the initial view is always ▶ f.
Now, I really don't want to be changing anything about how Joose instantiates objects (getMutableCopy...?), but if there was something I could add to this so that I could provide my own name, that would be great.
Some things that I've looked at, but couldn't get anywhere with:
> function foo {}
> foo.name
"foo"
> foo.name = "bar"
"bar"
> foo.name
"foo" // <-- looks like it is read only
Object.defineProperty(fn, "name", { value: "New Name" });
Will do the trick and is the most performant solution. No eval either.
I've been playing around with this for the last 3 hours and finally got it at least somewhat elegant using new Function as suggested on other threads:
/**
* JavaScript Rename Function
* #author Nate Ferrero
* #license Public Domain
* #date Apr 5th, 2014
*/
var renameFunction = function (name, fn) {
return (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
/**
* Test Code
*/
var cls = renameFunction('Book', function (title) {
this.title = title;
});
new cls('One Flew to Kill a Mockingbird');
If you run the above code, you should see the following output to your console:
Book {title: "One Flew to Kill a Mockingbird"}
Combine usage of computed property name to dynamically name a property, and inferred function naming to give our anonymous function that computed property name:
const name = "aDynamicName"
const tmp = {
[name]: function(){
return 42
}
}
const myFunction= tmp[name]
console.log(myFunction) //=> [Function: aDynamicName]
console.log(myFunction.name) //=> 'aDynamicName'
One could use whatever they want for 'name' here, to create a function with whatever name they want.
If this isn't clear, let's break down the two pieces of this technique separately:
Computed Property Names
const name = "myProperty"
const o = {
[name]: 42
}
console.log(o) //=> { myProperty: 42 }
We can see that the property name assigned on o was myProperty, by way of computed property naming. The []'s here cause JS to lookup the value inside the bracket, and to use that for the property name.
Inferred Function Naming
const o = {
myFunction: function(){ return 42 }
}
console.log(o.myFunction) //=> [Function: myFunction]
console.log(o.myFunction.name) //=> 'myFunction'
Here we use inferred function naming. The language looks at the name of wherever the function is being assigned to, & gives the function that inferred name.
We can combine these two techniques, as shown in the beginning. We create an anonymous function, which gets it's name via inferred function naming, from a computed property name, which is the dynamic name we wanted to create. Then we have to extract the newly created function from the object it is embedded inside of.
Example Using Stack Trace
Naming a supplied anonymous function
// Check the error stack trace to see the given name
function runAnonFnWithName(newName, fn) {
const hack = { [newName]: fn };
hack[newName]();
}
runAnonFnWithName("MyNewFunctionName", () => {
throw new Error("Fire!");
});
Although it is ugly, you could cheat via eval():
function copy(parent, name){
name = typeof name==='undefined'?'Foobar':name;
var f = eval('function '+name+'(){};'+name);
f.prototype = parent;
return new f();
}
var parent = {a:50};
var child = copy(parent, 'MyName');
console.log(child); // Shows 'MyName' in Chrome console.
Beware: You can only use names which would be valid as function names!
Addendum: To avoid evaling on every object instantiation, use a cache:
function Cache(fallback){
var cache = {};
this.get = function(id){
if (!cache.hasOwnProperty(id)){
cache[id] = fallback.apply(null, Array.prototype.slice.call(arguments, 1));
}
return cache[id];
}
}
var copy = (function(){
var cache = new Cache(createPrototypedFunction);
function createPrototypedFunction(parent, name){
var f = eval('function '+name+'(){};'+name);
f.prototype = parent;
return f;
}
return function(parent, name){
return new (cache.get(name, parent, typeof name==='undefined'?'Foobar':name));
};
})();
This won't totally solve your problem, but I would suggest overriding the toString method on the class's prototype. For instance:
my_class = function () {}
my_class.prototype.toString = function () { return 'Name of Class'; }
You'll still see the original class name if you enter an instance of my_class directly in the console (I don't think it's possible to do anything about this), but you'll get the nice name in error messages, which I find very helpful. For instance:
a = new my_class()
a.does_not_exist()
Will give the error message: "TypeError: Object Name of Class has no method 'does_not_exist'"
If you want to dynamically create a named function. You can use new Function to create your named function.
function getMutableCopy(fnName,proto) {
var f = new Function(`function ${fnName}(){}; return ${fnName}`)()
f.prototype = proto;
return new f();
}
getMutableCopy("bar",{})
// ▶ bar{}
Similar to #Piercey4 answer, but I had to set the name for the instance as well:
function generateConstructor(newName) {
function F() {
// This is important:
this.name = newName;
};
Object.defineProperty(F, 'name', {
value: newName,
writable: false
});
return F;
}
const MyFunc = generateConstructor('MyFunc');
const instance = new MyFunc();
console.log(MyFunc.name); // prints 'MyFunc'
console.log(instance.name); // prints 'MyFunc'
normally you use window[name] like
var name ="bar";
window["foo"+name] = "bam!";
foobar; // "bam!"
which would lead you to a function like:
function getmc (object, name) {
window[name] = function () {};
window[name].prototype = object;
return new window[name]();
}
but then
foo = function(){};
foobar = getmc(foo, "bar");
foobar; // ▶ window
foobar.name; // foo
x = new bar; x.name; // foo .. not even nija'ing the parameter works
and since you can't eval a return statement (eval("return new name()");), I think you're stuck
I think this is the best way to dynamically set the name of a function :
Function.prototype.setName = function (newName) {
Object.defineProperty(this,'name', {
get : function () {
return newName;
}
});
}
Now you just need to call the setName method
function foo () { }
foo.name; // returns 'foo'
foo.setName('bar');
foo.name; // returns 'bar'
foo.name = 'something else';
foo.name; // returns 'bar'
foo.setName({bar : 123});
foo.name; // returns {bar : 123}
Based on the answer of #josh, this prints in a console REPL, shows in console.log and shows in the debugger tooltip:
var fn = function() {
return 1917;
};
fn.oldToString = fn.toString;
fn.toString = function() {
return "That fine function I wrote recently: " + this.oldToString();
};
var that = fn;
console.log(that);
Inclusion of fn.oldToString() is a magic which makes it work. If I exclude it, nothing works any more.
With ECMAScript2015 (ES2015, ES6) language specification, it is possible to dynamically set a function name without the use of slow and unsafe eval function and without Object.defineProperty method which both corrupts function object and does not work in some crucial aspects anyway.
See, for example, this nameAndSelfBind function that is able to both name anonymous functions and renaming named functions, as well as binding their own bodies to themselves as this and storing references to processed functions to be used in an outer scope (JSFiddle):
(function()
{
// an optional constant to store references to all named and bound functions:
const arrayOfFormerlyAnonymousFunctions = [],
removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
// this function both names argument function and makes it self-aware,
// binding it to itself; useful e.g. for event listeners which then will be able
// self-remove from within an anonymous functions they use as callbacks:
function nameAndSelfBind(functionToNameAndSelfBind,
name = 'namedAndBoundFunction', // optional
outerScopeReference) // optional
{
const functionAsObject = {
[name]()
{
return binder(...arguments);
}
},
namedAndBoundFunction = functionAsObject[name];
// if no arbitrary-naming functionality is required, then the constants above are
// not needed, and the following function should be just "var namedAndBoundFunction = ":
var binder = function()
{
return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
}
// this optional functionality allows to assign the function to a outer scope variable
// if can not be done otherwise; useful for example for the ability to remove event
// listeners from the outer scope:
if (typeof outerScopeReference !== 'undefined')
{
if (outerScopeReference instanceof Array)
{
outerScopeReference.push(namedAndBoundFunction);
}
else
{
outerScopeReference = namedAndBoundFunction;
}
}
return namedAndBoundFunction;
}
// removeEventListener callback can not remove the listener if the callback is an anonymous
// function, but thanks to the nameAndSelfBind function it is now possible; this listener
// removes itself right after the first time being triggered:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
e.target.removeEventListener('visibilitychange', this, false);
console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
'\n\nremoveEventListener 1 was called; if "this" value was correct, "'
+ e.type + '"" event will not listened to any more');
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
// name -- belong to different scopes and hence removing one does not mean removing another,
// a different event listener is added:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
// formerly anonymous callback function of one of the event listeners, an attempt to remove
// it is made:
setTimeout(function(delay)
{
document.removeEventListener('visibilitychange',
arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
false);
console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed; if reference in '
+ 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
+ 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
}, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
})();
I have not seen anyone mention the use of ES6 Proxies. Which in my opinion solve this problem beautifully. So here it is.
function shadow(object, secondObject) {
return new Proxy(object, {
get(target, prop, receiver) {
if (secondObject.hasOwnProperty(prop)) return secondObject[prop];
return Reflect.get(...arguments);
}
})
}
let t=function namedFunction(a,b,c){return a+b+c;}
console.log(t.name)//read only property
let f=shadow(t,{name:"addition"})
console.log(f.name)

Categories