Javascript change function arguments - javascript

I'm trying to change function arguments in javascript.
f = function(){
console.log(a,b,c);
};
SetArgumentList( f, ["a", "b", "c"] );
f(1,2,3);
// should print "1 2 3"
// [edit]
// alternatively SetArgumentList could also work like
f = SetArgumentList( f, ["a", "b", "c"] );
Is there some solid way of doing this?
Where do I need it?... basically I'm trying to add type checked functions:
Object.prototype.method = function( name, typedef, func ){ ... }
function Thing(){};
Thing.method("log",
{ arr: Array, str: String },
function(){
console.log(arr, str);
});
t = new Thing();
t.log([1,2,3], "ok");
t.log("err", "ok"); // <-- causes an exception
// I know I can do this way
Thing.method("log",
[Array, String],
function(arr, str){
console.log(arr, str);
});
// but that's harder to read
NOTE! I know how to do type checking, but not the new function construction.

As delnan said in the comments, it seems like what you're trying to do is essentially "rename" the variables which are local to a function. Like he said, this is not possible (and for good reason too! Could you imagine debugging that? maaan...)
Anyway, I don't know exactly why you'd want that, but Javascript is a flexible language and you could probably get close using a more sane method. It's hard to know exactly what you're trying to achieve, but perhaps this information might get you on the right track:
The arguments which are passed to a function at call time are referenced in a variable named arguments.
function f() {
console.log(arguments);
}
f(); // []
f(1, 2); // [1, 2]
You can call a function with an arbitrary list of arguments using .apply, which is a method on the Function prototype. It takes 2 parameters. The first is the object which will be this inside the function call, and the second is an array of arguments.
f.apply(null, []); // []
f.apply(null, [1, 2, 3]); [1, 2, 3]
Applying this in your situation, perhaps this is what you're after:
function f() {
console.log.apply(console, arguments);
}

Tested in IE7,8,9, opera, chrome, firefox and safari. Uses evil in the background, but
I cannot see any other way if you must rename arguments.
(function(){
var decompileRE = /function\s*\([\s\S]*?\)\s*\{([\s\S]*)/,
lastBrace = /\}[^}]*$/;
window.SetArgumentList = function( fn, argNames ) {
var match
if( !( match = fn.toString().match( decompileRE ) ) ) {
return fn;
}
argNames.push( match[1].replace( lastBrace, "" ) );
return Function.apply( null, argNames );
};
})()
f = function(){
console.log(a,b,c);
};
f = SetArgumentList( f, ["a","b","c"] );
console.log(f);
Logs this in all browsers mentioned above:
function anonymous(a,b,c) {
console.log(a,b,c);
}

I've got a simpler solution similar to the accepted answer for anyone out there that is looking. Works in Chrome, Firefox, Safari, IE7+
Solution
function SetArgList(fn, args) {
var fnbody = fn.toString().replace(
/^\s*function\s*[\$_a-zA-Z0-9]+\(.*\)\s*\{/, //BEWARE, removes original arguments
''
).replace(
/\s*\}\s*$/,
''
);
return new Function(args, fnbody)
}
How to use
Just redefine your original function like this using SetArgList:
function main() {
alert(hello);
alert(world);
}
main = SetArgList(main, 'hello,world');
main('hello', 'world');
In my solution there's no need for an array but you could edit it, my function only requires argument names separated by a comma.

you can use .apply:
this works for me:
f = function(a,b,c){
console.log(a,b,c);
};
var args = ["a", "b", "c"];
f.apply(this, args); //print "abc"
using arguments:
f = function(){
for(var key in arguments) {
console.log(arguments[key]);
}
};
var args = ["a", "b", "c"];
f.apply(this, args);
it's that you looking?

As said before, you can't do it the way you want, you're breaking lexical scoping.
However, here a minimalism version of the way you can implement it (a lot of improvements should be done !). The only thing required is that you function has the named parameter in arguments.
function getType( el ) {
// TODO
return "";
}
function addMethod( obj, name, typedef, func ) {
var argumentsLength = typedef.length;
obj[ name ] = function() {
var len = arguments.length, i = 0;
if( argumentsLength != len )
{
throw new TypeError( "Wrong number of arguments for method " + name );
}
for( i = 0; i < len; i++ )
{
// TODO better type checking
if( getType( arguments[i] ) != getType( typedef[i] ) )
{
throw new TypeError( "Wrong type for arguments number " + i + " for method " + name );
}
}
return func.apply( obj, arguments );
};
};
var o = {};
addMethod( o, "log", [Array, String], function( arr, str ) {
// arguments MUST be explicitly declared above
console.log( arr, str );
});
o.log( ["a"], "b" ); // OK
o.log( "a", "b" ); // TypeError
o.log( ["a"], "b", "c" ); // TypeError

