Details
Write a function defaultArguments
. It takes a function as an argument, along with an object containing default values for that function’s arguments, and returns another function which defaults to the right values.
You cannot assume that the function’s arguments have any particular names.
You should be able to call defaultArguments
repeatedly to change the defaults.
function add(a,b) { return a+b;};
var add_ = defaultArguments(add,{b:9});
add_(10); // returns 19
add_(10,7); // returns 17
add_(); // returns NaN
add_ = defaultArguments(add_,{b:3, a:2});
add_(10); // returns 13 now
add_(); // returns 5
add_ = defaultArguments(add_,{c:3}); // doesn't do anything, since c isn't an argument
add_(10); // returns NaN
add_(10,10); // returns 20
HINT: This problem requires using Fuction.prototype.toString()
in order to extract a function’s argument list
My Solution
function defaultArguments(func, params) {
// get the properties list of params
let defArg = Object.keys(params);
// use Function.prototype.toString() to get the arguments list
let args = func.args || func.toString().replace(/\/\/.*$|\/\*.*?\*\/|\s/gm, '').match(/(?:[\w]+(?:,[\w]+)*)?(?=\))/m)[0].split(',');
// pass the properties into func
let newArgs = {};
for (var i = 0; i < defArg.length; i++) {
if (args.indexOf(defArg[i]) > -1) {
let idx = args.indexOf(defArg[i]);
newArgs[idx] = params[defArg[i]];
}
}
// bind the func and params into a new function
let detour = function() {
let argsArray = [].slice.call(arguments);
let x = Object.keys(newArgs);
for (var i = 0; i < x.length; i++) {
if(x[i] >= argsArray.length) {
argsArray[x[i]] = newArgs[x[i]];
}
}
return func.apply(this,argsArray);
}
detour.args = args;
return detour;
}
args
那一大串正则表达式,获得的是原函数的参数列表,是一个 array。defArg
是函数 defaultArguments()
预先给定的参数。将两者合并成新的参数 newArgs
,类型为 object。
举例说明,如果 params
传入的值是 { b: 3, a: 2 }
,那么经过中间 10 行到 16 行的循环运算得到的 newArgs
值是 { '0': 2, '1': 3 }
。
20 行 argsArray = [].slice.call(arguments);
作用是打印参数列表。举例说明:
>function add(a,b) { console.log([].slice.call(arguments));return a+b;};
undefined
>add(1,2)
[ 1, 2 ]
3
>add(1,2,3)
[ 1, 2, 3 ]
3
>add(1,2,3,4)
[ 1, 2, 3, 4 ]
3
因为整个函数最后 return 的是 detour。也就是说当第一次运行 defaultArguments()
时,仅仅是声明了 detour()
函数但是并没有运行。按照 details 里给出的例子,只有当 add_(10)
调用时, detour()
函数才真正被运行。此时的 arguments
值为 [10]
。
27 行 apply()
方法的应用,就是调取 func
函数,并给他特定的 arguments 值,并获得结果。第一个参数是修改 func
函数的 this
指向的, 是 this
还是 null
对结果都是没有影响的。因为都是对函数的直接调用,不存在对某个 object 的 method 进行调用。
注意第 29 行的,函数也是 object,而且这个定义使得返回的函数对象多携带了 args 信息。这一点当时自己没有想到,也是看了答案后才明白的。
Other Solutions
mrkishi, guyingll, iphp, willin, marek_mistrzuk
function defaultArguments(func, params) {
var names = func.names || func.toString()
.replace(/\/\/.*$|\/\*.*?\*\/|\s/gm, '')
.match(/(?:[\w]+(?:,[\w]+)*)?(?=\))/m)[0].split(',');
var detour = function () {
var input = arguments;
return func.apply(this, names.map(function (val, i) {
return i < input.length ? input[i] : params[names[i]];
}));
};
detour.names = names;
return detour;
}