JavaScript中,this的綁定規則

對於 JavaScript 新手來講,this 是很是基礎同時也難以理解的知識點。javascript

好比下面的代碼,this 指向就有三種方式。html

function foo() {
  console.log(this.a);
}
var a = 1;
var obj = { a: 2, foo: foo };

foo(); // 1
obj.foo(); // 2
foo.call({ a: 3 }); // 3

在《你不知道的 JavaScript》一書中,我總算比較清晰理解了 this。java

this 就是函數運行時所在的環境對象,它指向運行時所在的位置,而不是定義時的位置。數組

根據 this 綁定規則,分爲下面四種:安全

  1. 默認綁定
  2. 隱式綁定
  3. 顯式綁定
  4. new 綁定

1. 默認綁定

默認狀況下,this 指向全局對象。例以下面的代碼。閉包

var a = 2;
console.log(this.a); // 2

// 在函數中
function foo() {
  console.log(this.a);
}
foo(); // 2

// 在回調函數中
function doFoo() {
  return function() {
    console.log(this.a);
  };
}
doFoo()(); // 2

不過,函數在嚴格模式下,this 沒法指向全局對象,所以this會綁定到undefined。app

function foo() {
  "use strict";
  console.log(this);
}
foo(); // undefined

2. 隱式綁定

當函數做爲方法被調用,需考慮運行時上下文環境。函數

function foo() {
  console.log(this.a);
}
var obj = { a: 2, foo: foo };
obj.foo(); // 2

代碼中,foo()前面添加了obj對象的修飾,所以,obj對象就是函數的上下文。學習

this 會綁定到這個對象上。this

另外,須要注意下面三種狀況,它們會出現綁定對象丟失,從而把this綁定到全局對象或undefined上。

狀況一

這段代碼中,雖然obj.foo()bar()都指向同一函數,但執行結果並不同。

能夠理解爲,bar()是一個不帶任何修飾的函數調用,等價於直接調用foo(),所以應用了默認綁定。

function foo() {
  console.log(this.a);
}
var a = 3;
var obj = { a: 2, foo: foo };
var bar = obj.foo;

obj.foo(); // 2
bar(); // 3

狀況二

另外一種狀況,發生在回調函數中。原理和上面的狀況同樣。

修改上面的代碼,obj.foo做爲參數傳入doFoo()函數中,實際上,引用的是foo()函數自己,所以應用了默認綁定。

function foo() {
  console.log(this.a);
}
var a = 3;
var obj = { a: 2, foo: foo };
function doFoo(fn) {
  return fn();
}

doFoo(obj.foo);  // 3

狀況三

在一些流行的 JavaScript 庫,進行事件處理操做時,回調函數的 this 可能會強制綁定到 DOM 元素上。

這種狀況也值得注意。

3. 顯式綁定

JavaScript 提供了call()apply()bind()方法,能夠強制綁定函數的this對象。

function fn() {
  console.log(this.a);
}
fn.call({ a: 3 }); // 3

經過fn.call(obj),咱們將函數運行時,this強制綁定到obj上。

call applybind的區別

call 和 apply 改變了函數的 this 上下文後,便執行該函數,而 bind 則是將該函數返回。

function fn() {
    console.log(this.a);
}
fn.call({ a: 3 }); // 3
fn.bind({ a: 4 })(); // 4

callapply的區別

call 和 apply 的區別在於傳入參數不一樣。

call 方法接收若干個參數列表。

apply 第一個參數和 call 同樣,第二個參數接收一個包含多個參數的數組。

let arr = [2, 5, 9]
console.log(Math.max.call(3, 2, 5, 9))  // 9
console.log(Math.max.call(3, arr))  // NaN
console.log(Math.max.apply(3, arr))  // 9

4. new 綁定

若是使用 new 構造函數,在函數內部,this 指向新建立的對象。

function foo() {
  console.log(this);
}
new foo(); // foo {}
foo(); // Window {...}

其餘狀況

須要忽略的this

若第三方庫的某個函數使用了 this,默認綁定會把this指向全局對象,從而致使不可預測的後果。

一個安全的作法,就是使用顯式綁定,將函數的this指向空對象。

function foo() {
  console.log(this);
}
// 建立空對象
var ø = Object.create(null);
// 使用bind()進行柯里化(Curry)
var bar = foo.bind(ø);
bar(); // {}

這方法一般也用於對象初始化,以得到一個乾淨且高度可定製的對象。

箭頭函數

箭頭函數經常使用於回調函數中。

它會繼承父級做用域 this 綁定的對象,等價於ES6以前self = this的書寫方式。

const people = {
    name: "Merry",
    sayHi() {
        console.log(this.name)
    },
    wait() {
        setTimeout(() => {
            console.log(this.name)
        })
    }
}
people.sayHi() // Merry
people.wait()  // Merry

此外還有軟綁定(softBind),用的很少,瞭解一下便可。


參考連接

學習JavaScript閉包

JavaScript的this用法

JavaScript 的 this 原理

相關文章
相關標籤/搜索