I found a solution that only works on webkit browsers:
f = function(){ console.log(a,b,c); };
fx = eval(
f.toString().replace(
/^function [^\(]*\(\)/,
"var __temp = function (a,b,c)")
+ "; __temp");
fx(1,2,3);
This can also be generalized.
[edit]
This works for other browsers as well, memory let me down - comments // /**/ in some browsers get discarded.

Related

Get Object's method that have a particular parameter [duplicate]

Is there a way to get the function parameter names of a function dynamically?
Let’s say my function looks like this:
function doSomething(param1, param2, .... paramN){
// fill an array with the parameter name and value
// some other code
}
Now, how would I get a list of the parameter names and their values into an array from inside the function?
The following function will return an array of the parameter names of any function passed in.
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if(result === null)
result = [];
return result;
}
Example usage:
getParamNames(getParamNames) // returns ['func']
getParamNames(function (a,b,c,d){}) // returns ['a','b','c','d']
getParamNames(function (a,/*b,c,*/d){}) // returns ['a','d']
getParamNames(function (){}) // returns []
Edit:
With the invent of ES6 this function can be tripped up by default parameters. Here is a quick hack which should work in most cases:
var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;
I say most cases because there are some things that will trip it up
function (a=4*(5/3), b) {} // returns ['a']
Edit:
I also note vikasde wants the parameter values in an array also. This is already provided in a local variable named arguments.
excerpt from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments:
The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length. For example, it does not have the pop method. However it can be converted to a real Array:
var args = Array.prototype.slice.call(arguments);
If Array generics are available, one can use the following instead:
var args = Array.slice(arguments);
Below is the code taken from AngularJS which uses the technique for its dependency injection mechanism.
And here is an explanation of it taken from http://docs.angularjs.org/tutorial/step_05
Angular's dependency injector provides services to your controller
when the controller is being constructed. The dependency injector also
takes care of creating any transitive dependencies the service may
have (services often depend upon other services).
Note that the names of arguments are significant, because the injector
uses these to look up the dependencies.
/**
* #ngdoc overview
* #name AUTO
* #description
*
* Implicit module which gets automatically added to each {#link AUTO.$injector $injector}.
*/
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function annotate(fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
arg.replace(FN_ARG, function(all, underscore, name){
$inject.push(name);
});
});
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn')
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}
Here is an updated solution that attempts to address all the edge cases mentioned above in a compact way:
function $args(func) {
return (func + '')
.replace(/[/][/].*$/mg,'') // strip single-line comments
.replace(/\s+/g, '') // strip white space
.replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments
.split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters
.replace(/=[^,]+/g, '') // strip any ES6 defaults
.split(',').filter(Boolean); // split & filter [""]
}
Abbreviated test output (full test cases are attached below):
'function (a,b,c)...' // returns ["a","b","c"]
'function ()...' // returns []
'function named(a, b, c) ...' // returns ["a","b","c"]
'function (a /* = 1 */, b /* = true */) ...' // returns ["a","b"]
'function fprintf(handle, fmt /*, ...*/) ...' // returns ["handle","fmt"]
'function( a, b = 1, c )...' // returns ["a","b","c"]
'function (a=4*(5/3), b) ...' // returns ["a","b"]
'function (a, // single-line comment xjunk) ...' // returns ["a","b"]
'function (a /* fooled you...' // returns ["a","b"]
'function (a /* function() yes */, \n /* no, */b)/* omg! */...' // returns ["a","b"]
'function ( A, b \n,c ,d \n ) \n ...' // returns ["A","b","c","d"]
'function (a,b)...' // returns ["a","b"]
'function $args(func) ...' // returns ["func"]
'null...' // returns ["null"]
'function Object() ...' // returns []
function $args(func) {
return (func + '')
.replace(/[/][/].*$/mg,'') // strip single-line comments
.replace(/\s+/g, '') // strip white space
.replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments
.split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters
.replace(/=[^,]+/g, '') // strip any ES6 defaults
.split(',').filter(Boolean); // split & filter [""]
}
// test cases
document.getElementById('console_info').innerHTML = (
[
// formatting -- typical
function(a,b,c){},
function(){},
function named(a, b, c) {
/* multiline body */
},
// default values -- conventional
function(a /* = 1 */, b /* = true */) { a = a||1; b=b||true; },
function fprintf(handle, fmt /*, ...*/) { },
// default values -- ES6
"function( a, b = 1, c ){}",
"function (a=4*(5/3), b) {}",
// embedded comments -- sardonic
function(a, // single-line comment xjunk) {}
b //,c,d
) // single-line comment
{},
function(a /* fooled you{*/,b){},
function /* are you kidding me? (){} */(a /* function() yes */,
/* no, */b)/* omg! */{/*}}*/},
// formatting -- sardonic
function ( A, b
,c ,d
)
{
},
// by reference
this.jQuery || function (a,b){return new e.fn.init(a,b,h)},
$args,
// inadvertent non-function values
null,
Object
].map(function(f) {
var abbr = (f + '').replace(/\n/g, '\\n').replace(/\s+|[{]+$/g, ' ').split("{", 1)[0] + "...";
return " '" + abbr + "' // returns " + JSON.stringify($args(f));
}).join("\n") + "\n"); // output for copy and paste as a markdown snippet
<pre id='console_info'></pre>
Solution that is less error prone to spaces and comments would be:
var fn = function(/* whoa) */ hi, you){};
fn.toString()
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'')
.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1]
.split(/,/)
["hi", "you"]
A lot of the answers on here use regexes, this is fine but it doesn't handle new additions to the language too well (like arrow functions and classes). Also of note is that if you use any of these functions on minified code it's going to go 🔥. It will use whatever the minified name is. Angular gets around this by allowing you to pass in an ordered array of strings that matches the order of the arguments when registering them with the DI container. So on with the solution:
var esprima = require('esprima');
var _ = require('lodash');
const parseFunctionArguments = (func) => {
// allows us to access properties that may or may not exist without throwing
// TypeError: Cannot set property 'x' of undefined
const maybe = (x) => (x || {});
// handle conversion to string and then to JSON AST
const functionAsString = func.toString();
const tree = esprima.parse(functionAsString);
console.log(JSON.stringify(tree, null, 4))
// We need to figure out where the main params are. Stupid arrow functions 👊
const isArrowExpression = (maybe(_.first(tree.body)).type == 'ExpressionStatement');
const params = isArrowExpression ? maybe(maybe(_.first(tree.body)).expression).params
: maybe(_.first(tree.body)).params;
// extract out the param names from the JSON AST
return _.map(params, 'name');
};
This handles the original parse issue and a few more function types (e.g. arrow functions). Here's an idea of what it can and can't handle as is:
// I usually use mocha as the test runner and chai as the assertion library
describe('Extracts argument names from function signature. 💪', () => {
const test = (func) => {
const expectation = ['it', 'parses', 'me'];
const result = parseFunctionArguments(toBeParsed);
result.should.equal(expectation);
}
it('Parses a function declaration.', () => {
function toBeParsed(it, parses, me){};
test(toBeParsed);
});
it('Parses a functional expression.', () => {
const toBeParsed = function(it, parses, me){};
test(toBeParsed);
});
it('Parses an arrow function', () => {
const toBeParsed = (it, parses, me) => {};
test(toBeParsed);
});
// ================= cases not currently handled ========================
// It blows up on this type of messing. TBH if you do this it deserves to
// fail 😋 On a tech note the params are pulled down in the function similar
// to how destructuring is handled by the ast.
it('Parses complex default params', () => {
function toBeParsed(it=4*(5/3), parses, me) {}
test(toBeParsed);
});
// This passes back ['_ref'] as the params of the function. The _ref is a
// pointer to an VariableDeclarator where the ✨🦄 happens.
it('Parses object destructuring param definitions.' () => {
function toBeParsed ({it, parses, me}){}
test(toBeParsed);
});
it('Parses object destructuring param definitions.' () => {
function toBeParsed ([it, parses, me]){}
test(toBeParsed);
});
// Classes while similar from an end result point of view to function
// declarations are handled completely differently in the JS AST.
it('Parses a class constructor when passed through', () => {
class ToBeParsed {
constructor(it, parses, me) {}
}
test(ToBeParsed);
});
});
Depending on what you want to use it for ES6 Proxies and destructuring may be your best bet. For example if you wanted to use it for dependency injection (using the names of the params) then you can do it as follows:
class GuiceJs {
constructor() {
this.modules = {}
}
resolve(name) {
return this.getInjector()(this.modules[name]);
}
addModule(name, module) {
this.modules[name] = module;
}
getInjector() {
var container = this;
return (klass) => {
console.log(klass);
var paramParser = new Proxy({}, {
// The `get` handler is invoked whenever a get-call for
// `injector.*` is made. We make a call to an external service
// to actually hand back in the configured service. The proxy
// allows us to bypass parsing the function params using
// taditional regex or even the newer parser.
get: (target, name) => container.resolve(name),
// You shouldn't be able to set values on the injector.
set: (target, name, value) => {
throw new Error(`Don't try to set ${name}! 😑`);
}
})
return new klass(paramParser);
}
}
}
It's not the most advanced resolver out there but it gives an idea of how you can use a Proxy to handle it if you want to use args parser for simple DI. There is however one slight caveat in this approach. We need to use destructuring assignments instead of normal params. When we pass in the injector proxy the destructuring is the same as calling the getter on the object.
class App {
constructor({tweeter, timeline}) {
this.tweeter = tweeter;
this.timeline = timeline;
}
}
class HttpClient {}
class TwitterApi {
constructor({client}) {
this.client = client;
}
}
class Timeline {
constructor({api}) {
this.api = api;
}
}
class Tweeter {
constructor({api}) {
this.api = api;
}
}
// Ok so now for the business end of the injector!
const di = new GuiceJs();
di.addModule('client', HttpClient);
di.addModule('api', TwitterApi);
di.addModule('tweeter', Tweeter);
di.addModule('timeline', Timeline);
di.addModule('app', App);
var app = di.resolve('app');
console.log(JSON.stringify(app, null, 4));
This outputs the following:
{
"tweeter": {
"api": {
"client": {}
}
},
"timeline": {
"api": {
"client": {}
}
}
}
Its wired up the entire application. The best bit is that the app is easy to test (you can just instantiate each class and pass in mocks/stubs/etc). Also if you need to swap out implementations, you can do that from a single place. All this is possible because of JS Proxy objects.
Note: There is a lot of work that would need to be done to this before it would be ready for production use but it does give an idea of what it would look like.
It's a bit late in the answer but it may help others who are thinking of the same thing. 👍
I know this is an old question, but beginners have been copypasting solutions that extract parameter names from the string representation of a function as if this was good practice in any code. Most of the time, this just hides a flaw in the logic.
Writing parameter names in the parentheses of a function declaration can be seen as a shorthand syntax for variable creation. This:
function doSomething(foo, bar) {
console.log("does something");
}
...is akin to this:
function doSomething() {
var foo = arguments[0];
var bar = arguments[1];
console.log("does something");
}
The variables themselves are stored in the function's scope, not as properties in an object. Just like you can't manipulate the name of a variable with code, there is no way to retrieve the name of a parameter as it is not a string, and it could be eliminated during JIT compilation.
I always thought of the string representation of a function as a tool for debugging purposes, especially because of this arguments array-like object. You are not required to give names to the arguments in the first place. If you try parsing a stringified function, it doesn't actually tell you about extra unnamed parameters it might take.
Here's an even worse and more common situation. If a function has more than 3 or 4 arguments, it might be logical to pass it an object instead, which is easier to work with.
function saySomething(obj) {
if(obj.message) console.log((obj.sender || "Anon") + ": " + obj.message);
}
saySomething({sender: "user123", message: "Hello world"});
In this case, the function itself will be able to read through the object it receives and look for its properties and get both their names and values, but trying to parse the string representation of the function would only give you "obj" for parameters, which isn't useful at all.
I have read most of the answers here, and I would like to add my one-liner.
new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)').exec(Function.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
or
function getParameters(func) {
return new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
}
or for a one-liner function in ECMA6
var getParameters = func => new RegExp('(?:'+func.name+'\\s*|^)\\s*\\((.*?)\\)').exec(func.toString().replace(/\n/g, ''))[1].replace(/\/\*.*?\*\//g, '').replace(/ /g, '');
__
Let's say you have a function
function foo(abc, def, ghi, jkl) {
//code
}
The below code will return "abc,def,ghi,jkl"
That code will also work with the setup of a function that Camilo Martin gave:
function ( A, b
,c ,d
){}
Also with Bubersson's comment on Jack Allan's answer:
function(a /* fooled you)*/,b){}
__
Explanation
new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)')
This creates a Regular Expression with the new RegExp('(?:'+Function.name+'\\s*|^)\\s*\\((.*?)\\)'). I have to use new RegExp because I am injecting a variable (Function.name, the name of the function being targeted) into the RegExp.
Example If the function name is "foo" (function foo()), the RegExp will be /foo\s*\((.*?)\)/.
Function.toString().replace(/\n/g, '')
Then it converts the entire function into a string, and removes all newlines. Removing newlines helps with the function setup Camilo Martin gave.
.exec(...)[1]
This is the RegExp.prototype.exec function. It basically matches the Regular Exponent (new RegExp()) into the String (Function.toString()). Then the [1] will return the first Capture Group found in the Regular Exponent ((.*?)).
.replace(/\/\*.*?\*\//g, '').replace(/ /g, '')
This will remove every comment inside /* and */, and remove all spaces.
This also now supports reading and understanding arrow (=>) functions, such as f = (a, b) => void 0;, in which Function.toString() would return (a, b) => void 0 instead of the normal function's function f(a, b) { return void 0; }. The original regular expression would have thrown an error in its confusion, but is now accounted for.
The change was from new RegExp(Function.name+'\\s*\\((.*?)\\)') (/Function\s*\((.*?)\)/) to new RegExp('(?:'+Function.name+'\\s*|^)\\((.*?)\\)') (/(?:Function\s*|^)\((.*?)\)/)
If you want to make all the parameters into an Array instead of a String separated by commas, at the end just add .split(',').
Since JavaScript is a scripting language, I feel that its introspection should support getting function parameter names. Punting on that functionality is a violation of first principles, so I decided to explore the issue further.
That led me to this question but no built-in solutions. Which led me to this answer which explains that arguments is only deprecated outside the function, so we can no longer use myFunction.arguments or we get:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
Time to roll up our sleeves and get to work:
⭐ Retrieving function parameters requires a parser because complex expressions like 4*(5/3) can be used as default values. So Gaafar's answer or James Drew's answer are so far the best approaches.
I tried the babylon and esprima parsers but unfortunately they can't parse standalone anonymous functions, as pointed out in Mateusz Charytoniuk's answer. I figured out another workaround though by surrounding the code in parentheses, so as not to change the logic:
const ast = parser.parse("(\n" + func.toString() + "\n)")
The newlines prevent issues with // (single-line comments).
⭐ If a parser is not available, the next-best option is to use a tried-and-true technique like Angular.js's dependency injector regular expressions. I combined a functional version of Lambder's answer with humbletim's answer and added an optional ARROW boolean for controlling whether ES6 fat arrow functions are allowed by the regular expressions.
Here are two solutions I put together. Note that these have no logic to detect whether a function has valid syntax, they only extract the arguments. This is generally ok since we usually pass parsed functions to getArguments() so their syntax is already valid.
I will try to curate these solutions as best I can, but without effort from the JavaScript maintainers, this will remain an open problem.
Node.js version (not runnable until StackOverflow supports Node.js):
const parserName = 'babylon';
// const parserName = 'esprima';
const parser = require(parserName);
function getArguments(func) {
const maybe = function (x) {
return x || {}; // optionals support
}
try {
const ast = parser.parse("(\n" + func.toString() + "\n)");
const program = parserName == 'babylon' ? ast.program : ast;
return program
.body[0]
.expression
.params
.map(function(node) {
return node.name || maybe(node.left).name || '...' + maybe(node.argument).name;
});
} catch (e) {
return []; // could also return null
}
};
////////// TESTS //////////
function logArgs(func) {
let object = {};
object[func] = getArguments(func);
console.log(object);
// console.log(/*JSON.stringify(*/getArguments(func)/*)*/);
}
console.log('');
console.log('////////// MISC //////////');
logArgs((a, b) => {});
logArgs((a, b = 1) => {});
logArgs((a, b, ...args) => {});
logArgs(function(a, b, ...args) {});
logArgs(function(a, b = 1, c = 4 * (5 / 3), d = 2) {});
logArgs(async function(a, b, ...args) {});
logArgs(function async(a, b, ...args) {});
console.log('');
console.log('////////// FUNCTIONS //////////');
logArgs(function(a, b, c) {});
logArgs(function() {});
logArgs(function named(a, b, c) {});
logArgs(function(a /* = 1 */, b /* = true */) {});
logArgs(function fprintf(handle, fmt /*, ...*/) {});
logArgs(function(a, b = 1, c) {});
logArgs(function(a = 4 * (5 / 3), b) {});
// logArgs(function (a, // single-line comment xjunk) {});
// logArgs(function (a /* fooled you {});
// logArgs(function (a /* function() yes */, \n /* no, */b)/* omg! */ {});
// logArgs(function ( A, b \n,c ,d \n ) \n {});
logArgs(function(a, b) {});
logArgs(function $args(func) {});
logArgs(null);
logArgs(function Object() {});
console.log('');
console.log('////////// STRINGS //////////');
logArgs('function (a,b,c) {}');
logArgs('function () {}');
logArgs('function named(a, b, c) {}');
logArgs('function (a /* = 1 */, b /* = true */) {}');
logArgs('function fprintf(handle, fmt /*, ...*/) {}');
logArgs('function( a, b = 1, c ) {}');
logArgs('function (a=4*(5/3), b) {}');
logArgs('function (a, // single-line comment xjunk) {}');
logArgs('function (a /* fooled you {}');
logArgs('function (a /* function() yes */, \n /* no, */b)/* omg! */ {}');
logArgs('function ( A, b \n,c ,d \n ) \n {}');
logArgs('function (a,b) {}');
logArgs('function $args(func) {}');
logArgs('null');
logArgs('function Object() {}');
Full working example:
https://repl.it/repls/SandybrownPhonyAngles
Browser version (note that it stops at the first complex default value):
function getArguments(func) {
const ARROW = true;
const FUNC_ARGS = ARROW ? /^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m : /^(function)\s*[^\(]*\(\s*([^\)]*)\)/m;
const FUNC_ARG_SPLIT = /,/;
const FUNC_ARG = /^\s*(_?)(.+?)\1\s*$/;
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
return ((func || '').toString().replace(STRIP_COMMENTS, '').match(FUNC_ARGS) || ['', '', ''])[2]
.split(FUNC_ARG_SPLIT)
.map(function(arg) {
return arg.replace(FUNC_ARG, function(all, underscore, name) {
return name.split('=')[0].trim();
});
})
.filter(String);
}
////////// TESTS //////////
function logArgs(func) {
let object = {};
object[func] = getArguments(func);
console.log(object);
// console.log(/*JSON.stringify(*/getArguments(func)/*)*/);
}
console.log('');
console.log('////////// MISC //////////');
logArgs((a, b) => {});
logArgs((a, b = 1) => {});
logArgs((a, b, ...args) => {});
logArgs(function(a, b, ...args) {});
logArgs(function(a, b = 1, c = 4 * (5 / 3), d = 2) {});
logArgs(async function(a, b, ...args) {});
logArgs(function async(a, b, ...args) {});
console.log('');
console.log('////////// FUNCTIONS //////////');
logArgs(function(a, b, c) {});
logArgs(function() {});
logArgs(function named(a, b, c) {});
logArgs(function(a /* = 1 */, b /* = true */) {});
logArgs(function fprintf(handle, fmt /*, ...*/) {});
logArgs(function(a, b = 1, c) {});
logArgs(function(a = 4 * (5 / 3), b) {});
// logArgs(function (a, // single-line comment xjunk) {});
// logArgs(function (a /* fooled you {});
// logArgs(function (a /* function() yes */, \n /* no, */b)/* omg! */ {});
// logArgs(function ( A, b \n,c ,d \n ) \n {});
logArgs(function(a, b) {});
logArgs(function $args(func) {});
logArgs(null);
logArgs(function Object() {});
console.log('');
console.log('////////// STRINGS //////////');
logArgs('function (a,b,c) {}');
logArgs('function () {}');
logArgs('function named(a, b, c) {}');
logArgs('function (a /* = 1 */, b /* = true */) {}');
logArgs('function fprintf(handle, fmt /*, ...*/) {}');
logArgs('function( a, b = 1, c ) {}');
logArgs('function (a=4*(5/3), b) {}');
logArgs('function (a, // single-line comment xjunk) {}');
logArgs('function (a /* fooled you {}');
logArgs('function (a /* function() yes */, \n /* no, */b)/* omg! */ {}');
logArgs('function ( A, b \n,c ,d \n ) \n {}');
logArgs('function (a,b) {}');
logArgs('function $args(func) {}');
logArgs('null');
logArgs('function Object() {}');
Full working example:
https://repl.it/repls/StupendousShowyOffices
(function(a,b,c){}).toString().replace(/.*\(|\).*/ig,"").split(',')
=> [ "a", "b", "c" ]
You can also use "esprima" parser to avoid many issues with comments, whitespace and other things inside parameters list.
function getParameters(yourFunction) {
var i,
// safetyValve is necessary, because sole "function () {...}"
// is not a valid syntax
parsed = esprima.parse("safetyValve = " + yourFunction.toString()),
params = parsed.body[0].expression.right.params,
ret = [];
for (i = 0; i < params.length; i += 1) {
// Handle default params. Exe: function defaults(a = 0,b = 2,c = 3){}
if (params[i].type == 'AssignmentPattern') {
ret.push(params[i].left.name)
} else {
ret.push(params[i].name);
}
}
return ret;
}
It works even with code like this:
getParameters(function (hello /*, foo ),* /bar* { */,world) {}); // ["hello", "world"]
The proper way to do this is to use a JS parser. Here is an example using acorn.
const acorn = require('acorn');
function f(a, b, c) {
// ...
}
const argNames = acorn.parse(f).body[0].params.map(x => x.name);
console.log(argNames); // Output: [ 'a', 'b', 'c' ]
The code here finds the names of the three (formal) parameters of the function f. It does so by feeding f into acorn.parse().
I've tried doing this before, but never found a praticial way to get it done. I ended up passing in an object instead and then looping through it.
//define like
function test(args) {
for(var item in args) {
alert(item);
alert(args[item]);
}
}
//then used like
test({
name:"Joe",
age:40,
admin:bool
});
I don't know if this solution suits your problem, but it lets you redefine whatever function you want, without having to change code that uses it. Existing calls will use positioned params, while the function implementation may use "named params" (a single hash param).
I thought that you will anyway modify existing function definitions so, why not having a factory function that makes just what you want:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var withNamedParams = function(params, lambda) {
return function() {
var named = {};
var max = arguments.length;
for (var i=0; i<max; i++) {
named[params[i]] = arguments[i];
}
return lambda(named);
};
};
var foo = withNamedParams(["a", "b", "c"], function(params) {
for (var param in params) {
alert(param + ": " + params[param]);
}
});
foo(1, 2, 3);
</script>
</head>
<body>
</body>
</html>
Hope it helps.
Taking the answer from #jack-allan I modified the function slightly to allow ES6 default properties such as:
function( a, b = 1, c ){};
to still return [ 'a', 'b' ]
/**
* Get the keys of the paramaters of a function.
*
* #param {function} method Function to get parameter keys for
* #return {array}
*/
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /(?:^|,)\s*([^\s,=]+)/g;
function getFunctionParameters ( func ) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var argsList = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')'));
var result = argsList.match( ARGUMENT_NAMES );
if(result === null) {
return [];
}
else {
var stripped = [];
for ( var i = 0; i < result.length; i++ ) {
stripped.push( result[i].replace(/[\s,]/g, '') );
}
return stripped;
}
}
As this has not yet been mentioned, if you are using Typescript you can emit meta-data when using Decorators which will allow you to get the parameter types.
Metadata will only be emitted if the class/function/prop has a decorator on it.
It doesn't matter which decorator.
This feature can be enabled by setting emitDecoratorMetadata to true inside tsconfig.json
{
"compilerOptions": {
"emitDecoratorMetadata": true
}
}
As the metadata is still an early proposal the reflect-metadata package must be installed or Reflect.getMetadata will not be defined.
npm install reflect-metadata
You can use it as follows:
const AnyDecorator = () : MethodDecorator => {
return target => { }
}
class Person{
#AnyDecorator()
sayHello(other: Person){}
}
const instance = new Person();
// This returns: Function
const funcType = Reflect.getMetadata('design:type', instance.sayHello);
// Returns an array of types, here it would be: [Person]
const funcParams = Reflect.getMetadata('design:paramtypes', instance.sayHello);
In newer versions of Angular for instance this is used to determine what to inject -> https://stackoverflow.com/a/53041387/1087372
I don't know how to get a list of the parameters but you can do this to get how many it expects. Note this only counts arguments without a default value in the signature:
function foobar(a, b, c) {}
function foobar2(a, b=false, c=false) {}
console.log(foobar.length); // prints 3
console.log(foobar2.length); // prints 1
//See this:
// global var, naming bB
var bB = 5;
// Dependency Injection cokntroller
var a = function(str, fn) {
//stringify function body
var fnStr = fn.toString();
// Key: get form args to string
var args = fnStr.match(/function\s*\((.*?)\)/);
//
console.log(args);
// if the form arg is 'bB', then exec it, otherwise, do nothing
for (var i = 0; i < args.length; i++) {
if(args[i] == 'bB') {
fn(bB);
}
}
}
// will do nothing
a('sdfdfdfs,', function(some){
alert(some)
});
// will alert 5
a('sdfdsdsfdfsdfdsf,', function(bB){
alert(bB)
});
// see, this shows you how to get function args in string
The answer to this requires 3 steps:
To get the values of the actual parameters passed to the function (let's call it argValues). This is straight forward as it will be available as arguments inside the function.
To get the parameter names from the function signature (let's call it argNames). This not as easy and requires parsing the function. Instead of doing the complex regex yourself and worrying about edge cases (default parameters, comments, ...), you can use a library like babylon that will parse the function into an abstract syntax tree from which you can obtain the names of parameters.
The last step is to join the 2 arrays together into 1 array that has the name and value of all the parameters.
The code will be like this
const babylon = require("babylon")
function doSomething(a, b, c) {
// get the values of passed argumenst
const argValues = arguments
// get the names of the arguments by parsing the function
const ast = babylon.parse(doSomething.toString())
const argNames = ast.program.body[0].params.map(node => node.name)
// join the 2 arrays, by looping over the longest of 2 arrays
const maxLen = Math.max(argNames.length, argValues.length)
const args = []
for (i = 0; i < maxLen; i++) {
args.push({name: argNames[i], value: argValues[i]})
}
console.log(args)
// implement the actual function here
}
doSomething(1, 2, 3, 4)
and the logged object will be
[
{
"name": "a",
"value": 1
},
{
"name": "c",
"value": 3
},
{
"value": 4
}
]
And here's a working example https://tonicdev.com/5763eb77a945f41300f62a79/5763eb77a945f41300f62a7a
function getArgs(args) {
var argsObj = {};
var argList = /\(([^)]*)/.exec(args.callee)[1];
var argCnt = 0;
var tokens;
while (tokens = /\s*([^,]+)/g.exec(argList)) {
argsObj[tokens[1]] = args[argCnt++];
}
return argsObj;
}
Wow so many answers already.. Im pretty sure this gets buried. Even so I figured this might be useful for some.
I wasn't fully satisfied with the chosen answers as in ES6 it doesn't work well with default values. And it also does not provide the default value information. I also wanted a lightweight function that does not depend on an external lib.
This function is very useful for debugging purposes, for example: logging called function with its params, default param values and arguments.
I spent some time on this yesterday, cracking the right RegExp to solve this issue and this is what I came up with. It works very well and I'm very pleased with the outcome:
const REGEX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
const REGEX_FUNCTION_PARAMS = /(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m
const REGEX_PARAMETERS_VALUES = /\s*(\w+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm
/**
* Retrieve a function's parameter names and default values
* Notes:
* - parameters with default values will not show up in transpiler code (Babel) because the parameter is removed from the function.
* - does NOT support inline arrow functions as default values
* to clarify: ( name = "string", add = defaultAddFunction ) - is ok
* ( name = "string", add = ( a )=> a + 1 ) - is NOT ok
* - does NOT support default string value that are appended with a non-standard ( word characters or $ ) variable name
* to clarify: ( name = "string" + b ) - is ok
* ( name = "string" + $b ) - is ok
* ( name = "string" + b + "!" ) - is ok
* ( name = "string" + λ ) - is NOT ok
* #param {function} func
* #returns {Array} - An array of the given function's parameter [key, default value] pairs.
*/
function getParams(func) {
let functionAsString = func.toString()
let params = []
let match
functionAsString = functionAsString.replace(REGEX_COMMENTS, '')
functionAsString = functionAsString.match(REGEX_FUNCTION_PARAMS)[1]
if (functionAsString.charAt(0) === '(') functionAsString = functionAsString.slice(1, -1)
while (match = REGEX_PARAMETERS_VALUES.exec(functionAsString)) params.push([match[1], match[2]])
return params
}
// Lets run some tests!
var defaultName = 'some name'
function test1(param1, param2, param3) { return (param1) => param1 + param2 + param3 }
function test2(param1, param2 = 4 * (5 / 3), param3) {}
function test3(param1, param2 = "/root/" + defaultName + ".jpeg", param3) {}
function test4(param1, param2 = (a) => a + 1) {}
console.log(getParams(test1))
console.log(getParams(test2))
console.log(getParams(test3))
console.log(getParams(test4))
// [ [ 'param1', undefined ], [ 'param2', undefined ], [ 'param3', undefined ] ]
// [ [ 'param1', undefined ], [ 'param2', '4 * (5 / 3)' ], [ 'param3', undefined ] ]
// [ [ 'param1', undefined ], [ 'param2', '"/root/" + defaultName + ".jpeg"' ], [ 'param3', undefined ] ]
// [ [ 'param1', undefined ], [ 'param2', '( a' ] ]
// --> This last one fails because of the inlined arrow function!
var arrowTest1 = (a = 1) => a + 4
var arrowTest2 = a => b => a + b
var arrowTest3 = (param1 = "/" + defaultName) => { return param1 + '...' }
var arrowTest4 = (param1 = "/" + defaultName, param2 = 4, param3 = null) => { () => param3 ? param3 : param2 }
console.log(getParams(arrowTest1))
console.log(getParams(arrowTest2))
console.log(getParams(arrowTest3))
console.log(getParams(arrowTest4))
// [ [ 'a', '1' ] ]
// [ [ 'a', undefined ] ]
// [ [ 'param1', '"/" + defaultName' ] ]
// [ [ 'param1', '"/" + defaultName' ], [ 'param2', '4' ], [ 'param3', 'null' ] ]
console.log(getParams((param1) => param1 + 1))
console.log(getParams((param1 = 'default') => { return param1 + '.jpeg' }))
// [ [ 'param1', undefined ] ]
// [ [ 'param1', '\'default\'' ] ]
As you can tell some of the parameter names disappear because the Babel transpiler removes them from the function. If you would run this in the latest NodeJS it works as expected (The commented results are from NodeJS).
Another note, as stated in the comment is that is does not work with inlined arrow functions as a default value. This simply makes it far to complex to extract the values using a RegExp.
Please let me know if this was useful for you! Would love to hear some feedback!
This package uses recast in order to create an AST and then the parameter names are gathered from their, this allows it to support pattern matching, default arguments, arrow functions and other ES6 features.
https://www.npmjs.com/package/es-arguments
Here's my solution -- it works for named and unnamed functions, async and non-async functions, async and non-async lambdas, and lambdas with and without parens.
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*.*\*\/))/mg;
const STRIP_KEYWORDS = /(\s*async\s*|\s*function\s*)+/;
const ARGUMENT_NAMES = /\(([^)]+)\)\s*=>|([a-zA-Z_$]+)\s*=>|[a-zA-Z_$]+\(([^)]+)\)|\(([^)]+)\)/;
const ARGUMENT_SPLIT = /[ ,\n\r\t]+/;
function getParamNames(func) {
const fnStr = func.toString()
.replace(STRIP_COMMENTS, "")
.replace(STRIP_KEYWORDS, "")
.trim();
const matches = ARGUMENT_NAMES.exec(fnStr);
var match;
if (matches) {
for (var i = 1; i < matches.length; i++) {
if (matches[i]) {
match = matches[i];
break;
}
}
}
if (match === undefined) {
return [];
}
return match.split(ARGUMENT_SPLIT).filter(part => part !== "");
}
How I typically do it:
function name(arg1, arg2){
var args = arguments; // array: [arg1, arg2]
var objecArgOne = args[0].one;
}
name({one: "1", two: "2"}, "string");
You can even ref the args by the functions name like:
name.arguments;
Hope this helps!
I'll give you a short example below:
function test(arg1,arg2){
var funcStr = test.toString()
var leftIndex = funcStr.indexOf('(');
var rightIndex = funcStr.indexOf(')');
var paramStr = funcStr.substr(leftIndex+1,rightIndex-leftIndex-1);
var params = paramStr.split(',');
for(param of params){
console.log(param); // arg1,arg2
}
}
test();
I have modified the version taken from AngularJS that implements a dependency injection mechanism to work without Angular. I have also updated the STRIP_COMMENTS regex to work with ECMA6, so it supports things like default values in the signature.
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
var STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;
function annotate(fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
});
fn.$inject = $inject;
}
} else {
throw Error("not a function")
}
return $inject;
}
console.log("function(a, b)",annotate(function(a, b) {
console.log(a, b, c, d)
}))
console.log("function(a, b = 0, /*c,*/ d)",annotate(function(a, b = 0, /*c,*/ d) {
console.log(a, b, c, d)
}))
annotate({})
You can access the argument values passed to a function using the "arguments" property.
function doSomething()
{
var args = doSomething.arguments;
var numArgs = args.length;
for(var i = 0 ; i < numArgs ; i++)
{
console.log("arg " + (i+1) + " = " + args[i]);
//console.log works with firefox + firebug
// you can use an alert to check in other browsers
}
}
doSomething(1, '2', {A:2}, [1,2,3]);
It's pretty easy.
At the first there is a deprecated arguments.callee — a reference to called function.
At the second if you have a reference to your function you can easily get their textual representation.
At the third if you calling your function as constructor you can also have a link via yourObject.constructor.
NB: The first solution deprecated so if you can't to not use it you must also think about your app architecture.
If you don't need exact variable names just use inside a function internal variable arguments without any magic.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
All of them going to call toString and replace with re so we can create a helper:
// getting names of declared parameters
var getFunctionParams = function (func) {
return String(func).replace(/[^\(]+\(([^\)]*)\).*/m, '$1');
}
Some examples:
// Solution 1. deprecated! don't use it!
var myPrivateFunction = function SomeFuncName (foo, bar, buz) {
console.log(getFunctionParams(arguments.callee));
};
myPrivateFunction (1, 2);
// Solution 2.
var myFunction = function someFunc (foo, bar, buz) {
// some code
};
var params = getFunctionParams(myFunction);
console.log(params);
// Solution 3.
var cls = function SuperKewlClass (foo, bar, buz) {
// some code
};
var inst = new cls();
var params = getFunctionParams(inst.constructor);
console.log(params);
Enjoy with JS!
UPD: Jack Allan was provided a little bit better solution actually. GJ Jack!
Whatever the solution, it must not break on wierd functions, whose toString() looks just as wierd:
function ( A, b
,c ,d
){}
Also, why use complex regular expressions? This can be done like:
function getArguments(f) {
return f.toString().split(')',1)[0].replace(/\s/g,'').substr(9).split(',');
}
This works everywhere with every function, and the only regex is whitespace removal that doesn't even process the whole string due to the .split trick.
Ok so an old question with plenty of adequate answers.
here is my offering that does not use regex, except for the menial task of stripping whitespace . (I should note that the "strips_comments" function actually spaces them out, rather than physically remove them. that's because i use it elsewhere and for various reasons need the locations of the original non comment tokens to stay intact)
It's a fairly lengthy block of code as this pasting includes a mini test framework.
function do_tests(func) {
if (typeof func !== 'function') return true;
switch (typeof func.tests) {
case 'undefined' : return true;
case 'object' :
for (var k in func.tests) {
var test = func.tests[k];
if (typeof test==='function') {
var result = test(func);
if (result===false) {
console.log(test.name,'for',func.name,'failed');
return false;
}
}
}
return true;
case 'function' :
return func.tests(func);
}
return true;
}
function strip_comments(src) {
var spaces=(s)=>{
switch (s) {
case 0 : return '';
case 1 : return ' ';
case 2 : return ' ';
default :
return Array(s+1).join(' ');
}
};
var c1 = src.indexOf ('/*'),
c2 = src.indexOf ('//'),
eol;
var out = "";
var killc2 = () => {
out += src.substr(0,c2);
eol = src.indexOf('\n',c2);
if (eol>=0) {
src = spaces(eol-c2)+'\n'+src.substr(eol+1);
} else {
src = spaces(src.length-c2);
return true;
}
return false;
};
while ((c1>=0) || (c2>=0)) {
if (c1>=0) {
// c1 is a hit
if ( (c1<c2) || (c2<0) ) {
// and it beats c2
out += src.substr(0,c1);
eol = src.indexOf('*/',c1+2);
if (eol>=0) {
src = spaces((eol-c1)+2)+src.substr(eol+2);
} else {
src = spaces(src.length-c1);
break;
}
} else {
if (c2 >=0) {
// c2 is a hit and it beats c1
if (killc2()) break;
}
}
} else {
if (c2>=0) {
// c2 is a hit, c1 is a miss.
if (killc2()) break;
} else {
// both c1 & c2 are a miss
break;
}
}
c1 = src.indexOf ('/*');
c2 = src.indexOf ('//');
}
return out + src;
}
function function_args(fn) {
var src = strip_comments(fn.toString());
var names=src.split(')')[0].replace(/\s/g,'').split('(')[1].split(',');
return names;
}
function_args.tests = [
function test1 () {
function/*al programmers will sometimes*/strip_comments_tester/* because some comments are annoying*/(
/*see this---(((*/ src//)) it's an annoying comment does not help anyone understand if the
,code,//really does
/**/sucks ,much /*?*/)/*who would put "comment\" about a function like (this) { comment } here?*/{
}
var data = function_args(strip_comments_tester);
return ( (data.length==4) &&
(data[0]=='src') &&
(data[1]=='code') &&
(data[2]=='sucks') &&
(data[3]=='much') );
}
];
do_tests(function_args);
Here's one way:
// Utility function to extract arg name-value pairs
function getArgs(args) {
var argsObj = {};
var argList = /\(([^)]*)/.exec(args.callee)[1];
var argCnt = 0;
var tokens;
var argRe = /\s*([^,]+)/g;
while (tokens = argRe.exec(argList)) {
argsObj[tokens[1]] = args[argCnt++];
}
return argsObj;
}
// Test subject
function add(number1, number2) {
var args = getArgs(arguments);
console.log(args); // ({ number1: 3, number2: 4 })
}
// Invoke test subject
add(3, 4);
Note: This only works on browsers that support arguments.callee.

Pass array into google.maps.LatLng via apply() [duplicate]

In JavaScript, I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible?
What I want to do is something like this (but the code below does not work):
function Something(){
// init stuff
}
function createSomething(){
return new Something.apply(null, arguments);
}
var s = createSomething(a,b,c); // 's' is an instance of Something
The Answer
From the responses here, it became clear that there's no built-in way to call .apply() with the new operator. However, people suggested a number of really interesting solutions to the problem.
My preferred solution was this one from Matthew Crumley (I've modified it to pass the arguments property):
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function() {
return new F(arguments);
}
})();
With ECMAScript5's Function.prototype.bind things get pretty clean:
function newCall(Cls) {
return new (Function.prototype.bind.apply(Cls, arguments));
// or even
// return new (Cls.bind.apply(Cls, arguments));
// if you know that Cls.bind has not been overwritten
}
It can be used as follows:
var s = newCall(Something, a, b, c);
or even directly:
var s = new (Function.prototype.bind.call(Something, null, a, b, c));
var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));
This and the eval-based solution are the only ones that always work, even with special constructors like Date:
var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true
edit
A bit of explanation:
We need to run new on a function that takes a limited number of arguments. The bind method allows us to do it like so:
var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();
The anything parameter doesn't matter much, since the new keyword resets f's context. However, it is required for syntactical reasons. Now, for the bind call: We need to pass a variable number of arguments, so this does the trick:
var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();
Let's wrap that in a function. Cls is passed as argument 0, so it's gonna be our anything.
function newCall(Cls /*, arg1, arg2, ... */) {
var f = Cls.bind.apply(Cls, arguments);
return new f();
}
Actually, the temporary f variable is not needed at all:
function newCall(Cls /*, arg1, arg2, ... */) {
return new (Cls.bind.apply(Cls, arguments))();
}
Finally, we should make sure that bind is really what we need. (Cls.bind may have been overwritten). So replace it by Function.prototype.bind, and we get the final result as above.
Here's a generalized solution that can call any constructor (except native constructors that behave differently when called as functions, like String, Number, Date, etc.) with an array of arguments:
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
An object created by calling construct(Class, [1, 2, 3]) would be identical to an object created with new Class(1, 2, 3).
You could also make a more specific version so you don't have to pass the constructor every time. This is also slightly more efficient, since it doesn't need to create a new instance of the inner function every time you call it.
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function(args) {
return new F(args);
}
})();
The reason for creating and calling the outer anonymous function like that is to keep function F from polluting the global namespace. It's sometimes called the module pattern.
[UPDATE]
For those who want to use this in TypeScript, since TS gives an error if F returns anything:
function construct(constructor, args) {
function F() : void {
constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
If your environment supports ECMA Script 2015's spread operator (...), you can simply use it like this
function Something() {
// init stuff
}
function createSomething() {
return new Something(...arguments);
}
Note: Now that the ECMA Script 2015's specifications are published and most JavaScript engines are actively implementing it, this would be the preferred way of doing this.
You can check the Spread operator's support in few of the major environments, here.
Suppose you've got an Items constructor which slurps up all the arguments you throw at it:
function Items () {
this.elems = [].slice.call(arguments);
}
Items.prototype.sum = function () {
return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};
You can create an instance with Object.create() and then .apply() with that instance:
var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);
console.log(items.sum());
Which when run prints 10 since 1 + 2 + 3 + 4 == 10:
$ node t.js
10
In ES6, Reflect.construct() is quite convenient:
Reflect.construct(F, args)
#Matthew
I think it's better to fix the constructor property also.
// Invoke new operator with arbitrary arguments
// Holy Grail pattern
function invoke(constructor, args) {
var f;
function F() {
// constructor returns **this**
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
f = new F();
f.constructor = constructor;
return f;
}
You could move the init stuff out into a separate method of Something's prototype:
function Something() {
// Do nothing
}
Something.prototype.init = function() {
// Do init stuff
};
function createSomething() {
var s = new Something();
s.init.apply(s, arguments);
return s;
}
var s = createSomething(a,b,c); // 's' is an instance of Something
An improved version of #Matthew's answer. This form has the slight performance benefits obtained by storing the temp class in a closure, as well as the flexibility of having one function able to be used to create any class
var applyCtor = function(){
var tempCtor = function() {};
return function(ctor, args){
tempCtor.prototype = ctor.prototype;
var instance = new tempCtor();
ctor.prototype.constructor.apply(instance,args);
return instance;
}
}();
This would be used by calling applyCtor(class, [arg1, arg2, argn]);
This answer is a little late, but figured anyone who sees this might be able to use it. There is a way to return a new object using apply. Though it requires one little change to your object declaration.
function testNew() {
if (!( this instanceof arguments.callee ))
return arguments.callee.apply( new arguments.callee(), arguments );
this.arg = Array.prototype.slice.call( arguments );
return this;
}
testNew.prototype.addThem = function() {
var newVal = 0,
i = 0;
for ( ; i < this.arg.length; i++ ) {
newVal += this.arg[i];
}
return newVal;
}
testNew( 4, 8 ) === { arg : [ 4, 8 ] };
testNew( 1, 2, 3, 4, 5 ).addThem() === 15;
For the first if statement to work in testNew you have to return this; at the bottom of the function. So as an example with your code:
function Something() {
// init stuff
return this;
}
function createSomething() {
return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );
Update: I've changed my first example to sum any number of arguments, instead of just two.
I just came across this problem, and I solved it like this:
function instantiate(ctor) {
switch (arguments.length) {
case 1: return new ctor();
case 2: return new ctor(arguments[1]);
case 3: return new ctor(arguments[1], arguments[2]);
case 4: return new ctor(arguments[1], arguments[2], arguments[3]);
//...
default: throw new Error('instantiate: too many parameters');
}
}
function Thing(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
var thing = instantiate(Thing, 'abc', 123, {x:5});
Yeah, it's a bit ugly, but it solves the problem, and it's dead simple.
if you're interested in an eval-based solution
function createSomething() {
var q = [];
for(var i = 0; i < arguments.length; i++)
q.push("arguments[" + i + "]");
return eval("new Something(" + q.join(",") + ")");
}
This works!
var cls = Array; //eval('Array'); dynamically
var data = [2];
new cls(...data);
See also how CoffeeScript does it.
s = new Something([a,b,c]...)
becomes:
var s;
s = (function(func, args, ctor) {
ctor.prototype = func.prototype;
var child = new ctor, result = func.apply(child, args);
return Object(result) === result ? result : child;
})(Something, [a, b, c], function(){});
This constructor approach works both with and without the new keyword:
function Something(foo, bar){
if (!(this instanceof Something)){
var obj = Object.create(Something.prototype);
return Something.apply(obj, arguments);
}
this.foo = foo;
this.bar = bar;
return this;
}
It assumes support for Object.create but you could always polyfill that if you're supporting older browsers. See the support table on MDN here.
Here's a JSBin to see it in action with console output.
Solution without ES6 or polyfills:
var obj = _new(Demo).apply(["X", "Y", "Z"]);
function _new(constr)
{
function createNamedFunction(name)
{
return (new Function("return function " + name + "() { };"))();
}
var func = createNamedFunction(constr.name);
func.prototype = constr.prototype;
var self = new func();
return { apply: function(args) {
constr.apply(self, args);
return self;
} };
}
function Demo()
{
for(var index in arguments)
{
this['arg' + (parseInt(index) + 1)] = arguments[index];
}
}
Demo.prototype.tagged = true;
console.log(obj);
console.log(obj.tagged);
output
Demo {arg1: "X", arg2: "Y", arg3: "Z"}
... or "shorter" way:
var func = new Function("return function " + Demo.name + "() { };")();
func.prototype = Demo.prototype;
var obj = new func();
Demo.apply(obj, ["X", "Y", "Z"]);
edit:
I think this might be a good solution:
this.forConstructor = function(constr)
{
return { apply: function(args)
{
let name = constr.name.replace('-', '_');
let func = (new Function('args', name + '_', " return function " + name + "() { " + name + "_.apply(this, args); }"))(args, constr);
func.constructor = constr;
func.prototype = constr.prototype;
return new func(args);
}};
}
You can't call a constructor with a variable number of arguments like you want with the new operator.
What you can do is change the constructor slightly. Instead of:
function Something() {
// deal with the "arguments" array
}
var obj = new Something.apply(null, [0, 0]); // doesn't work!
Do this instead:
function Something(args) {
// shorter, but will substitute a default if args.x is 0, false, "" etc.
this.x = args.x || SOME_DEFAULT_VALUE;
// longer, but will only put in a default if args.x is not supplied
this.x = (args.x !== undefined) ? args.x : SOME_DEFAULT_VALUE;
}
var obj = new Something({x: 0, y: 0});
Or if you must use an array:
function Something(args) {
var x = args[0];
var y = args[1];
}
var obj = new Something([0, 0]);
Matthew Crumley's solutions in CoffeeScript:
construct = (constructor, args) ->
F = -> constructor.apply this, args
F.prototype = constructor.prototype
new F
or
createSomething = (->
F = (args) -> Something.apply this, args
F.prototype = Something.prototype
return -> new Something arguments
)()
function createSomething() {
var args = Array.prototype.concat.apply([null], arguments);
return new (Function.prototype.bind.apply(Something, args));
}
If your target browser doesn't support ECMAScript 5 Function.prototype.bind, the code won't work. It is not very likely though, see compatibilty table.
modified #Matthew answer. Here I can pass any number of parameters to function as usual (not array). Also 'Something' is not hardcoded into:
function createObject( constr ) {
var args = arguments;
var wrapper = function() {
return constr.apply( this, Array.prototype.slice.call(args, 1) );
}
wrapper.prototype = constr.prototype;
return new wrapper();
}
function Something() {
// init stuff
};
var obj1 = createObject( Something, 1, 2, 3 );
var same = new Something( 1, 2, 3 );
This one-liner should do it:
new (Function.prototype.bind.apply(Something, [null].concat(arguments)));
While the other approaches are workable, they're unduly complex. In Clojure you generally create a function that instantiates types/records and use that function as the mechanism for instantiation. Translating this to JavaScript:
function Person(surname, name){
this.surname = surname;
this.name = name;
}
function person(surname, name){
return new Person(surname, name);
}
By taking this approach you avoid the use of new except as described above. And this function, of course, has no issues working with apply or any number of other functional programming features.
var doe = _.partial(person, "Doe");
var john = doe("John");
var jane = doe("Jane");
By using this approach, all of your type constructors (e.g. Person) are vanilla, do-nothing constructors. You just pass in arguments and assign them to properties of the same name. The hairy details go in the constructor function (e.g. person).
It is of little bother having to create these extra constructor functions since they are a good practice anyhow. They can be convenient since they allow you to potentially have several constructor functions with different nuances.
It's also intresting to see how the issue of reusing the temporary F() constructor, was addressed by using arguments.callee, aka the creator/factory function itself:
http://www.dhtmlkitchen.com/?category=/JavaScript/&date=2008/05/11/&entry=Decorator-Factory-Aspect
Any function (even a constructor) can take a variable number of arguments. Each function has an "arguments" variable which can be cast to an array with [].slice.call(arguments).
function Something(){
this.options = [].slice.call(arguments);
this.toString = function (){
return this.options.toString();
};
}
var s = new Something(1, 2, 3, 4);
console.log( 's.options === "1,2,3,4":', (s.options == '1,2,3,4') );
var z = new Something(9, 10, 11);
console.log( 'z.options === "9,10,11":', (z.options == '9,10,11') );
The above tests produce the following output:
s.options === "1,2,3,4": true
z.options === "9,10,11": true
Here is my version of createSomething:
function createSomething() {
var obj = {};
obj = Something.apply(obj, arguments) || obj;
obj.__proto__ = Something.prototype; //Object.setPrototypeOf(obj, Something.prototype);
return o;
}
Based on that, I tried to simulate the new keyword of JavaScript:
//JavaScript 'new' keyword simulation
function new2() {
var obj = {}, args = Array.prototype.slice.call(arguments), fn = args.shift();
obj = fn.apply(obj, args) || obj;
Object.setPrototypeOf(obj, fn.prototype); //or: obj.__proto__ = fn.prototype;
return obj;
}
I tested it and it seems that it works perfectly fine for all scenarios. It also works on native constructors like Date. Here are some tests:
//test
new2(Something);
new2(Something, 1, 2);
new2(Date); //"Tue May 13 2014 01:01:09 GMT-0700" == new Date()
new2(Array); //[] == new Array()
new2(Array, 3); //[undefined × 3] == new Array(3)
new2(Object); //Object {} == new Object()
new2(Object, 2); //Number {} == new Object(2)
new2(Object, "s"); //String {0: "s", length: 1} == new Object("s")
new2(Object, true); //Boolean {} == new Object(true)
Yes we can, javascript is more of prototype inheritance in nature.
function Actor(name, age){
this.name = name;
this.age = age;
}
Actor.prototype.name = "unknown";
Actor.prototype.age = "unknown";
Actor.prototype.getName = function() {
return this.name;
};
Actor.prototype.getAge = function() {
return this.age;
};
when we create an object with "new" then our created object INHERITS getAge(), But if we used apply(...) or call(...) to call Actor, then we are passing an object for "this" but the object we pass WON'T inherit from Actor.prototype
unless, we directly pass apply or call Actor.prototype but then.... "this" would point to "Actor.prototype" and this.name would write to: Actor.prototype.name. Thus affecting all other objects created with Actor...since we overwrite the prototype rather than the instance
var rajini = new Actor('Rajinikanth', 31);
console.log(rajini);
console.log(rajini.getName());
console.log(rajini.getAge());
var kamal = new Actor('kamal', 18);
console.log(kamal);
console.log(kamal.getName());
console.log(kamal.getAge());
Let's try with apply
var vijay = Actor.apply(null, ["pandaram", 33]);
if (vijay === undefined) {
console.log("Actor(....) didn't return anything
since we didn't call it with new");
}
var ajith = {};
Actor.apply(ajith, ['ajith', 25]);
console.log(ajith); //Object {name: "ajith", age: 25}
try {
ajith.getName();
} catch (E) {
console.log("Error since we didn't inherit ajith.prototype");
}
console.log(Actor.prototype.age); //Unknown
console.log(Actor.prototype.name); //Unknown
By passing Actor.prototype to Actor.call() as the first argument, when the Actor() function is ran, it executes this.name=name, Since "this" will point to Actor.prototype, this.name=name; means Actor.prototype.name=name;
var simbhu = Actor.apply(Actor.prototype, ['simbhu', 28]);
if (simbhu === undefined) {
console.log("Still undefined since the function didn't return anything.");
}
console.log(Actor.prototype.age); //simbhu
console.log(Actor.prototype.name); //28
var copy = Actor.prototype;
var dhanush = Actor.apply(copy, ["dhanush", 11]);
console.log(dhanush);
console.log("But now we've corrupted Parent.prototype in order to inherit");
console.log(Actor.prototype.age); //11
console.log(Actor.prototype.name); //dhanush
Coming back to orginal question how to use new operator with apply, here is my take....
Function.prototype.new = function(){
var constructor = this;
function fn() {return constructor.apply(this, args)}
var args = Array.prototype.slice.call(arguments);
fn.prototype = this.prototype;
return new fn
};
var thalaivar = Actor.new.apply(Parent, ["Thalaivar", 30]);
console.log(thalaivar);
since ES6 this is possible through the Spread operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Apply_for_new
This answer was already, sort of given in comment https://stackoverflow.com/a/42027742/7049810, but seems to have been missed by most
Actually the simplest method is:
function Something (a, b) {
this.a = a;
this.b = b;
}
function createSomething(){
return Something;
}
s = new (createSomething())(1, 2);
// s == Something {a: 1, b: 2}
A revised solution from #jordancpaul's answer.
var applyCtor = function(ctor, args)
{
var instance = new ctor();
ctor.prototype.constructor.apply(instance, args);
return instance;
};
Make an anonymous prototype and apply the Something prototype to it using the arguments and then create a new instance of that anonymous prototype. The one disadavantage of this is it will not pass the s instanceof Something check, though it is identical, it is basically an instance of a clone.
function Something(){
// init stuff
}
function createSomething(){
return new (function(){Something.apply(this, arguments)});
}
var s = createSomething(a,b,c); // 's' is an instance of Something
function FooFactory() {
var prototype, F = function(){};
function Foo() {
var args = Array.prototype.slice.call(arguments),
i;
for (i = 0, this.args = {}; i < args.length; i +=1) {
this.args[i] = args[i];
}
this.bar = 'baz';
this.print();
return this;
}
prototype = Foo.prototype;
prototype.print = function () {
console.log(this.bar);
};
F.prototype = prototype;
return Foo.apply(new F(), Array.prototype.slice.call(arguments));
}
var foo = FooFactory('a', 'b', 'c', 'd', {}, function (){});
console.log('foo:',foo);
foo.print();

Adding methods to Object / Number / String prototype

Disclaimer
This thread is supposed to serve as a help for other people encountering similar problems as well as checking whether there are better solutions. I will attach my own solution, but ideas and improvements (besides making it more generic) are welcome.
I know that generally, extending the built-in objects is a bad idea. So an assumption for this thread is that there is a good reason and no way around it.
Scenario
As a developer, I want to add a method someMethod to all Javascript objects, wherein the implementation is different for Object, Number and String.
I want the solution to meet the following acceptance criteria:
A) The solution works in a browser
A1) The solution works in strict mode in case the script is used within a strict context
A2) The solution works in non-strict mode because 'use strict'; will be removed during compression in, e.g., the YUI Compressor[1]
B) The solution works in node.js
B1) The solution works in strict mode (reason see A1)
B2) The solution works in non-strict mode for the same reason as in B2, plus strict mode in node.js cannot be activated on function level[2]
C) I want other objects to be allowed to override this method
D) If possible, I want to have control over whether or not the method shows up in a for .. in loop to avoid conflicts with other libraries
E) The solution shall actually modify the prototypes.
[1] Minfication removes strict directives
[2] Any way to force strict mode in node?
My own solution
While trying to figure this out, I have encountered a few problems causing one or another acceptance criterium to break (e.g. a problem described in [1]). After some time I came up with the following solution which seems to work for me. This can be written in a more generic way, of course.
(function () {
'use strict';
var methodName = 'someMethod',
/** Sample method implementations */
__someMethod = {
'object': function () {
var _this = this.valueOf();
return ['Object'].concat( Array.prototype.slice.call( arguments ) );
},
'number': function () {
var _this = this.valueOf();
return ['Number'].concat( Array.prototype.slice.call( arguments ) );
},
'string': function () {
var _this = this.valueOf();
return ['String'].concat( Array.prototype.slice.call( arguments ) );
},
'boolean': function () {
var _this = this.valueOf();
return ['Boolean', _this];
}
};
if( Object.defineProperty ) {
Object.defineProperty( Number.prototype, methodName, {
value: __someMethod['number'],
writable: true
} );
Object.defineProperty( String.prototype, methodName, {
value: __someMethod['string'],
writable: true
} );
Object.defineProperty( Boolean.prototype, methodName, {
value: __someMethod['boolean'],
writable: true
} );
Object.defineProperty( Object.prototype, methodName, {
value: __someMethod['object'],
writable: true
} );
} else {
Number.prototype[methodName] = __someMethod['number'];
String.prototype[methodName] = __someMethod['string'];
Boolean.prototype[methodName] = __someMethod['boolean'];
Object.prototype[methodName] = __someMethod['object'];
}
})();
Edit: I updated the solution to add the solution for the problem mentioned in [1]. Namely it's the line (e.g.) var _this = this.valueOf();. The reason for this becomes clear if using
'number': function (other) {
return this === other;
}
In this case, you will get
var someNumber = 42;
console.log( someNumber.someMethod( 42 ) ); // false
This, of course, isn't what we'd want (again, the reason is stated in [1]). So you should use _this instead of this:
'number': function (other) {
var _this = this.valueOf();
return _this === other;
}
// ...
var someNumber = 42;
console.log( someNumber.someMethod( 42 ) ); // true
[1] Why does `typeof this` return "object"?
Creating a wrapper object (note this is just an example, it is not very robust):
var $ = (function(){
function $(obj){
if(!(this instanceof $))
return new $(obj);
this.method = function(method){
var objtype = typeof obj;
var methodName = method + objtype[0].toUpperCase() + objtype.substr(1);
typeof _$[methodName] == 'function' && _$[methodName].call(obj);
}
}
var _$ = {};
_$.formatNumber = function(){
console.log('Formatting number: ' + this);
}
_$.formatString = function(){
console.log('Formatting str: "' + this + '"');
}
_$.formatObject = function(){
console.log('Formatting object: ');
console.log(JSON.stringify(this));
}
return $;
})();
Usage:
var num = 5;
var str = 'test';
var obj = {num: num, str: str};
var $num = $(num);
$num.method('format');
$(str).method('format');
$(obj).method('format');
Demo

