# 函数调用 Function Invoke ↵
00function sayThis(){ 01 console.log(this); 02}
我们先创建一个函数
sayThis
以期打印 函数调用
的时候 this
的指向00sayThis(); 01// => 02// window or global
这时候 this 指向 全局对象(在浏览器上是 window, node上是 global),这点没有异议。 当函数被调用的时候, this 的取值将会计算出来,当用
( )
调用函数的时候,this 指向这个函数所属的对象,换句话说就是:
this 指的是,调用函数的那个对象
---- 阮一峰 - Javascript的this用法
因此如下代码能很好的理解:
00var sayA = function(){ 01 console.log(this.A); 02} 03 04A = 'i am A in the global'; 05var obj = { 06 A: 'i am A in the obj', 07 __sayThisA: sayA, 08 __question: function(a_function){ 09 console.log('in __question'); 10 console.log(this.A); 11 a_function(); 12 } 13} 14 15sayA === obj.__sayThisA; 16// => true 17 18sayA(); 19// => 20// i am A in the global 21 22obj.__sayThisA(); 23// => 24// i am A in the obj 25 26obj.__question(obj.__sayThisA); 27// => 28// in __question 29// i am A in the obj 30// i am A in the global 31// 在 __question 里调用 作为参数传递的 a_function 其 this 指向 global, 是 纯粹的函数调用
以上就是简单的函数调用
- 普通的执行
sayA()
指向 global - 作为对象的方法执行
obj.__sayThisA()
指向 obj - 还有一种是 new 的时候
var o = new O();
这时候就指向 o
# Function.prototype ↵
每个函数都从
Function.prototype
上继承了属性方法 比如 call, apply 和 bind, 每个函数都可以使用这三种方法,他们用来指定 如何执行函数
(钦定 this 的指向和参数表)为了说明方便,定义一个新的函数
sayA_V2
00var sayA_V2 = function(i, j, k){ 01 console.log(this.A); 02 console.log(i + j + k); 03}
# Function.prototype.apply ↵
00sayA_V2(); 01// => 02// i am A in the global 03// NaN ( undefined + undefined + undefined ) 04 05sayA_V2.apply(obj); 06// => 07// i am A in the obj 08// NaN ( undefined + undefined + undefined ) 09 10sayA_V2.apply(obj, [1, 2, 3]); 11// => 12// i am A in the obj 13// 6 (1 + 2 + 3)
某函数
func
执行 .apply
的时候 会把 call 的第一个参数作为 this
的指向,第二个参数是数组,按顺序传递给函数执行。需要注意,如果这段代码运行在非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。
# Function.prototype.call ↵
00// 保持前文的代码 继续写: 01sayA_V2(); 02// => 03// i am A in the global 04// NaN ( undefined + undefined + undefined ) 05 06sayA_V2.call(obj); 07// => 08// i am A in the obj 09// NaN ( undefined + undefined + undefined ) 10 11sayA_V2.call(obj, 1, 2, 3); 12// => 13// i am A in the obj 14// 6 (1 + 2 + 3)
注意:该函数的语法与 apply() 方法的语法几乎完全相同,唯一的区别在于,call()方法接受的是一个参数列表,而 apply()方法接受的是一个包含多个参数的数组(或类数组对象)。
---- MDN - Function.prototype.call()
注意 apply 方法接受的是一个包含多个参数的数组(或类数组对象)
apply也可以接受类数组对象
apply也可以接受类数组对象
# Function.prototype.bind ↵
- bind(newThis, a, b, c, …) 方法会返回一个新函数
- 这个新函数的 this 由 newThis 给出
- 参数表由后面的 a, b, c, … 给出
- 它很像
.call
, 不同在于他返回一个新函数 该函数不论如何执行,其 this 都被锁死 - 可以利用 bind 预置函数需要的参数
00// 依据之前的代码 继续写: 01var bind001 = sayA_V2.bind(obj); 02bind001(); 03// => 04// i am A in the obj 05// NaN ( undefined + undefined + undefined ) 06 07bind001(1,2,3); 08// => 09// i am A in the obj 10// 6 ( 1 + 2 + 3 ) 11 12bind001.call(window, 1, 2, 3); 13// => 14// i am A in the obj ** this is NOT window ** 15// 6 ( 1 + 2 + 3 ) 16 17var bindPre = sayA_V2.bind(obj, 1, 2); 18bindPre(3); 19// => 20// i am A in the obj 21// 6 ( 1 + 2 + 3 ) 22// 与 sayA_V2.call(obj, 1, 2, 3) 结果一致 23 24bindPre(4); 25// => 26// i am A in the obj 27// 7 ( 1 + 2 + 4 ) 28// 与 sayA_V2.call(obj, 1, 2, 4) 结果一致 29 30// 三个参数 a, b, c 中 a, b 被钦点为 1, 2 了
# 玩法 ↵
虽然知道 call apply bind 三个函数的用法和意义…
但是基本没用过,也不知道 有什么实际的地方
但是基本没用过,也不知道 有什么实际的地方
最近在鼓捣
函数式编程
,因此用到了不少 call apply 和 bind# 高阶函数的 this 指向 ↵
00var obj = { 01 copyDist: '/path/for/copy/destination', 02 readFinish: function(err, data){ 03 if (err) { throw err } 04 else { 05 this.copyTo(this.copyDist, data); 06 } 07 }, 08 copyTo: function(path, data){ 09 // .... 10 }, 11 copy: function(filePath){ 12 fs.readFile(filePath, this.readFinish.bind(this)); 13 } 14} 15obj.copy('File.dat');
高阶函数指的是参数或者返回值为函数的函数.
上面的代码 copy 中的 this.readFinish 执行的时候 this 应该指向 obj , 因此用了 bind 才会让 readFinish 能正常工作
# 偏函数 ↵
00function logger(logType){ 01 return console.log.bind(console, logType); 02} 03 04var info = logger('[INFO]'); 05var error = logger('[ERROR]'); 06 07info('this is an info'); 08// => 09// [INFO] this is an info 10 11error('this is an error'); 12// => 13// [ERROR] this is an error
bind 方法可以用来用来制造
偏函数
,用于预置参数。有时候我们需要打印东西,还希望给打印什么东西加点前缀,比如这是条信息,这是条错误,因此就有上面的做法。
# 变长参数的处理 ↵
00function sumFor(){ 01 let argu = Array.prototype.slice.apply(arguments); 02 return argu.reduce(function(prev, curr, idx, its){ 03 return prev+curr; 04 }, 0); 05} 06 07sumFor(1); 08// => 09// 1 10 11sumFor(1,2,3); 12// => 13// 6 14 15// 不用 bind 实现 logger 16function loggerWithOutBind(namespace){ 17 // namespace 闭包 18 return function(){ 19 var argu = Array.prototype.slice.call(arguments); 20 argu.unshift(namespace); 21 console.log(argu.join(' ')); 22 } 23}
有时候不得不处理变长参数… 这时候需要调用
.apply
或者 .call
将 类数组对象arguments
格式化为数组# 函数监视 Function Spy ↵
00function Spy(target, method){ 01 let pre = target[method]; 02 var spy = { 03 count: 0 04 }; 05 06 target[method] = function(){ 07 let argu = Array.prototype.slice.call(arguments); 08 spy.count++; 09 10 return pre.apply(target, argu); 11 } 12 13 return spy; // 闭包 14} 15 16var spy4consoleLog = Spy(console, 'log'); 17 18spy4consoleLog.count; 19// => 20// 0 21 22console.log('WoW~ this is 1'); 23spy4consoleLog.count; 24// => 25// 1 26 27console.log('WoW~ this is 2'); 28spy4consoleLog.count; 29// => 30// 2
这很厉害,用于调试,可以监视很多东西
# 函数柯里化 ↵
00function curry3(fun){ 01 return function(one){ 02 return function(two){ 03 return function (three){ 04 return fun(one, two, three) 05 } 06 } 07 } 08} 09 10function add3(a, b, c){ 11 return a + b + c; 12} 13 14var add_0 = curry3(add3); 15var add_1 = add_0(1); 16var add_2 = add_1(2); 17 18add_2(3); 19// => 20// 6 21// just like add3(1, 2, 3) 22 23add_2(10); 24// => 25// 13 26// just like add3(1, 2, 10) 27 28var add_another = add_1(4); 29add_another(5); 30// => 31// 10 32// just like add3(1, 4, 5) 33 34curry3(add3)('This is ')('Function ')('Currying'); 35// => 36// This is Function Currying
函数柯里化就好像是慢慢填充函数的参数一样 最后填满的时候就可以得出答案,
没有状态
,只有 第几阶段的函数
第 n 阶可以生成无数个第 n+1 阶函数,而且相互没有关系
就像代码里的 add_0, 反复执行
就像代码里的 add_0, 反复执行
add_0(0)
返回的结果都是与众不同的# Links ↵
- 大量参阅了MDN, 尤其是 Function: MDN -Function
- 本文许多代码是受 nodeschool 上的教程启发的 NODESCHOOL - 教你 Web 开发技能的开源课程,自学或者参加一个附近的教学活动
- 受此函数式编程教程影响 Functional JavaScript