20160119

you dont know js ( this )

this 在函数调用时绑定值,值取决于函数的调用方式
我们需要检查和确定这 4 种调用方式,我们首先独立解释这 4 种规则,然后我们将说明它们的优先顺序,当多个规则可以适用于这一次调用时。

1. Default Binding(缺省绑定)

第一条规则我们将检查最常见的函数调用:独立的函数调用,当没有其他规则适用时,这个是默认规则。

1.function foo() {
2. console.log( this.a ); // 这里 this 为 Global Object
3.}
4.var a = 2; // a 是全局变量,也相当于 global-object 的属性,像是硬币的两面
5.foo(); // 2

这里 this.a 指向了全局变量 a ,因为这里适用于函数变量的默认绑定。
因为 foo() 以一种简单的方式被调用,没有其他函数的参考,所以没有其他规则来应用。

If strict mode is in effect, the global object is not eligible for the default binding, so the this is instead set to undefined.
在严格模式下,全局对象不是默认的绑定方式,所以 this 的值为 undefined

1.function foo(){
2. "use strict";
3. console.log(this.a); // this 值为 undefined
4.}
5.var a = 2;
6.foo(); // TypeError: `this` is `undefined`

一个微妙而重要的细节是,虽然整体绑定完全基于调用,但是如果 foo() 的内容没有在全局模式下运行,还是可以用 global object,在调用 foo() 时使用严格模式是无关紧要的。

1.function foo(){
2. console(this.a);
3.}
4.var a = 2;
5.(function(){
6. "use strict";
7. foo(); // 2
8.})

所以在严格模式下,只要不在定义 this 时,使用全局模式,this 的值就是 global object。
调用有 this 的函数时使用全局模式没有关系

故意混合严格模式和非严格模式是不被允许的,你的整个程序需要时严格模式或者非严格模式。但是,有时包含一个第三库使用了不同于你的代码的严格模式或非严格模式,你需要注意这些微妙的细节兼容性。

2. Implicit Binding(隐式绑定)

另一个需要考虑的是:调用上下文对象,也称为拥有或包含对象,虽然这些代替点可能稍有误导。

1.function foo(){
2. console.log(this.a);
3.}
4.var obj={
5. a: 2,
6. foo: foo
7.}
8.obj.foo(); // 2

注意 foo() 首先被声明,然后作为 obj 的一个属性被引用,不去管 foo 是不是在 obj 中被声明,还是他稍后被引用在 obj 上,无论 obj 对象是 “拥有” 或者 “包含” 这个函数。

与函数在哪里声明无关

无论你使用哪种模式,在 foo() 函数被调用,是一个对象 obj 的引用,当这个上下文对象调用函数引用,隐式绑定的规则说明调用的对象将会被用于 this。
因为 foo() 被调用时,obj 是 this,所以 this.a 和 obj.a 意义相同。
只有调用链的最后一个对象调用。

1. function foo(){
2. console.log(this.a);
3. }
4. var obj2 = {
5. a: 42,
6. foo: foo
7. };
8. var obj1 = {
9. a: 2,
10. obj2: obj2
11. };
12. // 关联的是最近的 a
13. obj1.obj2.foo(); // 42
隐式绑定失去

这个绑定最常见的失败之一是一个隐式绑定函数失去了绑定,这通常意味着它返回到默认绑定,this 值为 global object 或 undefined, 这取决于是否为严格模式。

1.function foo(){
2. console.log(this.a);
3.}
4.var obj = {
5. a: 2,
6. foo: foo
7.};
8.var bar = obj.foo; // 只是 foo 函数的又一次引用
9.var a="oops global"
10.bar(); // // "oops, global"

尽管 bar 似乎引用了 obj.foo,事实上,它是 foo 函数自己的一个引用,此外,调用 bar 的方式是重要的,这里是一个简单,无装饰的调用所以应用默认的绑定方式。

更常见,更微妙,更难以置信的方式,当我们考虑一个回调函数。

1.function foo(){
2. condole.log(this.a);
3.}
4.function doFoo(fn){ // `fn` is just another reference to `foo`
5. fn(); // <-- call-site!
6.}
7.var obj={
8. a: 2,
9. foo: foo
10.}
11.var a="oops global";
12.doFoo(obj.foo); // "oops, global"

参数通过隐式的方式传递,因为我们传递一个函数,通过隐式的方式,所以这个片段的结果和前面的例子相同。

如果这个回调函数并不是你自己的,但是嵌入了你的语言之中,没有区别,相同的结果。

1.function foo(){
2. console.log(this.a);
3.}
4.var obj={
5. a: 2,
6. foo: foo
7.};
8.var a = "oops global";
9.setTimeout(obj.foo,100); // "oops, global"

思考一下这个天然的理论,javascript 提供了一个内置对象。

1.function setTimeout(fn,delay) {
2. // wait (somehow) for `delay` milliseconds
3. fn(); // <-- call-site!
4.}

Either way the this is changed unexpectedly, you are not really in control of how your callback function reference will be executed, so you have no way (yet) of controlling the call-site to give your intended binding. We’ll see shortly a way of “fixing” that problem by fixing the this.
这个变化不是我们所期望的,并没有控制回调函数执行时的引用,我们很快可以见到一种修复方式,用来修复这个问题。