我们正在寻求调校JavaScript的方式,使得我们可以做些真正的函数式编程。为了做到这一点,详细理解函数调用和函数原型是非常有必要的。
这是一个(待定)关于使用JavaScript进行函数式编程系列的第三篇文章。如果你是刚刚加入的,你可以跳回去看看之前的文章。
- 第一部分:引言
- 第二部分:如何打造“函数式”编程语言
函数原型
现在,不管你是已经读了还是忽略掉上面的链接所对应的文章,我们准备继续前进!
如果我们点开了我们喜欢的浏览器+JavaScript控制台,让我们看一下Function.prototype对象的属性:
1 2 3 4 |
; html-script: false ] Object.getOwnPropertyNames(Function.prototype) //=> ["length", "name", "arguments", "caller", // "constructor", "bind", "toString", "call", "apply"] |
这里的输出依赖于你使用的浏览器和JavaScript版本。(我用的是Chrome 33)
我们看到一些我们感兴趣的几个属性。鉴于这篇文章的目的,我会讨论下这几个:
- Function.prototype.length
- Function.prototype.call
- Function.prototype.apply
第一个是个属性,另外两个是方法。除了这三个,我还会愿意讨论下这个特殊的变量arguments,它和Function.prototype.arguments(已被弃用)稍有不同。
首先,我将定义一个“tester”函数来帮助我们弄清楚发生了什么。
1 2 3 4 5 6 7 8 9 |
; html-script: false ] var tester = function (a, b, c){ console.log({ this: this, a: a, b: b, c: c }); }; |
这个函数简单记录了输入参数的值,和“上下文变量”,即this的值。
现在,让我们尝试一些事情:
1 2 3 4 5 6 |
; html-script: false ] tester("a"); //=> {this: Window, a: "a", b: (undefined), c: (undefined)} tester("this", "is", "cool"); //=> {this: Window, a: "this", b: "is", c: "cool"} |
我们注意到如果我们不输入第2、3个参数,程序将会显示它们为undefined(未定义)。此外,我们注意到这个函数默认的“上下文”是全局对象window。
使用Function.prototype.call
一个函数的 .call 方法以这样的方式调用这个函数,它把上下文变量this设置为第一个输入参数的值,然后其他的的参数一个跟一个的也传进函数。
语法:
1 2 |
; html-script: false ] fn.call(thisArg[, arg1[, arg2[, ...]]]) |
因此,下面这两行是等效的:
1 2 3 |
; html-script: false ] tester("this", "is", "cool"); tester.call(window, "this", "is", "cool"); |
当然,我们能够随需传入任何参数:
1 2 3 |
; html-script: false ] tester.call("this?", "is", "even", "cooler"); //=> {this: "this?", a: "is", b: "even", c: "cooler"} |
这个方法主要的功能是设置你所调用函数的this变量的值。
使用Function.prototype.apply
函数的.apply方法比.call更实用一些。和.call类似,.apply的调用方式也是把上下文变量this设置为输入参数序列中的第一个参数的值。输入参数序列的第二个参数也是最后一个,以数组(或者类数组对象)的方式传入。
语法:
1 2 |
; html-script: false ] fun.apply(thisArg, [argsArray]) |
因此,下面三行全部等效:
我们正在寻求调校JavaScript的方式,使得我们可以做些真正的函数式编程。为了做到这一点,详细理解函数调用和函数原型是非常有必要的。
这是一个(待定)关于使用JavaScript进行函数式编程系列的第三篇文章。如果你是刚刚加入的,你可以跳回去看看之前的文章。
- 第一部分:引言
- 第二部分:如何打造“函数式”编程语言
函数原型
现在,不管你是已经读了还是忽略掉上面的链接所对应的文章,我们准备继续前进!
如果我们点开了我们喜欢的浏览器+JavaScript控制台,让我们看一下Function.prototype对象的属性:
1 2 3 4 |
; html-script: false ] Object.getOwnPropertyNames(Function.prototype) //=> ["length", "name", "arguments", "caller", // "constructor", "bind", "toString", "call", "apply"] |
这里的输出依赖于你使用的浏览器和JavaScript版本。(我用的是Chrome 33)
我们看到一些我们感兴趣的几个属性。鉴于这篇文章的目的,我会讨论下这几个:
- Function.prototype.length
- Function.prototype.call
- Function.prototype.apply
第一个是个属性,另外两个是方法。除了这三个,我还会愿意讨论下这个特殊的变量arguments,它和Function.prototype.arguments(已被弃用)稍有不同。
首先,我将定义一个“tester”函数来帮助我们弄清楚发生了什么。
1 2 3 4 5 6 7 8 9 |
; html-script: false ] var tester = function (a, b, c){ console.log({ this: this, a: a, b: b, c: c }); }; |
这个函数简单记录了输入参数的值,和“上下文变量”,即this的值。
现在,让我们尝试一些事情:
1 2 3 4 5 6 |
; html-script: false ] tester("a"); //=> {this: Window, a: "a", b: (undefined), c: (undefined)} tester("this", "is", "cool"); //=> {this: Window, a: "this", b: "is", c: "cool"} |
我们注意到如果我们不输入第2、3个参数,程序将会显示它们为undefined(未定义)。此外,我们注意到这个函数默认的“上下文”是全局对象window。
使用Function.prototype.call
一个函数的 .call 方法以这样的方式调用这个函数,它把上下文变量this设置为第一个输入参数的值,然后其他的的参数一个跟一个的也传进函数。
语法:
1 2 |
; html-script: false ] fn.call(thisArg[, arg1[, arg2[, ...]]]) |
因此,下面这两行是等效的:
1 2 3 |
; html-script: false ] tester("this", "is", "cool"); tester.call(window, "this", "is", "cool"); |
当然,我们能够随需传入任何参数:
1 2 3 |
; html-script: false ] tester.call("this?", "is", "even", "cooler"); //=> {this: "this?", a: "is", b: "even", c: "cooler"} |
这个方法主要的功能是设置你所调用函数的this变量的值。
使用Function.prototype.apply
函数的.apply方法比.call更实用一些。和.call类似,.apply的调用方式也是把上下文变量this设置为输入参数序列中的第一个参数的值。输入参数序列的第二个参数也是最后一个,以数组(或者类数组对象)的方式传入。
语法:
1 2 |
; html-script: false ] fun.apply(thisArg, [argsArray]) |
因此,下面三行全部等效: