JavaScript 函数 Currying 化
JavaScript 和我了解的其他编程语言,例如 C/C++、C#、Java,有一个很独特的语法特性,函数也是一种对象。既然作为对象,那么也有属性、方法。今天恰好有一个问题引入,对于 arguments
、apply()
、call()
以及 Currying 有了初步的了解,简单做个记录。
arguments
对象
这是一种类似于 Array
的对象,可以在每个函数内部调用。例如:
function foo(a, b, c) {
console.log(arguments[0]);
console.log(arguments.length);
}
foo(1, 2, 3); // expected output 1
但 arguments
对象并不是 Array
,没有继承 Array
的方法,比如 slice()
、pop()
。但是可以使用 call
的方式使用 Array
的函数,例如以下几种方式:
var args1 = Array.prototype.slice.call(arguments);
// 使用子面值,书写更短但是会创建空的数组对象
var args2 = [].slice.call(arguments);
// ES2015
var args3 = Array.from(arguments);
// spread syntax
var args4 = [...arguments];
好了,挖了一个新坑 Array.from()
以及 spread syntax operator,以后再整理。
除了 length
这个属性外,还有其他几个属性,这里暂不展开讨论:
arguments.callee
当前arguments
对象所属的函数arguments.caller
调用当前函数的函数,这个属性已经不一定可用了arguments[@@iterator]
ES2015 (6th Edition, ECMA-262) 中所引入的迭代器访问方式。
apply()
和 call()
其实上一段已经出现过了,这里做个解释。
function Foo() {
this.x = 42;
}
Foo.prototype.getX = function(callback) {
if (typeof callback === "function")
callback(this.x);
};
var cb = function(arg) {
console.log(arg);
}
var a = new Foo();
a.getX(cb); // 42
Foo.prototype.getX.apply(a, [cb]); // 42
Foo.prototype.getX.call(a, cb); // 42
例子可能略显啰嗦,仅仅是语法示例。apply()
第一个参数用于绑定函数调用时使用到的 this
指针,第二个则是参数数组。call()
相当于语法糖(syntax suger),和 apply()
几乎相同,区别在于它的语法是 call(thisArg, arg1, arg2, ... )
。
Currying 化示例
这也是个很大的话题,简单说是把多个参数的函数变成单一参数的函数。在其他编程语言中可以很方便的创建多维数组,而在 JavaScript 中可以这样实现:
// http://stackoverflow.com/a/966938/975097
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while (i--) arr[length-1 - i] = createArray.apply(this, args);
}
return arr;
}
createArray(); // [] or new Array()
createArray(2); // new Array(2)
createArray(3, 2); // [new Array(2),
// new Array(2),
// new Array(2)]
这里就借用到了前两节所提到的语法特性,用一种灵活的方式弥补了语法中缺少的部分。当然为啥要创建多维数组呢?Dynamic programming 呀!