this有四種綁定的方式

主要是以callsite發生的時機來決定

callsite就是()被執行的位置

1. Default binding rule

如果是在 strict mode , this 指向的是 undefined

若不是 strict mode , this 指向的事 global object

2. Implicit biding rule

在javascript中所有物件都是reference from object 或者 function

如下面的例子

foo,o2.foo,o3.foo都是reference from 第一行定義的 function foo

o2.foo(),o3.foo()的callsite 透過 .(dot) reference o2 跟 o3 這兩個object

所以this keyword 就是指向 o2,o3

1
2
3
4
5
6
7
8
9
10
11
function foo() {
console.log(this.bar)
}
var bar = "bar1";
var o2 = { bar: "bar2", foo: foo };
var o3 = { bar: "bar3", foo: foo };
foo(); //"bar1"
o2.foo(); // "bar2"
o3.foo(); // "bar3"

從下面的例子可以更清楚地看到

不論原本的 function 是在哪邊被定義的

在執行的時候都是reference 而不是擁有那個function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var o1 = {
bar: "bar1",
foo: function(){
console.log(this.bar)
}
};
var o2 = { bar: "bar2", foo: o1.foo };
var bar = "bar3";
var foo = o1.foo;
o1.foo(); // "bar1"
o2.foo(); // "bar2"
foo(); // "bar3"

binding confusion

1
2
3
4
5
6
7
8
9
10
11
function foo() {
var bar = "bar1";
this.baz = baz;
this.baz();
}
function baz() {
console.log(this.bar);
}
var bar = "bar2";
foo();

第十一行的 foo 的 callsite 是在 global scope底下

所以 reference function 中的 this 指向的是 global object

也就是第三行的 this.baz = baz; 等同於 global.baz = baz;

但在 global scope底下 已經有了第六行定義的 baz function

感覺上就是做了一個 a = a 的沒意義的操作

第四行的 this.baz() 的callstie 執行時 因為前面有 this object

所以 implicit binding rull apply

reference 的 baz function 中的 this 指向的是 this object 也就是 global object

因此執行的結果就會是 console.log(global.bar) 在瀏覽器中就是 console.log(window.bar)

3. Explicit binding rule

Explicit binding 就是當我們使用 .call 或者 .apply 去做function 的執行的時候

就明確指定了傳入的obj 為this

1
2
3
4
5
6
7
8
9
function foo() {
console.log(this.bar);
}
var bar = "bar1";
var obj = { bar: "bar2" };
foo(); // "bar1"
foo.call(obj); // "bar2"

javascript 也提供了一個bind的 function

讓 Explicit binding變成hard binding

在hard binding之後的function中

this的值就已經無法被改變

結果就像下面這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo() {
console.log(this.bar);
}
foo2 = foo.bind(obj);
var bar = "bar1";
var obj = { bar: "bar2" };
foo(); // "bar1"
foo.call(obj); // "bar2"
foo2(); // "bar2"
foo2("I want another string QQ"); // "bar2"

4. New keyword

在任何的function 之前加上new

會讓function call 變成 construction call

過程是這樣的

  1. 產生一個全新的object
  2. 這個全新的object和先前的object完全無關
  3. object的this指向自己
  4. 最後會自動return this
1
2
3
4
5
6
7
function foo() {
this.baz = "baz";
console.log(this.bar + " " + baz);
}
var bar = "bar";
var baz = new foo(); // undefined undefined

4種binding方式的優先順序

  1. Was the function called with new?
  2. Was the function called with call or apply specifying an explicit this?
  3. Was the function valled via a containing/owning object (context)?
  4. DEFAULT: global object(except strict mode)

Ref:

frontendmasters advanced-javascript

You-Dont-Know-JS