Ryan Shang

生死看淡,不服就干

0%

call和bind

call、apply和bind常用来显示改变函数运行环境中的this指向,这几天突发奇想如果call.call、bind.bind和bind().band()会是怎样的结果,先记录一下,给出一下我现在认为的结论,后面有时间了,深入研究下。

公共

1
2
3
4
5
6
7
8
function fn01 () { console.log('fn01:', this); }
function fn02 () { console.log('fn02:', this); }
let obj = { fn: fn01 }
let fn03 = obj.fn;
fn01(); // fn01 window
fn02(); // fn02 window
fn03(); // fn01 window
obj.fn(); // fn01 obj

这其中定义了两个函数fn01和fn02以及一个对象,每个方法右面注释是执行主体和this指向。上面的几种是比较常见的情况。

call

1
2
3
4
5
6
7
fn01.call(fn02); // fn01 fn02
fn01.call.call(fn02); // fn02 window
fn01.call.call(fn02, fn02); // fn02 fn02
fn01.call.call.call(fn02); // fn02 window
fn01.call.call.call.call(fn02); // fn02 window
fn01.call.call.call.call.call(fn02); // fn02 window
fn01.call.call.call.call.call(fn02, fn02, fn02, fn02, fn02); // fn02 fn02

从这开始是主要内容,第一句,是比较常见的方式,执行fn01,this指向改为fn02。

接着就是我的疑惑的地方:call.call

从结果上看,call.call中间插入几个call并不影响最终结果。

注意看下这两句:

1
2
fn01.call.call(fn02); // fn02 window
fn01.call.call(fn02, fn02); // fn02 fn02

按我现在的看法,fn01.call.call(fn02)可能就相当于fn02.call()

这行代码的运行过程可能是这样:fn01.call执行了call方法,改变了fn01.call的this指向为fn02。但是fn01.call执行的this指向是fn01,所以最终执行主体就变成了fn02。这时候就相当于fn02.call(),因为call没有参数,所以结果this指向了window。

这样看,fn01.call.call(fn02, fn02)相当于fn02.call(fn02)。所以最终结果,fn02是执行主体,this也指向了fn02。

后面加多少个call,原里应该都是一样的,所以不再赘述。

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// bind
let fn04 = fn01.bind();
fn04(); // fn01 window
let fn05 = fn01.bind(fn02);
fn05(); // fn01 fn02
let fn06 = fn01.bind(fn01).bind(fn01);
fn06(); // fn01 fn01
let fn07 = fn01.bind(fn01).bind(fn02);
fn07(); // fn01 fn01
let fn08 = fn01.bind(fn02).bind(fn01);
fn08(); // fn01 fn02
let fn09 = fn01.bind(fn02).bind(fn02);
fn09(); // fn01 fn02
let fn10 = fn01.bind(fn02).bind(fn01).bind(fn01);
fn10(); // fn01 fn02
let fn11 = fn01.bind(fn01).bind(fn02).bind(fn02);
fn11(); // fn01 fn01
let fn12 = fn01.bind(obj).bind(fn01);
fn12(); // fn01 obj
let fn13 = fn01.bind.bind(fn02);
fn13(); // 无输出
let fn14 = fn13();
fn14(); // fn02 window
let fn15 = fn13(fn01);
fn15(); // fbn02 fn01

bind有两种情况一种是bind.bind,一种是bind().bind()。

首先来看bind().bind(),无论链式多少个,实际上都以第一个bind()中的参数为准,原因可能是bind函数返回的函数中的this已经被外层的bind函数(第一个bind函数)确定,后面再进行bind操作传进来的参数没有做处理。但此原因暂时没有验证,仅作猜想。

接着是bind.bind,bind.bind可能和call.call类似,fn01.bind执行了bind方法,改变了fn01.bind的this指向为fn02。但是fn01.bind执行的this指向是fn01,所以最终执行主体就变成了fn02。这时候就相当于fn02.bind(),因为bind没有参数,所以结果this指向了window。执行一次fn02.bind()(也就是fn13),返回的函数执行后,就和call.call的结论类似,只是最后一次传参的方式不太一样。

以上所有结论仅是猜想,还需要从更加深入的研究this指向还有call、bind的机制,设计出合理的验证方式。因时间原因,先放一放。