Simple inheritance implementation for JS: is this a good example?

Coming from Dojo, I really miss a lot Dojo's declare() function.
I am developing a complex-ish application, and I hacked the living hell out of Node's lang.inherits() to make it more... well, more powerful.
Here is an example to show you what it actually does:
var First = declare( null, {
one: function(p){
console.log("one in First");
console.log(p);
return 1000;
},
two: function(p){
console.log("two in First");
console.log(p);
return 1001;
},
constructor: function(a){
this.a = a;
console.log("Constructor of First called");
},
})
var Second = declare( First, {
two: function( p ){
console.log("two in Second");
console.log( p );
a = this.inherited(arguments);
console.log("Inherited function returned: " + a );
},
constructor: function(a){
console.log("Constructor of Second called, and this.a is...");
console.log( this.a );
},
})
console.log("Creating first...");
first = new First(10);
console.log("Creating second...");
second = new Second( 20 );
console.log( "first.a:")
console.log( first.a );
console.log( "second.a:")
console.log( second.a );
console.log( "first.one(1):")
first.one(1);
console.log( "first.two(2):")
first.two(2);
console.log( "second.one(3):")
second.one(3);
console.log( "second.two(4):")
second.two(4);
Will display:
Creating first...
Constructor of First called
Creating second...
Constructor of First called
Constructor of Second called, and this.a is...
20
first.a:
10
second.a:
20
first.one(1):
one in First
1
first.two(2):
two in First
2
second.one(3):
one in First
3
second.two(4):
two in Second
4
two in First
4
Inherited function returned: 1001
I know that the function lang.inherits() is minimalistic for a reason: nodejs doesn't want to impose specific ways of dealing with "classes", prototypes, and objects in Javascript.
However, a lot of code out there is full of:
function SomeClass( options ){
this.options = options;
}
SomeClass.prototype.functionOne = function(something){
//...
}
SomeClass.prototype.functionTwo = function(something){
//...
}
Which could (and... well, should?) be written as:
SomeClass = declare( null, {
constructor: function(options){
this.options = options;
},
functionOne: function(something){
// ...
},
functionTwo: function(something){
// ...
},
})
With the benefit of being able to do:
SomeOtherClass = declare( SomeClass, {
constructor: function(){
this.options['manipulate'] ++;
},
functionOne: function(something){
this.inherited(arguments); // Call the superclass method
// ...
},
})
Which will automatically call the constructor of the parent etc.
(To implement this.inherited() I actually ended up creating a hash map of the functions, as they are effectively name-less);
The major difference between this and Dojo's is that this version doesn't implement multiple inheritance and mixins. However, while multiple inheritance/mixins make sense in a client-side environment, I feel that they would be a major overkill in a server-side program.
OK... here is the code.
Can you spot anything really wrong with this code?
Did I invent something that already existed?
Here we go...
var
dummy
;
var declare = exports.declare = function(superCtor, protoMixin) {
// Kidnap the `constructor` element from protoMixin, as this
// it mustn't get copied over into the prototype
var constructor = protoMixin.constructor;
delete protoMixin.constructor;
// The function that will work as the effective constructor. This
// will be returned
var ctor = function(){
// Call the superclass constructor automatically
if( typeof( superCtor.prototype.constructor === 'function' ) ){
superCtor.prototype.constructor.apply( this, arguments );
}
// Call its own constuctor (kidnapped a second ago)
if( typeof( constructor ) === 'function' ){
constructor.apply( this, arguments );
}
};
// The superclass can be either an empty one, or the one passed
// as a parameter
superCtor = superCtor === null ? function(){} : superCtor;
// Create the new class' prototype. It's a new object, which happen to
// have its own prototype (__proto__) set as the superclass' and the
// `constructor` attribute set as ctor (the one we are about to return)
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
// Implement inherited() so that classes can run this.inherited(arguments)
// This will only work for sub-classes created using declare() as they are
// the ones with the _inheritMap in their prototype
protoMixin.inherited = function(args){
var name, fn;
// Look for the name in the _inheritMap
name = this._inheritMap[ args.callee ];
if( name ){
fn = superCtor.prototype[name];
if( fn ){
return fn.apply( this, args );
} else {
throw( new Error("Method " + name + "() not inherited!") );
}
}
}
// Copy every element in protoMixin into the prototype.
ctor.prototype._inheritMap = {}
for( var k in protoMixin ){
ctor.prototype[ k ] = protoMixin[ k ];
ctor.prototype._inheritMap[ protoMixin[ k ] ] = k;
}
return ctor;
};
exports = module.exports = declare;
I'd look at npm install declarejs, which is basically a ripped out version of Dojo's declare.
You can find a bit more info here
Personally I prefer something like Backbone's .extend(), which can easily be ripped out.
Well, the answer I suppose is "if it works, then great!".
It works... so: great!
For future reference, "declare" is on GitHub:
https://github.com/mercmobily/JsonRestStores/blob/master/declare.js
I updated the code so that this.inherited(arguments) works without the hashmap.
For now, it's part of:
https://github.com/mercmobily/JsonRestStores
Even though I might as well create a separate repository, since it is a handy function to have in its own right!
Merc.

Javascript add extra argument

Let's take a look at this code:
var mainFunction = function() {
altFunction.apply(null, arguments);
}
The arguments that are passed to mainFunction are dynamic -- they can be 4 or 10, doesn't matter. However, I have to pass them through to altFunction AND I have to add an EXTRA argument to the argument list.
I have tried this:
var mainFunction = function() {
var mainArguments = arguments;
mainArguments[mainArguments.length] = 'extra data'; // not +1 since length returns "human" count.
altFunction.apply(null, mainArguments);
}
But that does not seem to work. How can I do this?
Use Array.prototype.push
[].push.call(arguments, "new value");
There's no need to shallow clone the arguments object because it and its .length are mutable.
(function() {
console.log(arguments[arguments.length - 1]); // foo
[].push.call(arguments, "bar");
console.log(arguments[arguments.length - 1]); // bar
})("foo");
From ECMAScript 5, 10.6 Arguments Object
Call the [[DefineOwnProperty]] internal method on obj passing "length", the Property Descriptor {[[Value]]: len, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, and false as arguments.
So you can see that .length is writeable, so it will update with Array methods.
arguments is not a pure array. You need to make a normal array out of it:
var mainArguments = Array.prototype.slice.call(arguments);
mainArguments.push("extra data");
The arguments object isn't an array; it's like an array, but it's different. You can turn it into an array however:
var mainArguments = [].slice.call(arguments, 0);
Then you can push another value onto the end:
mainArguments.push("whatever");
The arguments "array" isn't an array (it's a design bug in JavaScript, according to Crockford), so you can't do that. You can turn it into an array, though:
var mainFunction = function() {
var mainArguments = Array.prototype.slice.call(arguments);
mainArguments.push('extra data');
altFunction.apply(null, mainArguments);
}
Update 2016: You must convert the arguments to an array before adding the element. In addition to the slice method mentioned in many posts:
var args = Array.prototype.slice.call(arguments);
You can also use the Array.from() method or the spread operator to convert arguments to a real Array:
var args = Array.from(arguments);
or
var args = [...arguments];
The above may not be optimized by your javascript engine, it has been suggested by the MDN the following may be optimized:
var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
var mainFunction = function() {
var args = [].slice.call( arguments ); //Convert to array
args.push( "extra data");
return altFunction.apply( this, args );
}
One liner to add additional argument(s) and return the new array:
[].slice.call(arguments).concat(['new value']));
//
// var
// altFn = function () {},
// mainFn = prefilled( altFn /* ...params */ );
//
// mainFn( /* ...params */ );
//
//
function prefilled ( fn /* ...params */ ) {
return ( function ( args1 ) {
var orfn = this;
return function () {
return orfn.apply( this, args1.concat( cslc( arguments ) ) );
};
} ).call( fn, cslc( arguments, 1 ) );
}
// helper fn
function cslc( args, i, j ) {
return Array.prototype.slice.call( args, i, j );
}
// example
var
f1 = function () { console.log( cslc( arguments ) ); },
F1 = prefilled( f1, 98, 99, 100 );
F1( 'a', 'b', 'c' );
//
// logs: [98, 99, 100, "a", "b", "c"]
//
//
In this case it could be more comfortable to use call() instead of apply():
function first(parameter1, parameter2) {
var parameter3 = "123";
secondFunction.call(
this,
parameter1,
parameter2,
parameter3);
},
var myABC = '12321';
someFunction(result, error, myCallback.bind(this, myABC));
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
For those who like me was looking for a way to add an argument that is optional and may be not the only omitted one (myFunc = function(reqiured,optional_1,optional_2,targetOptional) with the call like myFunc(justThisOne)), this can be done as follows:
// first we make sure arguments is long enough
// argumentPosition is supposed to be 1,2,3... (4 in the example above)
while(arguments.length < argumentPosition)
[].push.call(arguments,undefined);
// next we assign it
arguments[argumentPosition-1] = arguments[argumentPosition-1] || defaultValue;

Categories

Resources